@agent-native/core 0.130.2 → 0.131.3
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 +79 -0
- package/corpus/core/docs/content/agent-surfaces.mdx +4 -4
- package/corpus/core/docs/content/automations.mdx +61 -12
- package/corpus/core/docs/content/deployment.mdx +11 -4
- package/corpus/core/docs/content/extensions.mdx +39 -33
- package/corpus/core/docs/content/recurring-jobs.mdx +41 -19
- package/corpus/core/docs/content/template-dispatch-operations.mdx +19 -7
- package/corpus/core/package.json +3 -1
- package/corpus/core/src/a2a/types.ts +6 -0
- package/corpus/core/src/action.ts +68 -0
- package/corpus/core/src/agent/engine/builder-engine.ts +8 -4
- package/corpus/core/src/agent/production-agent.ts +175 -100
- package/corpus/core/src/agent/tool-search.ts +36 -0
- package/corpus/core/src/automations/service.ts +506 -0
- package/corpus/core/src/browser-context/index.ts +392 -0
- package/corpus/core/src/client/AgentPanel.tsx +2 -1
- package/corpus/core/src/client/FeedbackButton.tsx +37 -4
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +21 -0
- package/corpus/core/src/client/agent-chat.ts +31 -3
- package/corpus/core/src/client/agent-page/AgentJobsTab.tsx +438 -310
- package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +4 -4
- package/corpus/core/src/client/agent-page/use-jobs.ts +7 -0
- package/corpus/core/src/client/analytics-session.ts +13 -0
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +67 -0
- package/corpus/core/src/client/settings/AutomationsSection.tsx +27 -499
- package/corpus/core/src/client/settings/SettingsPanel.tsx +32 -11
- package/corpus/core/src/client/settings/index.ts +2 -0
- package/corpus/core/src/client/use-agent-chat.ts +35 -3
- package/corpus/core/src/client/use-chat-threads.ts +8 -25
- package/corpus/core/src/deploy/build.ts +2 -0
- package/corpus/core/src/deploy/workspace-deploy.ts +2 -0
- package/corpus/core/src/extensions/actions.ts +115 -3
- package/corpus/core/src/extensions/fetch-tool.ts +11 -0
- package/corpus/core/src/extensions/routes.ts +16 -1
- package/corpus/core/src/extensions/web-content.ts +96 -1
- package/corpus/core/src/integrations/computer-supervision.ts +33 -1
- package/corpus/core/src/integrations/index.ts +6 -0
- package/corpus/core/src/integrations/integration-durable-dispatch-config.ts +10 -0
- package/corpus/core/src/integrations/integration-durable-dispatch.ts +4 -0
- package/corpus/core/src/integrations/plugin.ts +94 -41
- package/corpus/core/src/integrations/remote-browser-actions.ts +621 -0
- package/corpus/core/src/jobs/actions/list-recurring-jobs.ts +5 -7
- package/corpus/core/src/jobs/actions/manage-recurring-job.ts +3 -7
- package/corpus/core/src/jobs/background-automation-runner.ts +437 -0
- package/corpus/core/src/jobs/frontmatter.ts +308 -0
- package/corpus/core/src/jobs/scheduler.ts +89 -598
- package/corpus/core/src/jobs/tools.ts +22 -3
- package/corpus/core/src/localization/default-messages.ts +45 -45
- package/corpus/core/src/mcp-client/index.ts +6 -0
- package/corpus/core/src/notifications/actions.ts +5 -0
- package/corpus/core/src/progress/actions.ts +5 -0
- package/corpus/core/src/provider-api/actions/github-repo-files.ts +11 -0
- package/corpus/core/src/provider-api/actions/provider-api.ts +13 -0
- package/corpus/core/src/server/action-discovery.ts +7 -0
- package/corpus/core/src/server/agent-capabilities.ts +43 -8
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +63 -1
- package/corpus/core/src/server/agent-chat/browser-team-tools.ts +126 -3
- package/corpus/core/src/server/agent-chat/framework-prompts.ts +7 -11
- package/corpus/core/src/server/agent-chat/plugin-options.ts +3 -3
- package/corpus/core/src/server/agent-chat/script-entries.ts +52 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +25 -5
- package/corpus/core/src/server/attribution.ts +22 -0
- package/corpus/core/src/server/auth.ts +20 -6
- package/corpus/core/src/server/better-auth-instance.ts +10 -2
- package/corpus/core/src/server/core-routes-plugin.ts +10 -1
- package/corpus/core/src/server/csrf.ts +25 -0
- package/corpus/core/src/server/google-oauth.ts +21 -1
- package/corpus/core/src/server/onboarding-html.ts +18 -0
- package/corpus/core/src/server/prompts/framework-core-compact.ts +3 -3
- package/corpus/core/src/server/prompts/framework-core.ts +3 -3
- package/corpus/core/src/server/prompts/shared-rules.ts +3 -3
- package/corpus/core/src/settings/index.ts +1 -0
- package/corpus/core/src/shared/analytics-anonymous-id.ts +35 -0
- package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +26 -0
- package/corpus/core/src/templates/default/app/i18n/de-DE.ts +28 -0
- package/corpus/core/src/templates/default/app/i18n/en-US.ts +27 -0
- package/corpus/core/src/templates/default/app/i18n/es-ES.ts +28 -0
- package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +28 -0
- package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +25 -0
- package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +27 -0
- package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +27 -0
- package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +27 -0
- package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +24 -0
- package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +24 -0
- package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-page/SKILL.md +10 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +53 -8
- package/corpus/core/src/templates/workspace-core/.agents/skills/browser-sessions/SKILL.md +131 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +13 -4
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +23 -15
- package/corpus/core/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +33 -18
- package/corpus/core/src/tracking/providers.ts +1 -0
- package/corpus/core/src/tracking/registry.ts +2 -1
- package/corpus/core/src/tracking/types.ts +1 -0
- package/corpus/core/src/triggers/actions/list-automations.ts +43 -44
- package/corpus/core/src/triggers/actions/manage-automation.ts +17 -48
- package/corpus/core/src/triggers/actions.ts +218 -127
- package/corpus/core/src/triggers/dispatcher.ts +181 -367
- package/corpus/core/src/triggers/routes.ts +84 -27
- package/corpus/core/src/triggers/types.ts +9 -23
- package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/analytics/AGENTS.md +5 -0
- package/corpus/templates/analytics/actions/hubspot-deals.ts +1 -0
- package/corpus/templates/analytics/actions/hubspot-records.ts +1 -0
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +13 -4
- package/corpus/templates/analytics/actions/rename-dashboard.ts +8 -3
- package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +8 -3
- package/corpus/templates/analytics/actions/update-dashboard.ts +22 -5
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +1 -82
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +4 -4
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +23 -13
- package/corpus/templates/analytics/app/global.css +21 -0
- package/corpus/templates/analytics/app/i18n-data.ts +165 -101
- package/corpus/templates/analytics/app/lib/custom-block-promotion.ts +62 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +17 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +77 -12
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-save-queue.ts +34 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +42 -26
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +15 -0
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +1 -1
- package/corpus/templates/analytics/changelog/2026-07-29-chat-history-now-closes-smoothly-and-stays-ready-when-you-re.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-chat-history-stays-visible-when-you-return-to-the-dashboard.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-custom-dashboard-blocks-now-identify-one-off-agent-patches-a.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-dashboard-chart-deletions-now-save-in-order-so-removed-rows-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-dashboard-chart-loaders-are-easier-to-see-and-animate-more-s.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-plan-mode-can-now-query-connected-analytics-sources-while-ke.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-plan-mode-can-use-clips-as-read-only-prompt-context.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-signup-entry-pages-now-show-the-first-page-viewed-before-eac.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +26 -1
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -33
- package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +2 -0
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +13 -3
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +9 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +44 -1
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +96 -16
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +18 -27
- package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -0
- package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -8
- package/corpus/templates/assets/changelog/2026-07-29-chat-history-now-closes-smoothly-and-stays-ready-when-you-re.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-29-chat-history-stays-visible-when-you-return-to-the-workspace.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +3 -7
- package/corpus/templates/brain/app/root.tsx +0 -3
- package/corpus/templates/brain/changelog/2026-07-29-chat-history-now-closes-smoothly-and-stays-ready-when-you-re.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-29-chat-history-stays-visible-when-you-return-to-ask-brain.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-29-extensions-are-no-longer-shown-as-a-default-command-menu-des.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +25 -5
- package/corpus/templates/calendar/AGENTS.md +5 -0
- package/corpus/templates/calendar/actions/create-event.ts +43 -13
- package/corpus/templates/calendar/actions/event-action-helpers.ts +131 -1
- package/corpus/templates/calendar/actions/get-event.ts +1 -0
- package/corpus/templates/calendar/actions/manage-event-draft.ts +65 -2
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +438 -258
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +19 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +7 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +14 -19
- package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +24 -0
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +15 -2
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +1 -5
- package/corpus/templates/calendar/app/hooks/use-events.ts +51 -5
- package/corpus/templates/calendar/app/i18n-data.ts +50 -0
- package/corpus/templates/calendar/app/lib/all-day-layout.ts +9 -2
- package/corpus/templates/calendar/app/lib/event-form-initialization.ts +4 -8
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +43 -8
- package/corpus/templates/calendar/app/lib/location-suggestions.ts +36 -0
- package/corpus/templates/calendar/app/lib/out-of-office.ts +87 -1
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +82 -20
- package/corpus/templates/calendar/changelog/2026-07-26-out-of-office-events-now-default-to-full-days-with-a-ready.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-29-recent-calendar-locations-now-appear-as-address-suggestions-.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-29-unnamed-events-now-use-a-title-placeholder.md +6 -0
- package/corpus/templates/calendar/server/lib/find-time.ts +29 -5
- package/corpus/templates/calendar/server/lib/google-calendar.ts +2 -0
- package/corpus/templates/calendar/server/lib/ical-fetcher.ts +1 -0
- package/corpus/templates/calendar/shared/api.ts +4 -0
- package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +1 -5
- package/corpus/templates/chat/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -5
- package/corpus/templates/clips/changelog/2026-07-29-fixed-system-audio-recordings-picking-up-your-microphone-in-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-29-the-desktop-camera-preview-now-closes-after-a-recording-fin.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +14 -15
- package/corpus/templates/clips/desktop/src/lib/bubble-session.ts +11 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +9 -4
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +11 -0
- package/corpus/templates/clips/server/jobs/transactional-emails.ts +47 -23
- package/corpus/templates/clips/server/lib/transactional-email-store.ts +16 -6
- package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/content/.agents/skills/content-product-development/SKILL.md +103 -0
- package/corpus/templates/content/.agents/skills/content-product-development/references/verification.md +57 -0
- package/corpus/templates/content/AGENTS.md +2 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -6
- package/corpus/templates/content/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/content/docs/product/README.md +115 -0
- package/corpus/templates/content/docs/product/architecture.md +98 -0
- package/corpus/templates/content/docs/product/capabilities/content.access.page-database.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.access.row-private.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.access.safe-aggregate.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.access.visibility-closure.md +79 -0
- package/corpus/templates/content/docs/product/capabilities/content.action.button.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.agent.action-parity.md +73 -0
- package/corpus/templates/content/docs/product/capabilities/content.agent.audience-safe.md +91 -0
- package/corpus/templates/content/docs/product/capabilities/content.agent.automation.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.agent.expression-authoring.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.agent.presence.md +103 -0
- package/corpus/templates/content/docs/product/capabilities/content.agent.resource-consent.md +90 -0
- package/corpus/templates/content/docs/product/capabilities/content.agent.skill-catalog.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.api.cms.md +70 -0
- package/corpus/templates/content/docs/product/capabilities/content.author.code.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.author.document-editor.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.author.footnotes.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.author.math.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.author.media.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.author.mermaid.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.automation.scheduled.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.capture.enrich.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.command.fabric.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.diff.ai-assist.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.diff.filtered-review.md +79 -0
- package/corpus/templates/content/docs/product/capabilities/content.diff.in-place.md +80 -0
- package/corpus/templates/content/docs/product/capabilities/content.discussion.page.md +80 -0
- package/corpus/templates/content/docs/product/capabilities/content.embed.host-grant.md +70 -0
- package/corpus/templates/content/docs/product/capabilities/content.embed.mcp-app.md +69 -0
- package/corpus/templates/content/docs/product/capabilities/content.embed.surface.md +74 -0
- package/corpus/templates/content/docs/product/capabilities/content.event.committed.md +85 -0
- package/corpus/templates/content/docs/product/capabilities/content.expression.cached-result.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.expression.catalog.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.expression.language.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.feedback.signal.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.form.shared-engine.md +72 -0
- package/corpus/templates/content/docs/product/capabilities/content.history.queryable.md +79 -0
- package/corpus/templates/content/docs/product/capabilities/content.home.global.md +80 -0
- package/corpus/templates/content/docs/product/capabilities/content.job.durable.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.knowledge.graph.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.knowledge.links.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.knowledge.search.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.layout.responsive.md +80 -0
- package/corpus/templates/content/docs/product/capabilities/content.navigation.sidebar.md +86 -0
- package/corpus/templates/content/docs/product/capabilities/content.notification.source.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.object.block.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.object.blocks-field.md +79 -0
- package/corpus/templates/content/docs/product/capabilities/content.object.database.md +92 -0
- package/corpus/templates/content/docs/product/capabilities/content.object.multi-membership.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.object.page.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.object.reference.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.object.transclusion.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.organization.teams.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.portability.pdf-export.md +78 -0
- package/corpus/templates/content/docs/product/capabilities/content.portability.roundtrip.md +154 -0
- package/corpus/templates/content/docs/product/capabilities/content.portability.source-representation.md +70 -0
- package/corpus/templates/content/docs/product/capabilities/content.portability.vault-export.md +80 -0
- package/corpus/templates/content/docs/product/capabilities/content.presentation.mode.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.preset.catalog.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.property.actor.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.property.catalog.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.property.constraints.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.property.guarded-change.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.property.location.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.property.typed.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.publish.public.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.publish.reading.md +74 -0
- package/corpus/templates/content/docs/product/capabilities/content.query.object.md +114 -0
- package/corpus/templates/content/docs/product/capabilities/content.reader.documents.md +69 -0
- package/corpus/templates/content/docs/product/capabilities/content.reader.surface.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.relationship.edge.md +183 -0
- package/corpus/templates/content/docs/product/capabilities/content.renderer.artifact-block.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.renderer.custom-block.md +81 -0
- package/corpus/templates/content/docs/product/capabilities/content.renderer.graph.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.renderer.typed.md +92 -0
- package/corpus/templates/content/docs/product/capabilities/content.research.annotation.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.research.citation.md +70 -0
- package/corpus/templates/content/docs/product/capabilities/content.review.code.md +67 -0
- package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.rule.deterministic.md +79 -0
- package/corpus/templates/content/docs/product/capabilities/content.schedule.constraints.md +81 -0
- package/corpus/templates/content/docs/product/capabilities/content.security.private-vault.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.share.views.md +140 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.adapters.md +154 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.builder-codec.md +70 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.catalog.md +149 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.file-folder.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.local-bridge.md +156 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.local-project.md +70 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.page-link.md +70 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.row-union.md +69 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.spaces-files.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.sync-policy.md +161 -0
- package/corpus/templates/content/docs/product/capabilities/content.system.dependencies.md +80 -0
- package/corpus/templates/content/docs/product/capabilities/content.system.my-tasks.md +75 -0
- package/corpus/templates/content/docs/product/capabilities/content.system.project-status.md +77 -0
- package/corpus/templates/content/docs/product/capabilities/content.system.research-workspace.md +80 -0
- package/corpus/templates/content/docs/product/capabilities/content.system.task-project.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.template.governance.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.template.graph.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.template.item-body.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.template.update.md +71 -0
- package/corpus/templates/content/docs/product/capabilities/content.time.types.md +85 -0
- package/corpus/templates/content/docs/product/capabilities/content.version.branching.md +151 -0
- package/corpus/templates/content/docs/product/capabilities/content.version.field-history.md +103 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.canvas.md +94 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.chart.md +91 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.dynamic-create.md +137 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.fast-capture.md +82 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.graph.md +95 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.grouping-aggregation.md +83 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.map.md +77 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.personal-state.md +99 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.pivot.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.query.md +106 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.renderer-conformance.md +77 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.scale.md +77 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.source-query.md +159 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.timeline.md +86 -0
- package/corpus/templates/content/docs/product/capabilities/content.view.tree.md +77 -0
- package/corpus/templates/content/docs/product/capabilities/content.workspace.multi-scope.md +80 -0
- package/corpus/templates/content/docs/product/capabilities/content.workspace.session-restore.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.workspace.view-instance.md +76 -0
- package/corpus/templates/content/docs/product/capabilities/content.workspace.working-set.md +77 -0
- package/corpus/templates/content/docs/product/chapters/content.chapter.capture-research.md +20 -0
- package/corpus/templates/content/docs/product/chapters/content.chapter.connected-sources.md +20 -0
- package/corpus/templates/content/docs/product/chapters/content.chapter.consensus.md +19 -0
- package/corpus/templates/content/docs/product/chapters/content.chapter.durable-home.md +21 -0
- package/corpus/templates/content/docs/product/chapters/content.chapter.publishing-portability.md +21 -0
- package/corpus/templates/content/docs/product/chapters/content.chapter.working-systems.md +27 -0
- package/corpus/templates/content/docs/product/encyclopedia.md +559 -0
- package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md +39 -0
- package/corpus/templates/content/docs/product/features/content.feature.build-living-dashboards.md +34 -0
- package/corpus/templates/content/docs/product/features/content.feature.build-new-surfaces.md +33 -0
- package/corpus/templates/content/docs/product/features/content.feature.capture-into-action.md +34 -0
- package/corpus/templates/content/docs/product/features/content.feature.cite-what-you-found.md +34 -0
- package/corpus/templates/content/docs/product/features/content.feature.collaborate-in-context.md +59 -0
- package/corpus/templates/content/docs/product/features/content.feature.collect-structured-input.md +38 -0
- package/corpus/templates/content/docs/product/features/content.feature.connect-your-sources.md +38 -0
- package/corpus/templates/content/docs/product/features/content.feature.data-that-keeps-itself-right.md +45 -0
- package/corpus/templates/content/docs/product/features/content.feature.durable-foundations.md +48 -0
- package/corpus/templates/content/docs/product/features/content.feature.evolve-systems-safely.md +38 -0
- package/corpus/templates/content/docs/product/features/content.feature.explore-alternatives-safely.md +39 -0
- package/corpus/templates/content/docs/product/features/content.feature.find-your-place-again.md +40 -0
- package/corpus/templates/content/docs/product/features/content.feature.keep-your-private-vault-private.md +39 -0
- package/corpus/templates/content/docs/product/features/content.feature.living-references.md +38 -0
- package/corpus/templates/content/docs/product/features/content.feature.make-the-workspace-yours.md +40 -0
- package/corpus/templates/content/docs/product/features/content.feature.move-without-starting-over.md +38 -0
- package/corpus/templates/content/docs/product/features/content.feature.plan-work-across-time.md +39 -0
- package/corpus/templates/content/docs/product/features/content.feature.publish-with-confidence.md +43 -0
- package/corpus/templates/content/docs/product/features/content.feature.put-your-organizations-know-how-to-work.md +38 -0
- package/corpus/templates/content/docs/product/features/content.feature.read-and-annotate-anything.md +38 -0
- package/corpus/templates/content/docs/product/features/content.feature.review-changes-in-place.md +39 -0
- package/corpus/templates/content/docs/product/features/content.feature.run-projects-your-way.md +44 -0
- package/corpus/templates/content/docs/product/features/content.feature.see-your-information-your-way.md +44 -0
- package/corpus/templates/content/docs/product/features/content.feature.share-how-your-organization-works.md +38 -0
- package/corpus/templates/content/docs/product/features/content.feature.sketch-connections-keep-whats-true.md +39 -0
- package/corpus/templates/content/docs/product/features/content.feature.take-the-whole-vault-with-you.md +39 -0
- package/corpus/templates/content/docs/product/features/content.feature.trust-your-connected-sources.md +40 -0
- package/corpus/templates/content/docs/product/features/content.feature.understand-what-your-data-says.md +39 -0
- package/corpus/templates/content/docs/product/features/content.feature.when-this-happens-that-follows.md +43 -0
- package/corpus/templates/content/docs/product/features/content.feature.work-across-every-workspace.md +37 -0
- package/corpus/templates/content/docs/product/features/content.feature.work-on-content-inside-another-application.md +37 -0
- package/corpus/templates/content/docs/product/roadmap.md +878 -0
- package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +1 -7
- package/corpus/templates/crm/app/i18n/en-US.ts +4 -0
- package/corpus/templates/crm/app/routes/settings.tsx +33 -8
- package/corpus/templates/crm/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +133 -63
- package/corpus/templates/design/.generated/bridge/embedded-wheel.generated.ts +19 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +51 -14
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +41 -26
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +22 -4
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +216 -116
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +21 -2
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -1
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbenchLoader.tsx +10 -2
- package/corpus/templates/design/app/components/design/code-workbench/code-workbench-shell.ts +2 -0
- package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +60 -55
- package/corpus/templates/design/app/components/design/design-canvas/content-size-report.ts +88 -2
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +3 -2
- package/corpus/templates/design/app/components/design/types.ts +4 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -5
- package/corpus/templates/design/app/pages/design-editor/clipboard-layer-source.ts +28 -0
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +117 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +3 -1
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +120 -74
- package/corpus/templates/design/app/pages/design-editor/pending-structure-verification.ts +11 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +2 -0
- package/corpus/templates/design/app/pages/design-editor/source-location.ts +1 -1
- package/corpus/templates/design/changelog/2026-07-29-copy-rendered-ui-between-live-local-apps-and-design-files-wh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-design-boards-stay-dark-and-viewport-height-content-keeps-it.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-design-frames-no-longer-grow-continuously-when-their-content.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-the-code-panel-and-apply-design-update-control-now-use-clear.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-the-tools-panel-now-keeps-extension-creation-and-discovery-h.md +6 -0
- package/corpus/templates/design/shared/source-mode.ts +7 -1
- package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +171 -0
- package/corpus/templates/dispatch/app/routes/browser-chat.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/browser-connect.tsx +4 -0
- package/corpus/templates/dispatch/changelog/2026-07-29-browser-chat-can-securely-connect-the-agent-native-chrome-ex.md +6 -0
- package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +1 -6
- package/corpus/templates/forms/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -5
- package/corpus/templates/macros/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -9
- package/corpus/templates/mail/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +3 -7
- package/corpus/templates/plan/app/pages/PlansPage.tsx +2 -2
- package/corpus/templates/plan/changelog/2026-07-29-chat-history-now-closes-smoothly-and-stays-ready-when-you-re.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-29-chat-history-stays-visible-when-you-return-to-ask-plan.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-29-fixed-plan-pages-crashing-when-recent-activity-is-unavailabl.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/slides/.agents/skills/analytics-data-for-decks/SKILL.md +48 -0
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +60 -4
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +20 -0
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +30 -1
- package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +3 -1
- package/corpus/templates/slides/AGENTS.md +25 -24
- package/corpus/templates/slides/actions/check-image-gen.ts +8 -2
- package/corpus/templates/slides/actions/create-deck.ts +4 -12
- package/corpus/templates/slides/actions/export-pptx.ts +25 -3
- package/corpus/templates/slides/actions/generate-image-api.ts +4 -2
- package/corpus/templates/slides/actions/generate-slides-ai.ts +1 -1
- package/corpus/templates/slides/actions/get-deck-reference-context.ts +136 -0
- package/corpus/templates/slides/actions/get-workspace-defaults.ts +55 -0
- package/corpus/templates/slides/actions/image-gen-status.ts +10 -1
- package/corpus/templates/slides/actions/import-file.ts +53 -38
- package/corpus/templates/slides/actions/list-decks.ts +2 -0
- package/corpus/templates/slides/actions/patch-deck.ts +2 -0
- package/corpus/templates/slides/actions/set-workspace-defaults.ts +50 -0
- package/corpus/templates/slides/actions/view-screen.ts +6 -0
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +1 -1
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +67 -3
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +40 -8
- package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +33 -8
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +6 -2
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +114 -128
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +74 -9
- package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1193 -267
- package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +302 -102
- package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +270 -0
- package/corpus/templates/slides/app/components/editor/slide-escape-arbiter.ts +34 -0
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +368 -0
- package/corpus/templates/slides/app/components/editor/slide-presence.ts +13 -0
- package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +23 -3
- package/corpus/templates/slides/app/components/layout/Layout.tsx +39 -6
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +1 -6
- package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +23 -0
- package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +0 -2
- package/corpus/templates/slides/app/context/DeckContext.tsx +2 -0
- package/corpus/templates/slides/app/global.css +60 -0
- package/corpus/templates/slides/app/hooks/use-agent-generating.ts +76 -5
- package/corpus/templates/slides/app/hooks/use-workspace-defaults.ts +26 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +39 -3
- package/corpus/templates/slides/app/lib/generation-state.ts +13 -3
- package/corpus/templates/slides/app/lib/slide-image-replacement.ts +27 -6
- package/corpus/templates/slides/app/lib/text-tool-shortcut.ts +62 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +197 -83
- package/corpus/templates/slides/app/pages/DesignSystems.tsx +95 -0
- package/corpus/templates/slides/app/pages/Index.tsx +348 -53
- package/corpus/templates/slides/changelog/2026-07-28-pick-an-existing-deck-as-a-style-reference-when-creating-a-n.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-ai-editing-status-is-now-quieter-and-only-shows-slide-presen.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-design-systems-and-reference-decks-are-easier-to-choose-toge.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-generated-decks-now-receive-a-concise-relevant-title-before-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-google-slides-exports-can-connect-your-google-account-direct.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-new-deck-prompts-reuse-empty-chats.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-new-deck-setup-now-stays-in-the-editor-with-closable-style-a.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-selected-words-and-text-runs-can-now-be-styled-independently.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-slide-annotation-tools-are-clearer-and-escape-now-exits-anno.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-slide-editing-now-opens-with-the-app-navigation-collapsed-fo.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-slide-editing-stays-visually-clean-on-hover-without-a-canvas.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-slides-now-appear-one-by-one-while-ai-builds-a-deck-with-ste.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-text-boxes-now-select-move-duplicate-resize-and-style-like-a.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-29-untitled-decks-now-use-a-clearer-this-deck-label-in-the-agen.md +6 -0
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +4 -2
- package/corpus/templates/slides/server/handlers/image-providers/builder.ts +305 -0
- package/corpus/templates/slides/server/handlers/image-providers/index.ts +6 -3
- package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/slides/server/workspace-defaults.ts +169 -0
- package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +31 -8
- package/corpus/templates/tasks/app/i18n/en-US.ts +7 -0
- package/corpus/templates/tasks/app/routes/settings.tsx +25 -2
- package/corpus/templates/tasks/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +15 -0
- package/corpus/toolkit/README.md +2 -2
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/app-shell/sidebar-footer-actions.tsx +0 -3
- package/corpus/toolkit/src/chat-history.css +1 -0
- package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +35 -14
- package/corpus/toolkit/src/composer/PromptComposer.tsx +6 -0
- package/corpus/toolkit/src/composer/TiptapComposer.tsx +11 -3
- package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +64 -11
- package/dist/a2a/types.d.ts +6 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/action.d.ts +57 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +9 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/builder-engine.js +6 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/production-agent.d.ts +11 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +131 -90
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/tool-search.d.ts +4 -0
- package/dist/agent/tool-search.d.ts.map +1 -1
- package/dist/agent/tool-search.js +30 -0
- package/dist/agent/tool-search.js.map +1 -1
- package/dist/automations/service.d.ts +85 -0
- package/dist/automations/service.d.ts.map +1 -0
- package/dist/automations/service.js +332 -0
- package/dist/automations/service.js.map +1 -0
- package/dist/browser-context/index.d.ts +579 -0
- package/dist/browser-context/index.d.ts.map +1 -0
- package/dist/browser-context/index.js +331 -0
- package/dist/browser-context/index.js.map +1 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/FeedbackButton.d.ts +6 -2
- package/dist/client/FeedbackButton.d.ts.map +1 -1
- package/dist/client/FeedbackButton.js +20 -3
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +19 -2
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +15 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +13 -3
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/agent-page/AgentJobsTab.d.ts +1 -0
- package/dist/client/agent-page/AgentJobsTab.d.ts.map +1 -1
- package/dist/client/agent-page/AgentJobsTab.js +186 -98
- package/dist/client/agent-page/AgentJobsTab.js.map +1 -1
- package/dist/client/agent-page/AgentTabsPage.js +4 -4
- package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
- package/dist/client/agent-page/use-jobs.d.ts +7 -0
- package/dist/client/agent-page/use-jobs.d.ts.map +1 -1
- package/dist/client/agent-page/use-jobs.js.map +1 -1
- package/dist/client/analytics-session.d.ts.map +1 -1
- package/dist/client/analytics-session.js +13 -0
- package/dist/client/analytics-session.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +29 -6
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/settings/AutomationsSection.d.ts +0 -9
- package/dist/client/settings/AutomationsSection.d.ts.map +1 -1
- package/dist/client/settings/AutomationsSection.js +9 -168
- package/dist/client/settings/AutomationsSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts +9 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +19 -11
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/index.d.ts +1 -1
- package/dist/client/settings/index.d.ts.map +1 -1
- package/dist/client/settings/index.js +1 -1
- package/dist/client/settings/index.js.map +1 -1
- package/dist/client/use-agent-chat.d.ts.map +1 -1
- package/dist/client/use-agent-chat.js +20 -3
- package/dist/client/use-agent-chat.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +4 -17
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +2 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/workspace-deploy.d.ts.map +1 -1
- package/dist/deploy/workspace-deploy.js +2 -1
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +82 -6
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/fetch-tool.d.ts.map +1 -1
- package/dist/extensions/fetch-tool.js +11 -0
- package/dist/extensions/fetch-tool.js.map +1 -1
- package/dist/extensions/routes.d.ts +5 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +7 -1
- package/dist/extensions/routes.js.map +1 -1
- package/dist/extensions/web-content.d.ts +1 -0
- package/dist/extensions/web-content.d.ts.map +1 -1
- package/dist/extensions/web-content.js +82 -1
- package/dist/extensions/web-content.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/computer-supervision.d.ts.map +1 -1
- package/dist/integrations/computer-supervision.js +22 -1
- package/dist/integrations/computer-supervision.js.map +1 -1
- package/dist/integrations/index.d.ts +3 -2
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +3 -2
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/integration-durable-dispatch-config.d.ts +2 -0
- package/dist/integrations/integration-durable-dispatch-config.d.ts.map +1 -1
- package/dist/integrations/integration-durable-dispatch-config.js +4 -0
- package/dist/integrations/integration-durable-dispatch-config.js.map +1 -1
- package/dist/integrations/integration-durable-dispatch.d.ts +2 -2
- package/dist/integrations/integration-durable-dispatch.d.ts.map +1 -1
- package/dist/integrations/integration-durable-dispatch.js +2 -2
- package/dist/integrations/integration-durable-dispatch.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +84 -39
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/remote-browser-actions.d.ts +7 -0
- package/dist/integrations/remote-browser-actions.d.ts.map +1 -0
- package/dist/integrations/remote-browser-actions.js +482 -0
- package/dist/integrations/remote-browser-actions.js.map +1 -0
- package/dist/jobs/actions/list-recurring-jobs.d.ts.map +1 -1
- package/dist/jobs/actions/list-recurring-jobs.js +4 -6
- package/dist/jobs/actions/list-recurring-jobs.js.map +1 -1
- package/dist/jobs/actions/manage-recurring-job.js +3 -6
- package/dist/jobs/actions/manage-recurring-job.js.map +1 -1
- package/dist/jobs/background-automation-runner.d.ts +76 -0
- package/dist/jobs/background-automation-runner.d.ts.map +1 -0
- package/dist/jobs/background-automation-runner.js +288 -0
- package/dist/jobs/background-automation-runner.js.map +1 -0
- package/dist/jobs/frontmatter.d.ts +63 -0
- package/dist/jobs/frontmatter.d.ts.map +1 -0
- package/dist/jobs/frontmatter.js +216 -0
- package/dist/jobs/frontmatter.js.map +1 -0
- package/dist/jobs/scheduler.d.ts +6 -60
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +67 -484
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/jobs/tools.d.ts.map +1 -1
- package/dist/jobs/tools.js +24 -3
- package/dist/jobs/tools.js.map +1 -1
- package/dist/localization/default-messages.d.ts +28 -33
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +34 -39
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +4 -0
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/notifications/actions.d.ts.map +1 -1
- package/dist/notifications/actions.js +5 -0
- package/dist/notifications/actions.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/actions.d.ts.map +1 -1
- package/dist/progress/actions.js +5 -0
- package/dist/progress/actions.js.map +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/github-repo-files.d.ts.map +1 -1
- package/dist/provider-api/actions/github-repo-files.js +9 -0
- package/dist/provider-api/actions/github-repo-files.js.map +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +16 -16
- package/dist/provider-api/actions/provider-api.d.ts.map +1 -1
- package/dist/provider-api/actions/provider-api.js +14 -0
- package/dist/provider-api/actions/provider-api.js.map +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-capabilities.d.ts.map +1 -1
- package/dist/server/agent-capabilities.js +32 -3
- package/dist/server/agent-capabilities.js.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +17 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +50 -1
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/agent-chat/browser-team-tools.d.ts.map +1 -1
- package/dist/server/agent-chat/browser-team-tools.js +90 -3
- package/dist/server/agent-chat/browser-team-tools.js.map +1 -1
- package/dist/server/agent-chat/framework-prompts.d.ts.map +1 -1
- package/dist/server/agent-chat/framework-prompts.js +7 -11
- package/dist/server/agent-chat/framework-prompts.js.map +1 -1
- package/dist/server/agent-chat/plugin-options.d.ts +3 -3
- package/dist/server/agent-chat/plugin-options.js.map +1 -1
- package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
- package/dist/server/agent-chat/script-entries.js +43 -1
- package/dist/server/agent-chat/script-entries.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +24 -5
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/attribution.d.ts +6 -0
- package/dist/server/attribution.d.ts.map +1 -1
- package/dist/server/attribution.js +14 -14
- package/dist/server/attribution.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +5 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +2 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +6 -3
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +7 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +3 -1
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +25 -0
- package/dist/server/csrf.js.map +1 -1
- package/dist/server/google-oauth.d.ts +3 -0
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +12 -1
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +18 -0
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/prompts/framework-core-compact.js +3 -3
- package/dist/server/prompts/framework-core-compact.js.map +1 -1
- package/dist/server/prompts/framework-core.js +3 -3
- package/dist/server/prompts/framework-core.js.map +1 -1
- package/dist/server/prompts/shared-rules.js +3 -3
- package/dist/server/prompts/shared-rules.js.map +1 -1
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.d.ts.map +1 -1
- package/dist/settings/index.js +1 -1
- package/dist/settings/index.js.map +1 -1
- package/dist/shared/analytics-anonymous-id.d.ts +12 -0
- package/dist/shared/analytics-anonymous-id.d.ts.map +1 -0
- package/dist/shared/analytics-anonymous-id.js +29 -0
- package/dist/shared/analytics-anonymous-id.js.map +1 -0
- package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +1 -5
- package/dist/templates/chat/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/dist/templates/default/app/i18n/ar-SA.ts +26 -0
- package/dist/templates/default/app/i18n/de-DE.ts +28 -0
- package/dist/templates/default/app/i18n/en-US.ts +27 -0
- package/dist/templates/default/app/i18n/es-ES.ts +28 -0
- package/dist/templates/default/app/i18n/fr-FR.ts +28 -0
- package/dist/templates/default/app/i18n/hi-IN.ts +25 -0
- package/dist/templates/default/app/i18n/ja-JP.ts +27 -0
- package/dist/templates/default/app/i18n/ko-KR.ts +27 -0
- package/dist/templates/default/app/i18n/pt-BR.ts +27 -0
- package/dist/templates/default/app/i18n/zh-CN.ts +24 -0
- package/dist/templates/default/app/i18n/zh-TW.ts +24 -0
- package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/dist/templates/workspace-core/.agents/skills/agent-page/SKILL.md +10 -7
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +53 -8
- package/dist/templates/workspace-core/.agents/skills/browser-sessions/SKILL.md +131 -0
- package/dist/templates/workspace-core/.agents/skills/extension-points/SKILL.md +13 -4
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +23 -15
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +33 -18
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +1 -0
- package/dist/tracking/providers.js.map +1 -1
- package/dist/tracking/registry.d.ts +1 -0
- package/dist/tracking/registry.d.ts.map +1 -1
- package/dist/tracking/registry.js +1 -0
- package/dist/tracking/registry.js.map +1 -1
- package/dist/tracking/types.d.ts +1 -0
- package/dist/tracking/types.d.ts.map +1 -1
- package/dist/tracking/types.js.map +1 -1
- package/dist/triggers/actions/list-automations.d.ts +7 -0
- package/dist/triggers/actions/list-automations.d.ts.map +1 -1
- package/dist/triggers/actions/list-automations.js +29 -41
- package/dist/triggers/actions/list-automations.js.map +1 -1
- package/dist/triggers/actions/manage-automation.js +15 -33
- package/dist/triggers/actions/manage-automation.js.map +1 -1
- package/dist/triggers/actions.d.ts.map +1 -1
- package/dist/triggers/actions.js +184 -95
- package/dist/triggers/actions.js.map +1 -1
- package/dist/triggers/dispatcher.d.ts +3 -9
- package/dist/triggers/dispatcher.d.ts.map +1 -1
- package/dist/triggers/dispatcher.js +134 -304
- package/dist/triggers/dispatcher.js.map +1 -1
- package/dist/triggers/routes.d.ts.map +1 -1
- package/dist/triggers/routes.js +76 -17
- package/dist/triggers/routes.js.map +1 -1
- package/dist/triggers/types.d.ts +4 -23
- package/dist/triggers/types.d.ts.map +1 -1
- package/dist/triggers/types.js.map +1 -1
- package/docs/content/agent-surfaces.mdx +4 -4
- package/docs/content/automations.mdx +61 -12
- package/docs/content/deployment.mdx +11 -4
- package/docs/content/extensions.mdx +39 -33
- package/docs/content/recurring-jobs.mdx +41 -19
- package/docs/content/template-dispatch-operations.mdx +19 -7
- package/package.json +4 -2
- package/src/a2a/types.ts +6 -0
- package/src/action.ts +68 -0
- package/src/agent/engine/builder-engine.ts +8 -4
- package/src/agent/production-agent.ts +175 -100
- package/src/agent/tool-search.ts +36 -0
- package/src/automations/service.ts +506 -0
- package/src/browser-context/index.ts +392 -0
- package/src/client/AgentPanel.tsx +2 -1
- package/src/client/FeedbackButton.tsx +37 -4
- package/src/client/MultiTabAssistantChat.tsx +21 -0
- package/src/client/agent-chat.ts +31 -3
- package/src/client/agent-page/AgentJobsTab.tsx +438 -310
- package/src/client/agent-page/AgentTabsPage.tsx +4 -4
- package/src/client/agent-page/use-jobs.ts +7 -0
- package/src/client/analytics-session.ts +13 -0
- package/src/client/extensions/ExtensionViewer.tsx +67 -0
- package/src/client/settings/AutomationsSection.tsx +27 -499
- package/src/client/settings/SettingsPanel.tsx +32 -11
- package/src/client/settings/index.ts +2 -0
- package/src/client/use-agent-chat.ts +35 -3
- package/src/client/use-chat-threads.ts +8 -25
- package/src/deploy/build.ts +2 -0
- package/src/deploy/workspace-deploy.ts +2 -0
- package/src/extensions/actions.ts +115 -3
- package/src/extensions/fetch-tool.ts +11 -0
- package/src/extensions/routes.ts +16 -1
- package/src/extensions/web-content.ts +96 -1
- package/src/integrations/computer-supervision.ts +33 -1
- package/src/integrations/index.ts +6 -0
- package/src/integrations/integration-durable-dispatch-config.ts +10 -0
- package/src/integrations/integration-durable-dispatch.ts +4 -0
- package/src/integrations/plugin.ts +94 -41
- package/src/integrations/remote-browser-actions.ts +621 -0
- package/src/jobs/actions/list-recurring-jobs.ts +5 -7
- package/src/jobs/actions/manage-recurring-job.ts +3 -7
- package/src/jobs/background-automation-runner.ts +437 -0
- package/src/jobs/frontmatter.ts +308 -0
- package/src/jobs/scheduler.ts +89 -598
- package/src/jobs/tools.ts +22 -3
- package/src/localization/default-messages.ts +45 -45
- package/src/mcp-client/index.ts +6 -0
- package/src/notifications/actions.ts +5 -0
- package/src/progress/actions.ts +5 -0
- package/src/provider-api/actions/github-repo-files.ts +11 -0
- package/src/provider-api/actions/provider-api.ts +13 -0
- package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +4 -4
- package/src/server/action-discovery.ts +7 -0
- package/src/server/agent-capabilities.ts +43 -8
- package/src/server/agent-chat/action-filters-a2a.ts +63 -1
- package/src/server/agent-chat/browser-team-tools.ts +126 -3
- package/src/server/agent-chat/framework-prompts.ts +7 -11
- package/src/server/agent-chat/plugin-options.ts +3 -3
- package/src/server/agent-chat/script-entries.ts +52 -1
- package/src/server/agent-chat-plugin.ts +25 -5
- package/src/server/attribution.ts +22 -0
- package/src/server/auth.ts +20 -6
- package/src/server/better-auth-instance.ts +10 -2
- package/src/server/core-routes-plugin.ts +10 -1
- package/src/server/csrf.ts +25 -0
- package/src/server/google-oauth.ts +21 -1
- package/src/server/onboarding-html.ts +18 -0
- package/src/server/prompts/framework-core-compact.ts +3 -3
- package/src/server/prompts/framework-core.ts +3 -3
- package/src/server/prompts/shared-rules.ts +3 -3
- package/src/settings/index.ts +1 -0
- package/src/shared/analytics-anonymous-id.ts +35 -0
- package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/src/templates/chat/app/components/layout/Sidebar.tsx +1 -5
- package/src/templates/chat/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/src/templates/default/app/i18n/ar-SA.ts +26 -0
- package/src/templates/default/app/i18n/de-DE.ts +28 -0
- package/src/templates/default/app/i18n/en-US.ts +27 -0
- package/src/templates/default/app/i18n/es-ES.ts +28 -0
- package/src/templates/default/app/i18n/fr-FR.ts +28 -0
- package/src/templates/default/app/i18n/hi-IN.ts +25 -0
- package/src/templates/default/app/i18n/ja-JP.ts +27 -0
- package/src/templates/default/app/i18n/ko-KR.ts +27 -0
- package/src/templates/default/app/i18n/pt-BR.ts +27 -0
- package/src/templates/default/app/i18n/zh-CN.ts +24 -0
- package/src/templates/default/app/i18n/zh-TW.ts +24 -0
- package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/agent-page/SKILL.md +10 -7
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +53 -8
- package/src/templates/workspace-core/.agents/skills/browser-sessions/SKILL.md +131 -0
- package/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +13 -4
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +23 -15
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +33 -18
- package/src/tracking/providers.ts +1 -0
- package/src/tracking/registry.ts +2 -1
- package/src/tracking/types.ts +1 -0
- package/src/triggers/actions/list-automations.ts +43 -44
- package/src/triggers/actions/manage-automation.ts +17 -48
- package/src/triggers/actions.ts +218 -127
- package/src/triggers/dispatcher.ts +181 -367
- package/src/triggers/routes.ts +84 -27
- package/src/triggers/types.ts +9 -23
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/server/csrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;
|
|
1
|
+
{"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/server/csrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAgLH;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,eAAe,GAAE,MAAyB;;GAsB3C"}
|
package/dist/server/csrf.js
CHANGED
|
@@ -51,6 +51,27 @@ import { getConfiguredAppBasePath } from "./app-base-path.js";
|
|
|
51
51
|
* markers. These are signed/authenticated through other mechanisms (HMAC,
|
|
52
52
|
* JWT, internal token) so they don't need cookie-based CSRF protection.
|
|
53
53
|
*/
|
|
54
|
+
/**
|
|
55
|
+
* Sub-prefixes that must stay CSRF-protected even though a broader entry in
|
|
56
|
+
* `CSRF_ALLOWLIST_PREFIXES` covers them. Checked first, and they win.
|
|
57
|
+
*
|
|
58
|
+
* An allowlist entry is a promise that everything beneath it authenticates on
|
|
59
|
+
* something the browser will not attach by itself. Mount a route that calls
|
|
60
|
+
* `requireSessionContext` under such a prefix and that promise silently breaks:
|
|
61
|
+
* the route now rides the ambient `SameSite=None` session cookie with no
|
|
62
|
+
* first-party check, and nothing about the route's own code looks wrong.
|
|
63
|
+
* `/integrations/remote/*` is exactly that shape — device-token relay routes
|
|
64
|
+
* and cookie-authenticated control routes share one subtree.
|
|
65
|
+
*/
|
|
66
|
+
const CSRF_PROTECTED_PREFIXES = [
|
|
67
|
+
// Remote-device relay. The device's own routes (poll/result/heartbeat)
|
|
68
|
+
// authenticate with a bearer token and send no cookies, so they never reach
|
|
69
|
+
// the check; the sibling routes (register, enqueue, computer/approvals,
|
|
70
|
+
// computer/commands) are session-authenticated and must not be exempt —
|
|
71
|
+
// approving a browser-control operation is a state change an attacker page
|
|
72
|
+
// must never be able to make ride the victim's cookie.
|
|
73
|
+
"/integrations/remote/",
|
|
74
|
+
];
|
|
54
75
|
const CSRF_ALLOWLIST_PREFIXES = [
|
|
55
76
|
// Integration webhooks — verified by HMAC against a per-integration secret.
|
|
56
77
|
"/integrations/",
|
|
@@ -142,6 +163,10 @@ function isOnAllowlist(pathname, frameworkPrefix) {
|
|
|
142
163
|
if (!pathname.startsWith(frameworkPrefix))
|
|
143
164
|
return false;
|
|
144
165
|
const sub = pathname.slice(frameworkPrefix.length);
|
|
166
|
+
for (const protectedPrefix of CSRF_PROTECTED_PREFIXES) {
|
|
167
|
+
if (sub.startsWith(protectedPrefix))
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
145
170
|
for (const allowed of CSRF_ALLOWLIST_PREFIXES) {
|
|
146
171
|
if (sub.startsWith(allowed))
|
|
147
172
|
return true;
|
package/dist/server/csrf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csrf.js","sourceRoot":"","sources":["../../src/server/csrf.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Defense-in-depth CSRF check for framework state-changing routes.\n *\n * Threat model: action endpoints (`/_agent-native/actions/*`), extension\n * endpoints (`/_agent-native/extensions/*` and the legacy\n * `/_agent-native/tools/*` alias), and a handful of other state-changing\n * `/_agent-native/*` routes use the better-auth session cookie, which is\n * configured with `SameSite=None; Secure; Partitioned` so the iframe editor\n * (and other cross-site embeds) can authenticate. `SameSite=None` means the\n * browser ships the session cookie on top-level form POSTs from any origin —\n * which is exactly the precondition for classic cross-site request forgery.\n *\n * The browser still gates \"non-simple\" requests behind a CORS preflight, so\n * an attacker who has to send `Content-Type: application/json` is forced\n * through OPTIONS, which our CORS middleware (`create-server.ts`) rejects\n * for disallowed origins. But the simple-request bypass (`Content-Type:\n * text/plain` on a `<form enctype=\"text/plain\">` POST, or `multipart/form-data`)\n * never preflights — the browser delivers it cross-origin with cookies.\n *\n * Mitigation: this middleware rejects any state-changing\n * (`POST/PUT/PATCH/DELETE`) request to `/_agent-native/*` that\n *\n * 1. carries the auth-cookie pattern (any cookie at all is a heuristic\n * good-enough proxy — we don't want to deny anonymous fetches), AND\n * 2. is NOT clearly same-origin / first-party. We trust:\n * - `Sec-Fetch-Site: same-origin` (sent by every modern browser on\n * same-origin fetch — Chrome/Firefox/Safari/Edge all support it).\n * - `X-Agent-Native-CSRF` custom header. Custom headers force a\n * preflight, so an attacker can't add one cross-origin.\n * - `Content-Type: application/json` request body. Same logic — JSON\n * Content-Type is a non-simple request that triggers preflight.\n *\n * Why the existing CORS check isn't enough: a simple-request POST never\n * preflights, so the browser sends it through and only blocks the *response*\n * from being readable cross-origin. The state change (delete-account, write\n * SQL, etc.) happens server-side regardless. We need a server-side check that\n * proves first-party intent before running the action.\n *\n * Opt-out marker: a handful of routes legitimately accept cross-origin POSTs\n * — webhook endpoints (Slack, Telegram, email), the public A2A endpoint\n * (`/_agent-native/a2a`), the integrations process-task self-fire, and so on.\n * Those are listed in `CSRF_ALLOWLIST_PREFIXES` below; if you add a new\n * cross-origin-callable route, add it there.\n */\n\nimport {\n defineEventHandler,\n getMethod,\n getRequestHeader,\n setResponseStatus,\n} from \"h3\";\n\nimport { MCP_PUBLIC_ROUTE_PREFIX } from \"../mcp/route-paths.js\";\nimport { getConfiguredAppBasePath } from \"./app-base-path.js\";\n\n/**\n * Path prefixes (relative to the framework prefix `/_agent-native`) that are\n * allowed to receive cross-origin state-changing POSTs without first-party\n * markers. These are signed/authenticated through other mechanisms (HMAC,\n * JWT, internal token) so they don't need cookie-based CSRF protection.\n */\nconst CSRF_ALLOWLIST_PREFIXES = [\n // Integration webhooks — verified by HMAC against a per-integration secret.\n \"/integrations/\",\n // Agent Teams durable sub-agent processor self-fire — verified by the same\n // HMAC internal-token scheme as the integration/A2A processors.\n \"/agent-teams/\",\n // Durable sandbox-execution processor self-fire (run-code background\n // queue) — verified by the same HMAC internal-token scheme.\n \"/sandbox/_process-execution\",\n // A2A JSON-RPC endpoints — verified by signed JWT (when A2A_SECRET set) or\n // explicitly opt-in unauthenticated (handled at the A2A layer).\n \"/a2a\",\n // Better Auth's own login/sign-in/social-callback routes. Better Auth\n // ships its own CSRF protection (Origin/Sec-Fetch checks on its handlers)\n // and cookies are needed for the OAuth callback round-trip.\n \"/auth/\",\n // Stripe / Paddle / billing webhooks dropped in by templates.\n \"/billing/webhook\",\n // Public share endpoints — read-only and never cookie-driven, but kept\n // here so a templated POST (e.g. comment-on-public-recording) doesn't 403.\n \"/share/\",\n // OAuth callbacks (Builder, Google, Slack, Notion, Zoom). These get a\n // `code` query param via top-level navigation — they DO ride the session\n // cookie and they SHOULD validate state, but the framework can't see the\n // state token. Each callback handler is responsible for its own CSRF\n // check (signed state tokens).\n \"/oauth/\",\n // Builder's CLI-auth callback — uses the BUILDER_STATE_PARAM signed token\n // to authenticate the round-trip; framework CSRF check would block it.\n \"/builder/callback\",\n];\n\nconst STATE_CHANGING_METHODS = new Set([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\n/**\n * Decide whether a request is \"first-party enough\" to trust as not-CSRF.\n * Any of the following make a request non-CSRF:\n *\n * - `Sec-Fetch-Site: same-origin` (or `none` for top-level navigations\n * to our own pages — but state-changing methods don't ship `none`).\n * - `X-Agent-Native-CSRF` header (any value, even \"1\"). This is a custom\n * header so the browser forces a preflight cross-origin, which our\n * CORS layer rejects for disallowed origins.\n * - `Content-Type: application/json` (case-insensitive). JSON content\n * type is a non-simple request that triggers preflight.\n *\n * We accept ANY of these — the goal is \"did the request come through a\n * channel the browser would have preflighted\", not a strict-mode token.\n *\n * NOTE: `Sec-Fetch-Site: same-site` is deliberately NOT trusted. Under a\n * shared cookie domain (COOKIE_DOMAIN / crossSubDomainCookies), the browser\n * labels a request from a SIBLING subdomain (evil.example.com → app.example.com)\n * as `same-site` even though it is cross-origin and would ride the shared\n * session cookie — a CSRF vector. Legitimate first-party clients all also send\n * `X-Agent-Native-CSRF` or `application/json`, so they still pass via those\n * paths and iframe/embed flows are unaffected.\n */\nfunction looksFirstParty(event: any): boolean {\n const sfs = getRequestHeader(event, \"sec-fetch-site\");\n if (sfs === \"same-origin\" || sfs === \"none\") {\n return true;\n }\n if (getRequestHeader(event, \"x-agent-native-csrf\")) {\n return true;\n }\n const contentType = getRequestHeader(event, \"content-type\");\n if (\n contentType &&\n typeof contentType === \"string\" &&\n contentType.toLowerCase().includes(\"application/json\")\n ) {\n return true;\n }\n return false;\n}\n\n/**\n * Returns true when the request carries any cookie. We use \"has any cookie\"\n * as a coarse heuristic for \"the browser is going to attach the session\n * cookie\" — anonymous tools (curl, server-to-server) typically don't send\n * cookies, so they bypass this check entirely.\n */\nfunction requestHasCookies(event: any): boolean {\n const cookie = getRequestHeader(event, \"cookie\");\n return typeof cookie === \"string\" && cookie.trim().length > 0;\n}\n\n/**\n * The path is the full request URL pathname (e.g.\n * `/_agent-native/actions/foo` or `/app/_agent-native/actions/foo`).\n * `frameworkPrefix` is the root framework route prefix without a trailing\n * slash, e.g. `/_agent-native`.\n */\nfunction isOnAllowlist(pathname: string, frameworkPrefix: string): boolean {\n if (!pathname.startsWith(frameworkPrefix)) return false;\n const sub = pathname.slice(frameworkPrefix.length);\n for (const allowed of CSRF_ALLOWLIST_PREFIXES) {\n if (sub.startsWith(allowed)) return true;\n }\n return false;\n}\n\nfunction matchingFrameworkPrefix(\n pathname: string,\n frameworkPrefix: string,\n): string | undefined {\n if (pathname.startsWith(frameworkPrefix)) return frameworkPrefix;\n\n if (\n pathname === MCP_PUBLIC_ROUTE_PREFIX ||\n pathname.startsWith(`${MCP_PUBLIC_ROUTE_PREFIX}/`)\n ) {\n return MCP_PUBLIC_ROUTE_PREFIX;\n }\n\n const basePath = getConfiguredAppBasePath();\n const basePathFrameworkPrefix = `${basePath}${frameworkPrefix}`;\n if (basePath && pathname.startsWith(basePathFrameworkPrefix)) {\n return basePathFrameworkPrefix;\n }\n\n const basePathMcpPrefix = `${basePath}${MCP_PUBLIC_ROUTE_PREFIX}`;\n if (\n basePath &&\n (pathname === basePathMcpPrefix ||\n pathname.startsWith(`${basePathMcpPrefix}/`))\n ) {\n return basePathMcpPrefix;\n }\n\n return undefined;\n}\n\n/**\n * Create the framework CSRF middleware.\n *\n * Mount this BEFORE any state-changing route handler. The middleware\n * - lets every non-state-changing method through (GET/HEAD/OPTIONS).\n * - lets requests without cookies through (anonymous/server tools).\n * - lets allowlisted paths through (webhooks, A2A, OAuth callbacks).\n * - lets first-party-shaped requests through (custom header, JSON\n * Content-Type, or `Sec-Fetch-Site: same-origin`).\n * - rejects everything else with 403.\n */\nexport function createCsrfMiddleware(\n frameworkPrefix: string = \"/_agent-native\",\n) {\n return defineEventHandler((event) => {\n const method = getMethod(event);\n if (!STATE_CHANGING_METHODS.has(method)) return undefined;\n\n const pathname = event.url?.pathname ?? \"\";\n const matchingPrefix = matchingFrameworkPrefix(pathname, frameworkPrefix);\n if (!matchingPrefix) return undefined;\n if (isOnAllowlist(pathname, matchingPrefix)) return undefined;\n\n // No cookie = no risk of confused-deputy CSRF on the session cookie.\n if (!requestHasCookies(event)) return undefined;\n\n if (looksFirstParty(event)) return undefined;\n\n setResponseStatus(event, 403);\n return {\n error:\n \"CSRF check failed: state-changing requests must include a same-origin marker. Set Content-Type: application/json or X-Agent-Native-CSRF: 1.\",\n };\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"csrf.js","sourceRoot":"","sources":["../../src/server/csrf.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Defense-in-depth CSRF check for framework state-changing routes.\n *\n * Threat model: action endpoints (`/_agent-native/actions/*`), extension\n * endpoints (`/_agent-native/extensions/*` and the legacy\n * `/_agent-native/tools/*` alias), and a handful of other state-changing\n * `/_agent-native/*` routes use the better-auth session cookie, which is\n * configured with `SameSite=None; Secure; Partitioned` so the iframe editor\n * (and other cross-site embeds) can authenticate. `SameSite=None` means the\n * browser ships the session cookie on top-level form POSTs from any origin —\n * which is exactly the precondition for classic cross-site request forgery.\n *\n * The browser still gates \"non-simple\" requests behind a CORS preflight, so\n * an attacker who has to send `Content-Type: application/json` is forced\n * through OPTIONS, which our CORS middleware (`create-server.ts`) rejects\n * for disallowed origins. But the simple-request bypass (`Content-Type:\n * text/plain` on a `<form enctype=\"text/plain\">` POST, or `multipart/form-data`)\n * never preflights — the browser delivers it cross-origin with cookies.\n *\n * Mitigation: this middleware rejects any state-changing\n * (`POST/PUT/PATCH/DELETE`) request to `/_agent-native/*` that\n *\n * 1. carries the auth-cookie pattern (any cookie at all is a heuristic\n * good-enough proxy — we don't want to deny anonymous fetches), AND\n * 2. is NOT clearly same-origin / first-party. We trust:\n * - `Sec-Fetch-Site: same-origin` (sent by every modern browser on\n * same-origin fetch — Chrome/Firefox/Safari/Edge all support it).\n * - `X-Agent-Native-CSRF` custom header. Custom headers force a\n * preflight, so an attacker can't add one cross-origin.\n * - `Content-Type: application/json` request body. Same logic — JSON\n * Content-Type is a non-simple request that triggers preflight.\n *\n * Why the existing CORS check isn't enough: a simple-request POST never\n * preflights, so the browser sends it through and only blocks the *response*\n * from being readable cross-origin. The state change (delete-account, write\n * SQL, etc.) happens server-side regardless. We need a server-side check that\n * proves first-party intent before running the action.\n *\n * Opt-out marker: a handful of routes legitimately accept cross-origin POSTs\n * — webhook endpoints (Slack, Telegram, email), the public A2A endpoint\n * (`/_agent-native/a2a`), the integrations process-task self-fire, and so on.\n * Those are listed in `CSRF_ALLOWLIST_PREFIXES` below; if you add a new\n * cross-origin-callable route, add it there.\n */\n\nimport {\n defineEventHandler,\n getMethod,\n getRequestHeader,\n setResponseStatus,\n} from \"h3\";\n\nimport { MCP_PUBLIC_ROUTE_PREFIX } from \"../mcp/route-paths.js\";\nimport { getConfiguredAppBasePath } from \"./app-base-path.js\";\n\n/**\n * Path prefixes (relative to the framework prefix `/_agent-native`) that are\n * allowed to receive cross-origin state-changing POSTs without first-party\n * markers. These are signed/authenticated through other mechanisms (HMAC,\n * JWT, internal token) so they don't need cookie-based CSRF protection.\n */\n/**\n * Sub-prefixes that must stay CSRF-protected even though a broader entry in\n * `CSRF_ALLOWLIST_PREFIXES` covers them. Checked first, and they win.\n *\n * An allowlist entry is a promise that everything beneath it authenticates on\n * something the browser will not attach by itself. Mount a route that calls\n * `requireSessionContext` under such a prefix and that promise silently breaks:\n * the route now rides the ambient `SameSite=None` session cookie with no\n * first-party check, and nothing about the route's own code looks wrong.\n * `/integrations/remote/*` is exactly that shape — device-token relay routes\n * and cookie-authenticated control routes share one subtree.\n */\nconst CSRF_PROTECTED_PREFIXES = [\n // Remote-device relay. The device's own routes (poll/result/heartbeat)\n // authenticate with a bearer token and send no cookies, so they never reach\n // the check; the sibling routes (register, enqueue, computer/approvals,\n // computer/commands) are session-authenticated and must not be exempt —\n // approving a browser-control operation is a state change an attacker page\n // must never be able to make ride the victim's cookie.\n \"/integrations/remote/\",\n];\n\nconst CSRF_ALLOWLIST_PREFIXES = [\n // Integration webhooks — verified by HMAC against a per-integration secret.\n \"/integrations/\",\n // Agent Teams durable sub-agent processor self-fire — verified by the same\n // HMAC internal-token scheme as the integration/A2A processors.\n \"/agent-teams/\",\n // Durable sandbox-execution processor self-fire (run-code background\n // queue) — verified by the same HMAC internal-token scheme.\n \"/sandbox/_process-execution\",\n // A2A JSON-RPC endpoints — verified by signed JWT (when A2A_SECRET set) or\n // explicitly opt-in unauthenticated (handled at the A2A layer).\n \"/a2a\",\n // Better Auth's own login/sign-in/social-callback routes. Better Auth\n // ships its own CSRF protection (Origin/Sec-Fetch checks on its handlers)\n // and cookies are needed for the OAuth callback round-trip.\n \"/auth/\",\n // Stripe / Paddle / billing webhooks dropped in by templates.\n \"/billing/webhook\",\n // Public share endpoints — read-only and never cookie-driven, but kept\n // here so a templated POST (e.g. comment-on-public-recording) doesn't 403.\n \"/share/\",\n // OAuth callbacks (Builder, Google, Slack, Notion, Zoom). These get a\n // `code` query param via top-level navigation — they DO ride the session\n // cookie and they SHOULD validate state, but the framework can't see the\n // state token. Each callback handler is responsible for its own CSRF\n // check (signed state tokens).\n \"/oauth/\",\n // Builder's CLI-auth callback — uses the BUILDER_STATE_PARAM signed token\n // to authenticate the round-trip; framework CSRF check would block it.\n \"/builder/callback\",\n];\n\nconst STATE_CHANGING_METHODS = new Set([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\n/**\n * Decide whether a request is \"first-party enough\" to trust as not-CSRF.\n * Any of the following make a request non-CSRF:\n *\n * - `Sec-Fetch-Site: same-origin` (or `none` for top-level navigations\n * to our own pages — but state-changing methods don't ship `none`).\n * - `X-Agent-Native-CSRF` header (any value, even \"1\"). This is a custom\n * header so the browser forces a preflight cross-origin, which our\n * CORS layer rejects for disallowed origins.\n * - `Content-Type: application/json` (case-insensitive). JSON content\n * type is a non-simple request that triggers preflight.\n *\n * We accept ANY of these — the goal is \"did the request come through a\n * channel the browser would have preflighted\", not a strict-mode token.\n *\n * NOTE: `Sec-Fetch-Site: same-site` is deliberately NOT trusted. Under a\n * shared cookie domain (COOKIE_DOMAIN / crossSubDomainCookies), the browser\n * labels a request from a SIBLING subdomain (evil.example.com → app.example.com)\n * as `same-site` even though it is cross-origin and would ride the shared\n * session cookie — a CSRF vector. Legitimate first-party clients all also send\n * `X-Agent-Native-CSRF` or `application/json`, so they still pass via those\n * paths and iframe/embed flows are unaffected.\n */\nfunction looksFirstParty(event: any): boolean {\n const sfs = getRequestHeader(event, \"sec-fetch-site\");\n if (sfs === \"same-origin\" || sfs === \"none\") {\n return true;\n }\n if (getRequestHeader(event, \"x-agent-native-csrf\")) {\n return true;\n }\n const contentType = getRequestHeader(event, \"content-type\");\n if (\n contentType &&\n typeof contentType === \"string\" &&\n contentType.toLowerCase().includes(\"application/json\")\n ) {\n return true;\n }\n return false;\n}\n\n/**\n * Returns true when the request carries any cookie. We use \"has any cookie\"\n * as a coarse heuristic for \"the browser is going to attach the session\n * cookie\" — anonymous tools (curl, server-to-server) typically don't send\n * cookies, so they bypass this check entirely.\n */\nfunction requestHasCookies(event: any): boolean {\n const cookie = getRequestHeader(event, \"cookie\");\n return typeof cookie === \"string\" && cookie.trim().length > 0;\n}\n\n/**\n * The path is the full request URL pathname (e.g.\n * `/_agent-native/actions/foo` or `/app/_agent-native/actions/foo`).\n * `frameworkPrefix` is the root framework route prefix without a trailing\n * slash, e.g. `/_agent-native`.\n */\nfunction isOnAllowlist(pathname: string, frameworkPrefix: string): boolean {\n if (!pathname.startsWith(frameworkPrefix)) return false;\n const sub = pathname.slice(frameworkPrefix.length);\n for (const protectedPrefix of CSRF_PROTECTED_PREFIXES) {\n if (sub.startsWith(protectedPrefix)) return false;\n }\n for (const allowed of CSRF_ALLOWLIST_PREFIXES) {\n if (sub.startsWith(allowed)) return true;\n }\n return false;\n}\n\nfunction matchingFrameworkPrefix(\n pathname: string,\n frameworkPrefix: string,\n): string | undefined {\n if (pathname.startsWith(frameworkPrefix)) return frameworkPrefix;\n\n if (\n pathname === MCP_PUBLIC_ROUTE_PREFIX ||\n pathname.startsWith(`${MCP_PUBLIC_ROUTE_PREFIX}/`)\n ) {\n return MCP_PUBLIC_ROUTE_PREFIX;\n }\n\n const basePath = getConfiguredAppBasePath();\n const basePathFrameworkPrefix = `${basePath}${frameworkPrefix}`;\n if (basePath && pathname.startsWith(basePathFrameworkPrefix)) {\n return basePathFrameworkPrefix;\n }\n\n const basePathMcpPrefix = `${basePath}${MCP_PUBLIC_ROUTE_PREFIX}`;\n if (\n basePath &&\n (pathname === basePathMcpPrefix ||\n pathname.startsWith(`${basePathMcpPrefix}/`))\n ) {\n return basePathMcpPrefix;\n }\n\n return undefined;\n}\n\n/**\n * Create the framework CSRF middleware.\n *\n * Mount this BEFORE any state-changing route handler. The middleware\n * - lets every non-state-changing method through (GET/HEAD/OPTIONS).\n * - lets requests without cookies through (anonymous/server tools).\n * - lets allowlisted paths through (webhooks, A2A, OAuth callbacks).\n * - lets first-party-shaped requests through (custom header, JSON\n * Content-Type, or `Sec-Fetch-Site: same-origin`).\n * - rejects everything else with 403.\n */\nexport function createCsrfMiddleware(\n frameworkPrefix: string = \"/_agent-native\",\n) {\n return defineEventHandler((event) => {\n const method = getMethod(event);\n if (!STATE_CHANGING_METHODS.has(method)) return undefined;\n\n const pathname = event.url?.pathname ?? \"\";\n const matchingPrefix = matchingFrameworkPrefix(pathname, frameworkPrefix);\n if (!matchingPrefix) return undefined;\n if (isOnAllowlist(pathname, matchingPrefix)) return undefined;\n\n // No cookie = no risk of confused-deputy CSRF on the session cookie.\n if (!requestHasCookies(event)) return undefined;\n\n if (looksFirstParty(event)) return undefined;\n\n setResponseStatus(event, 403);\n return {\n error:\n \"CSRF check failed: state-changing requests must include a same-origin marker. Set Content-Type: application/json or X-Agent-Native-CSRF: 1.\",\n };\n });\n}\n"]}
|
|
@@ -100,6 +100,7 @@ export interface OAuthStatePayload {
|
|
|
100
100
|
returnUrl?: string;
|
|
101
101
|
flowId?: string;
|
|
102
102
|
signupAttribution?: Record<string, string | undefined>;
|
|
103
|
+
signupAnonymousId?: string;
|
|
103
104
|
}
|
|
104
105
|
/**
|
|
105
106
|
* Options for the named-argument form of {@link encodeOAuthState}.
|
|
@@ -117,6 +118,7 @@ export interface EncodeOAuthStateOptions {
|
|
|
117
118
|
returnUrl?: string;
|
|
118
119
|
flowId?: string;
|
|
119
120
|
signupAttribution?: Record<string, string | undefined>;
|
|
121
|
+
signupAnonymousId?: string;
|
|
120
122
|
}
|
|
121
123
|
/**
|
|
122
124
|
* Encode OAuth state into a signed base64url string.
|
|
@@ -168,6 +170,7 @@ export declare function createOAuthSession(event: H3Event, email: string, opts:
|
|
|
168
170
|
authUserId?: string;
|
|
169
171
|
name?: string | null;
|
|
170
172
|
attribution?: Record<string, string | undefined>;
|
|
173
|
+
signupAnonymousId?: string;
|
|
171
174
|
};
|
|
172
175
|
}): Promise<OAuthSessionResult>;
|
|
173
176
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-oauth.d.ts","sourceRoot":"","sources":["../../src/server/google-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAKL,KAAK,OAAO,EACb,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"google-oauth.d.ts","sourceRoot":"","sources":["../../src/server/google-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import crypto from "node:crypto";
|
|
10
10
|
import { getHeader, getQuery, setResponseStatus, setResponseHeader, } from "h3";
|
|
11
|
+
import { normalizeAnalyticsAnonymousId } from "../shared/analytics-anonymous-id.js";
|
|
11
12
|
import { getAppBasePathFromViteEnv } from "./app-base-path.js";
|
|
12
13
|
import { getAppName } from "./app-name.js";
|
|
13
|
-
import { signupAttributionFromCookieHeader } from "./attribution.js";
|
|
14
|
+
import { readAnalyticsAnonymousId, signupAttributionFromCookieHeader, } from "./attribution.js";
|
|
14
15
|
import { addSession, getSession, getSessionMaxAge, hasLegacySessionForEmail, setFrameworkSessionCookie, } from "./auth.js";
|
|
15
16
|
import { hasBetterAuthUserEmail, trackSignupEvent, } from "./better-auth-instance.js";
|
|
16
17
|
import { getWorkspaceA2ADerivedSecret } from "./derived-secret.js";
|
|
@@ -426,6 +427,9 @@ function sanitizeStateAttribution(value) {
|
|
|
426
427
|
}
|
|
427
428
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
428
429
|
}
|
|
430
|
+
function sanitizeStateAnonymousId(value) {
|
|
431
|
+
return normalizeAnalyticsAnonymousId(value);
|
|
432
|
+
}
|
|
429
433
|
export function encodeOAuthState(redirectUriOrOpts, owner, desktop, addAccount, app, returnUrl, flowId) {
|
|
430
434
|
const opts = typeof redirectUriOrOpts === "string"
|
|
431
435
|
? {
|
|
@@ -459,6 +463,9 @@ export function encodeOAuthState(redirectUriOrOpts, owner, desktop, addAccount,
|
|
|
459
463
|
payload.f = opts.flowId;
|
|
460
464
|
if (opts.signupAttribution)
|
|
461
465
|
payload.ft = opts.signupAttribution;
|
|
466
|
+
const signupAnonymousId = normalizeAnalyticsAnonymousId(opts.signupAnonymousId);
|
|
467
|
+
if (signupAnonymousId)
|
|
468
|
+
payload.ai = signupAnonymousId;
|
|
462
469
|
const data = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
463
470
|
const sig = crypto
|
|
464
471
|
.createHmac("sha256", getStateSigningKey())
|
|
@@ -502,6 +509,7 @@ export function decodeOAuthState(stateParam, fallbackUri) {
|
|
|
502
509
|
returnUrl: typeof parsed.r2 === "string" ? parsed.r2 : undefined,
|
|
503
510
|
flowId: parsed.f || undefined,
|
|
504
511
|
signupAttribution: sanitizeStateAttribution(parsed.ft),
|
|
512
|
+
signupAnonymousId: sanitizeStateAnonymousId(parsed.ai),
|
|
505
513
|
};
|
|
506
514
|
}
|
|
507
515
|
catch { }
|
|
@@ -548,12 +556,15 @@ export async function createOAuthSession(event, email, opts) {
|
|
|
548
556
|
if (shouldTrackSignup && opts.trackSignup) {
|
|
549
557
|
const attribution = opts.trackSignup.attribution ??
|
|
550
558
|
signupAttributionFromCookieHeader(getHeader(event, "cookie") ?? null);
|
|
559
|
+
const anonymousId = opts.trackSignup.signupAnonymousId ??
|
|
560
|
+
readAnalyticsAnonymousId(getHeader(event, "cookie") ?? null);
|
|
551
561
|
await trackSignupEvent({
|
|
552
562
|
authProvider: opts.trackSignup.authProvider,
|
|
553
563
|
authUserId: opts.trackSignup.authUserId,
|
|
554
564
|
email,
|
|
555
565
|
name: opts.trackSignup.name,
|
|
556
566
|
attribution,
|
|
567
|
+
anonymousId,
|
|
557
568
|
});
|
|
558
569
|
}
|
|
559
570
|
// Desktop SSO: record this session in the home-dir broker file so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-oauth.js","sourceRoot":"","sources":["../../src/server/google-oauth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared Google OAuth utilities for all templates.\n *\n * Handles platform detection (desktop/mobile), state encoding,\n * session token creation, and deep-link responses — the logic\n * that was previously copy-pasted across every template's\n * google-auth.ts handler.\n */\n\nimport crypto from \"node:crypto\";\n\nimport {\n getHeader,\n getQuery,\n setResponseStatus,\n setResponseHeader,\n type H3Event,\n} from \"h3\";\n\nimport { getAppBasePathFromViteEnv } from \"./app-base-path.js\";\nimport { getAppName } from \"./app-name.js\";\nimport { signupAttributionFromCookieHeader } from \"./attribution.js\";\nimport {\n addSession,\n getSession,\n getSessionMaxAge,\n hasLegacySessionForEmail,\n setFrameworkSessionCookie,\n} from \"./auth.js\";\nimport {\n hasBetterAuthUserEmail,\n trackSignupEvent,\n} from \"./better-auth-instance.js\";\nimport { getWorkspaceA2ADerivedSecret } from \"./derived-secret.js\";\nimport { writeDesktopSso } from \"./desktop-sso.js\";\nimport { appendSessionToOAuthReturnUrl } from \"./oauth-return-url.js\";\nimport { isWorkspaceOAuthCallbackRelayEnabled } from \"./workspace-oauth.js\";\n\n// ─── Platform Detection ─────────────────────────────────────────────────────\n\n/** Return an HTML response with the correct Content-Type.\n * Uses a web-standard Response to ensure the header survives\n * Nitro dev mode's mock-node-response pipeline. */\nfunction htmlResponse(html: string, status = 200): Response {\n return new Response(html, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\n/** Shared markup for OAuth success \"close this tab\" pages. Renders a green\n * check icon above the message, with a little breathing room between the\n * headline and secondary line. Used by every template that goes through the\n * shared Google OAuth flow. */\nfunction oauthDebugFlowId(flowId?: string): string | undefined {\n return flowId ? flowId.slice(-10) : undefined;\n}\n\nfunction oauthSuccessCloseTabHtml(\n headline: string,\n footnote: string,\n debugFlowId?: string,\n): string {\n const debug = debugFlowId\n ? `<p style=\"font-size:11px;color:#555;margin:12px 0 0 0\">Debug flow: ${escapeHtml(debugFlowId)}</p>`\n : \"\";\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column\"><svg width=\"44\" height=\"44\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#22c55e\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-bottom:14px\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M9 12l2 2l4 -4\"/></svg><p style=\"font-size:16px;margin:0 0 12px 0\">${headline}</p><p style=\"font-size:13px;color:#888;margin:0\">${footnote}</p>${debug}<script>console.info(\"[agent-native][google-oauth] success page loaded\",{flow:${JSON.stringify(debugFlowId || null)}});setTimeout(function(){try{window.close()}catch(e){}},250)</script></body></html>`;\n}\n\n/**\n * HTML escape — minimal but covers the cases that matter when interpolating\n * user-controlled values into our OAuth callback HTML. Mirrors the helper in\n * email-template.ts; kept inline here to avoid a circular import.\n */\nfunction escapeHtml(s: string): string {\n return String(s ?? \"\")\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n/**\n * Detect requests from the Agent Native desktop app specifically.\n *\n * The desktop app appends `AgentNativeDesktop/<version>` to its user-agent\n * (see `packages/desktop-app/src/main/index.ts`). We check for that marker\n * rather than matching generic `Electron`, which would also match other\n * Electron-based webviews like Builder.io's Fusion, Slack desktop, Discord,\n * etc. Falsely treating those as \"the desktop app\" sends users to the\n * `agentnative://oauth-complete` deep-link success page after Google sign-in,\n * where the protocol handler can't fire and the \"Open Agent Native\" button\n * does nothing.\n *\n * Kept exported as `isElectron` for backwards compatibility with consumers.\n */\nexport function isElectron(event: H3Event): boolean {\n return /AgentNativeDesktop/i.test(getHeader(event, \"user-agent\") || \"\");\n}\n\n/** Detect requests from a mobile browser (iOS/Android). */\nexport function isMobile(event: H3Event): boolean {\n return /iPhone|iPad|iPod|Android/i.test(getHeader(event, \"user-agent\") || \"\");\n}\n\n/**\n * Build the static allowlist of origins we trust for `getOrigin`. Reads\n * deployment-known public URLs. Each entry is normalised to\n * `${proto}://${host}` (no path). Duplicates collapse, invalid entries are\n * dropped silently.\n */\nconst EXPLICIT_PUBLIC_ORIGIN_ENV_KEYS = [\n \"WORKSPACE_OAUTH_ORIGIN\",\n \"VITE_WORKSPACE_OAUTH_ORIGIN\",\n \"APP_URL\",\n \"VITE_APP_URL\",\n \"BETTER_AUTH_URL\",\n \"VITE_BETTER_AUTH_URL\",\n \"URL\",\n \"DEPLOY_URL\",\n] as const;\n\nconst WORKSPACE_GATEWAY_ORIGIN_ENV_KEYS = [\n \"WORKSPACE_GATEWAY_URL\",\n \"VITE_WORKSPACE_GATEWAY_URL\",\n] as const;\n\nfunction normalizeOrigin(raw: string | undefined): string | undefined {\n if (!raw) return undefined;\n try {\n const u = new URL(raw);\n return `${u.protocol}//${u.host}`;\n } catch {\n return undefined;\n }\n}\n\nfunction addNormalizedOrigin(\n out: Set<string>,\n raw: string | undefined,\n options: { allowLoopback: boolean },\n): void {\n const origin = normalizeOrigin(raw);\n if (!origin) return;\n if (!options.allowLoopback && isLoopbackOrigin(origin)) return;\n out.add(origin);\n}\n\nfunction firstOriginFromEnv(\n keys: readonly string[],\n options: { allowLoopback: boolean },\n): string | undefined {\n for (const key of keys) {\n const origin = normalizeOrigin(process.env[key]);\n if (!origin) continue;\n if (!options.allowLoopback && isLoopbackOrigin(origin)) continue;\n return origin;\n }\n return undefined;\n}\n\nfunction getConfiguredOriginAllowlist(): Set<string> {\n const out = new Set<string>();\n for (const key of EXPLICIT_PUBLIC_ORIGIN_ENV_KEYS) {\n addNormalizedOrigin(out, process.env[key], { allowLoopback: true });\n }\n for (const key of WORKSPACE_GATEWAY_ORIGIN_ENV_KEYS) {\n addNormalizedOrigin(out, process.env[key], { allowLoopback: false });\n }\n return out;\n}\n\n/** Return whether a candidate is one of this deployment's configured origins. */\nexport function isConfiguredAppOrigin(value: string | undefined): boolean {\n const origin = normalizeOrigin(value);\n return !!origin && getConfiguredOriginAllowlist().has(origin);\n}\n\nfunction getWorkspaceCallbackOrigin(): string | undefined {\n const publicAuthOrigin = firstOriginFromEnv(EXPLICIT_PUBLIC_ORIGIN_ENV_KEYS, {\n allowLoopback: true,\n });\n if (publicAuthOrigin) return publicAuthOrigin;\n\n return firstOriginFromEnv(WORKSPACE_GATEWAY_ORIGIN_ENV_KEYS, {\n allowLoopback: false,\n });\n}\n\nfunction isLoopbackHost(host: string | undefined): boolean {\n if (!host) return false;\n try {\n const parsed = new URL(`http://${host}`);\n return (\n parsed.hostname === \"localhost\" ||\n parsed.hostname === \"127.0.0.1\" ||\n parsed.hostname === \"::1\" ||\n parsed.hostname === \"[::1]\"\n );\n } catch {\n return false;\n }\n}\n\nfunction isLoopbackOrigin(origin: string | undefined): boolean {\n if (!origin) return false;\n try {\n return isLoopbackHost(new URL(origin).host);\n } catch {\n return false;\n }\n}\n\nfunction isBuilderPreviewHost(host: string | undefined): boolean {\n if (!host) return false;\n try {\n const parsed = new URL(`http://${host}`);\n const hostname = parsed.hostname.toLowerCase();\n return (\n hostname === \"builderio.xyz\" ||\n hostname.endsWith(\".builderio.xyz\") ||\n hostname === \"builderio.dev\" ||\n hostname.endsWith(\".builderio.dev\") ||\n hostname === \"builder.codes\" ||\n hostname.endsWith(\".builder.codes\") ||\n hostname === \"builder.io\" ||\n hostname.endsWith(\".builder.io\") ||\n hostname === \"builder.my\" ||\n hostname.endsWith(\".builder.my\")\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Get the origin from forwarded headers or Host.\n *\n * Defends against Host-header injection: in production we require the resolved\n * origin to match `APP_URL` / `BETTER_AUTH_URL` / `WORKSPACE_GATEWAY_URL`,\n * falling back to those values when inbound headers are missing or don't match.\n * In dev we accept inbound `Host` so localhost / ngrok / preview hosts keep\n * working without configuration, except workspace OAuth requests from loopback\n * or Builder preview hosts use the configured gateway origin when one exists.\n * The protocol defaults to `https` in production (so a TLS-terminating proxy\n * that drops `x-forwarded-proto` doesn't downgrade us to plain HTTP).\n */\nexport function getOrigin(event: H3Event): string {\n const headerHost =\n getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const isProd = process.env.NODE_ENV === \"production\";\n const headerProto =\n getHeader(event, \"x-forwarded-proto\") || (isProd ? \"https\" : \"http\");\n const workspaceCallbackOrigin = isWorkspaceOAuthCallbackRelayEnabled()\n ? getWorkspaceCallbackOrigin()\n : undefined;\n\n if (\n workspaceCallbackOrigin &&\n (isLoopbackHost(headerHost) || isBuilderPreviewHost(headerHost))\n ) {\n return workspaceCallbackOrigin;\n }\n\n if (isProd) {\n const allow = getConfiguredOriginAllowlist();\n // If the deploy declares its public URL, prefer it over inbound headers.\n if (allow.size > 0) {\n const inbound = headerHost ? `${headerProto}://${headerHost}` : \"\";\n if (inbound && allow.has(inbound)) return inbound;\n // Inbound didn't match — fall back to the first configured origin.\n return [...allow][0];\n }\n // No allowlist configured: still default to https, but accept the\n // inbound Host (best we can do without a configured base URL).\n return `${headerProto}://${headerHost ?? \"\"}`;\n }\n\n return `${headerProto}://${headerHost ?? \"localhost\"}`;\n}\n\n/** App mount prefix, if the template is served under APP_BASE_PATH. */\nexport function getAppBasePath(): string {\n // Vite statically replaces VITE_* values in the server bundle during the\n // build, but Netlify/Nitro does not necessarily expose those build vars at\n // runtime. Keep auth and OAuth path matching aligned with the SSR handler by\n // falling back to import.meta.env (including BASE_URL).\n return getAppBasePathFromViteEnv();\n}\n\n/** Build an absolute same-origin URL that preserves APP_BASE_PATH. */\nexport function getAppUrl(event: H3Event, path = \"/\"): string {\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return `${getOrigin(event)}${getAppBasePath()}${cleanPath}`;\n}\n\nfunction isFrameworkOAuthCallbackPath(pathname: string): boolean {\n return (\n pathname.startsWith(\"/_agent-native/\") &&\n (pathname.endsWith(\"/callback\") || pathname.includes(\"/callback/\"))\n );\n}\n\nfunction getOriginalRequestPath(event: H3Event): string {\n const mountedPathname = (event as any).context?._mountedPathname;\n if (typeof mountedPathname === \"string\" && mountedPathname) {\n return mountedPathname;\n }\n\n const urlPathname = (event as any).url?.pathname;\n if (typeof urlPathname === \"string\" && urlPathname) return urlPathname;\n\n const nodeUrl = event.node?.req?.url;\n if (typeof nodeUrl === \"string\" && nodeUrl) {\n const queryStart = nodeUrl.indexOf(\"?\");\n return queryStart >= 0 ? nodeUrl.slice(0, queryStart) : nodeUrl;\n }\n\n const eventPath = (event as any).path;\n if (typeof eventPath === \"string\" && eventPath) {\n const queryStart = eventPath.indexOf(\"?\");\n return queryStart >= 0 ? eventPath.slice(0, queryStart) : eventPath;\n }\n\n return \"/\";\n}\n\nfunction isRequestUnderAppBasePath(event: H3Event): boolean {\n const basePath = getAppBasePath();\n if (!basePath) return false;\n const requestPath = getOriginalRequestPath(event);\n return (\n requestPath === `${basePath}/_agent-native` ||\n requestPath.startsWith(`${basePath}/_agent-native/`)\n );\n}\n\nfunction getDefaultOAuthRedirectUrl(event: H3Event, path: string): string {\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n if (\n isWorkspaceOAuthCallbackRelayEnabled() &&\n isFrameworkOAuthCallbackPath(cleanPath)\n ) {\n return `${getOrigin(event)}${cleanPath}`;\n }\n const basePath = isRequestUnderAppBasePath(event) ? getAppBasePath() : \"\";\n return `${getOrigin(event)}${basePath}${cleanPath}`;\n}\n\n// ─── redirect_uri Allowlist ──────────────────────────────────────────────────\n\n/**\n * Validate a user-supplied `redirect_uri` for OAuth flows.\n *\n * Defends against authorization-code interception (RFC 6819 §4.4.1.7):\n * even though the upstream provider (Google/Atlassian/Zoom) refuses\n * unregistered redirect URIs, prefix-style registrations and side\n * registrations on the same host let a malicious caller swap in an\n * attacker-controlled URI that the provider still accepts. We reject any\n * candidate that isn't on this server's own origin AND under the\n * framework's `/_agent-native/` namespace. Returns the validated URI on\n * success, or `undefined` on rejection — callers must treat `undefined`\n * as a 400.\n *\n * The intentional shape is exact-prefix:\n * - Origin must equal `getOrigin(event)` — no Host-header injection\n * reusing somebody else's registered redirect URI.\n * - Path must start with `${appBasePath}/_agent-native/` so we never\n * hand auth codes to a public marketing or open-redirect endpoint\n * on the same registered host.\n *\n * For desktop / native flows that need ephemeral `http://127.0.0.1:<port>`\n * loopback URIs, callers should validate those at the template level\n * with a dedicated allowlist — this helper rejects them by design.\n */\nexport function isAllowedOAuthRedirectUri(\n candidate: string,\n event: H3Event,\n): boolean {\n if (typeof candidate !== \"string\" || candidate.length === 0) return false;\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n return false;\n }\n // Must be same origin as our server.\n const expectedOrigin = getOrigin(event);\n let expectedUrl: URL;\n try {\n expectedUrl = new URL(expectedOrigin);\n } catch {\n return false;\n }\n if (url.protocol !== expectedUrl.protocol) return false;\n if (url.host !== expectedUrl.host) return false;\n // Must live under the framework's namespace. Workspace deploys can route\n // root /_agent-native/* to Dispatch even when Dispatch itself is mounted at\n // /dispatch, but app-prefixed requests should not be able to swap their\n // callback to that root namespace.\n const basePath = getAppBasePath();\n const allowedPrefixes =\n basePath && isRequestUnderAppBasePath(event)\n ? [\n `${basePath}/_agent-native/`,\n ...(isWorkspaceOAuthCallbackRelayEnabled() &&\n isFrameworkOAuthCallbackPath(url.pathname)\n ? [\"/_agent-native/\"]\n : []),\n ]\n : [\"/_agent-native/\"];\n if (!allowedPrefixes.some((prefix) => url.pathname.startsWith(prefix))) {\n return false;\n }\n return true;\n}\n\n/**\n * Resolve the `redirect_uri` for an outbound OAuth `auth-url` request.\n *\n * Reads `?redirect_uri=` from the query and validates it via\n * `isAllowedOAuthRedirectUri`. Returns:\n * - the validated URI when supplied and allowed, OR\n * - the framework default when no override was supplied, OR\n * - `null` when an override was supplied but rejected — callers must\n * respond with 400 in that case.\n *\n * Templates that need a non-default redirect path can pass it via\n * `defaultPath` (e.g. `\"/_agent-native/google/desktop-callback\"` for\n * desktop flows).\n */\nexport function resolveOAuthRedirectUri(\n event: H3Event,\n defaultPath = \"/_agent-native/google/callback\",\n): string | null {\n const supplied = getQuery(event).redirect_uri;\n if (typeof supplied === \"string\" && supplied.length > 0) {\n return isAllowedOAuthRedirectUri(supplied, event) ? supplied : null;\n }\n return getDefaultOAuthRedirectUrl(event, defaultPath);\n}\n\n// ─── OAuth State ─────────────────────────────────────────────────────────────\n\nexport interface OAuthStatePayload {\n redirectUri: string;\n owner?: string;\n orgId?: string;\n desktop?: boolean;\n addAccount?: boolean;\n app?: string;\n /**\n * Same-origin path to redirect to after a successful web-flow sign-in.\n * Threaded through the (HMAC-signed) state so it survives the round trip\n * to Google. Validated again on decode via safeReturnPath as defence in\n * depth. Has no effect on desktop / mobile / add-account flows, which\n * use their own deep-link / close-tab handling.\n */\n returnUrl?: string;\n flowId?: string;\n signupAttribution?: Record<string, string | undefined>;\n}\n\n/**\n * Ephemeral in-memory state-signing key for development. Generated lazily\n * on first read so dev sessions don't depend on filesystem writability or\n * env-var configuration. Sessions reset on each restart, which is fine\n * for dev — no real users / production data are involved.\n */\nlet _devStateSigningKey: string | undefined;\n\n/**\n * Derive a server-only signing key for HMAC verification of OAuth state.\n *\n * Uses a dedicated secret — never an OAuth client secret. Reusing a\n * client_secret (which is shared with Google / GitHub / Atlassian) as our\n * own HMAC key conflates two trust domains: rotating the client secret\n * silently invalidates every in-flight OAuth state, and any leak of the\n * client secret also lets an attacker forge our state envelopes.\n *\n * Resolution order:\n * 1. OAUTH_STATE_SECRET (preferred — dedicated to this purpose)\n * 2. BETTER_AUTH_SECRET (already used by Better Auth as a server secret)\n * 3. Hosted workspace deploys derive a per-purpose key from A2A_SECRET\n * 4. In dev only, an ephemeral random key (per-process)\n *\n * In production, throws if no usable server secret is set.\n */\nfunction getStateSigningKey(): string {\n const secret =\n process.env.OAUTH_STATE_SECRET ||\n process.env.BETTER_AUTH_SECRET ||\n getWorkspaceA2ADerivedSecret(\"oauth-state\");\n if (secret) return secret;\n\n const isProd = process.env.NODE_ENV === \"production\";\n if (isProd) {\n throw new Error(\n \"OAuth state signing requires a server secret. \" +\n \"Set OAUTH_STATE_SECRET, BETTER_AUTH_SECRET, or A2A_SECRET in production workspace deploys.\",\n );\n }\n\n if (!_devStateSigningKey) {\n _devStateSigningKey = crypto.randomBytes(32).toString(\"hex\");\n }\n return _devStateSigningKey;\n}\n\n/**\n * Options for the named-argument form of {@link encodeOAuthState}.\n * Prefer this form — the positional overload is easy to misuse (the mail\n * and calendar templates historically passed `flowId` in the `returnUrl`\n * slot, smuggling state into a defence-in-depth path).\n */\nexport interface EncodeOAuthStateOptions {\n redirectUri: string;\n owner?: string;\n orgId?: string;\n desktop?: boolean;\n addAccount?: boolean;\n app?: string;\n returnUrl?: string;\n flowId?: string;\n signupAttribution?: Record<string, string | undefined>;\n}\n\nfunction sanitizeStateAttribution(\n value: unknown,\n): Record<string, string | undefined> | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return undefined;\n }\n const out: Record<string, string | undefined> = {};\n for (const [key, raw] of Object.entries(value)) {\n if (typeof raw === \"string\") out[key] = raw;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * Encode OAuth state into a signed base64url string.\n * The state is HMAC-signed so the callback can verify it wasn't forged,\n * preventing CSRF attacks on the OAuth flow.\n *\n * Two call shapes are supported:\n * - Recommended: pass an options object — clear, mismatch-proof.\n * `encodeOAuthState({ redirectUri, owner, desktop, ... })`\n * - Legacy positional form (kept working for backward compatibility):\n * `encodeOAuthState(redirectUri, owner, desktop, addAccount, app, returnUrl, flowId)`.\n * Callers should migrate to the options form — see the audit on\n * templates/mail and templates/calendar where the positional shape\n * led to `flowId` being smuggled in via the `returnUrl` slot.\n */\nexport function encodeOAuthState(opts: EncodeOAuthStateOptions): string;\nexport function encodeOAuthState(\n redirectUri: string,\n owner?: string,\n desktop?: boolean,\n addAccount?: boolean,\n app?: string,\n returnUrl?: string,\n flowId?: string,\n): string;\nexport function encodeOAuthState(\n redirectUriOrOpts: string | EncodeOAuthStateOptions,\n owner?: string,\n desktop?: boolean,\n addAccount?: boolean,\n app?: string,\n returnUrl?: string,\n flowId?: string,\n): string {\n const opts: EncodeOAuthStateOptions =\n typeof redirectUriOrOpts === \"string\"\n ? {\n redirectUri: redirectUriOrOpts,\n owner,\n desktop,\n addAccount,\n app,\n returnUrl,\n flowId,\n }\n : redirectUriOrOpts;\n\n const nonce = crypto.randomBytes(8).toString(\"hex\");\n const payload: Record<string, unknown> = {\n n: nonce,\n r: opts.redirectUri,\n };\n if (opts.owner) payload.o = opts.owner;\n if (opts.orgId) payload.g = opts.orgId;\n if (opts.desktop) payload.d = true;\n if (opts.addAccount) payload.a = true;\n if (opts.app) payload.app = opts.app;\n if (opts.returnUrl) payload.r2 = opts.returnUrl;\n if (opts.flowId) payload.f = opts.flowId;\n if (opts.signupAttribution) payload.ft = opts.signupAttribution;\n const data = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const sig = crypto\n .createHmac(\"sha256\", getStateSigningKey())\n .update(data)\n .digest(\"base64url\");\n return `${data}.${sig}`;\n}\n\n/**\n * Decode and verify OAuth state from the callback's state query parameter.\n * Rejects forged or tampered state by checking the HMAC signature.\n * Falls back to the provided URI if decoding or verification fails.\n */\nexport function decodeOAuthState(\n stateParam: string | undefined,\n fallbackUri: string,\n): OAuthStatePayload {\n if (stateParam) {\n try {\n const dotIdx = stateParam.lastIndexOf(\".\");\n if (dotIdx === -1) return { redirectUri: fallbackUri };\n\n const data = stateParam.slice(0, dotIdx);\n const sig = stateParam.slice(dotIdx + 1);\n const expected = crypto\n .createHmac(\"sha256\", getStateSigningKey())\n .update(data)\n .digest(\"base64url\");\n\n if (\n sig.length !== expected.length ||\n !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))\n ) {\n return { redirectUri: fallbackUri };\n }\n\n const parsed = JSON.parse(Buffer.from(data, \"base64url\").toString());\n return {\n redirectUri: parsed.r || fallbackUri,\n owner: parsed.o || undefined,\n orgId: typeof parsed.g === \"string\" ? parsed.g : undefined,\n desktop: !!parsed.d,\n addAccount: !!parsed.a,\n app: typeof parsed.app === \"string\" ? parsed.app : undefined,\n // Pass returnUrl through as-is — same-origin validation runs at the\n // consumer (oauthCallbackResponse → safeReturnPath). The state is\n // HMAC-signed, but we still validate at consumption as defence in\n // depth in case the signing key ever leaks.\n returnUrl: typeof parsed.r2 === \"string\" ? parsed.r2 : undefined,\n flowId: parsed.f || undefined,\n signupAttribution: sanitizeStateAttribution(parsed.ft),\n };\n } catch {}\n }\n return { redirectUri: fallbackUri };\n}\n\n// ─── Session Creation ────────────────────────────────────────────────────────\n\nexport interface OAuthOwnerResult {\n owner: string | undefined;\n hasProductionSession: boolean;\n}\n\n/**\n * Determine the token owner from the current session and OAuth state.\n * Call this BEFORE exchangeCode to get the owner parameter.\n */\nexport async function resolveOAuthOwner(\n event: H3Event,\n stateOwner?: string,\n): Promise<OAuthOwnerResult> {\n const existingSession = await getSession(event);\n const hasProductionSession = !!existingSession?.email;\n const owner = hasProductionSession\n ? existingSession!.email\n : stateOwner || undefined;\n\n return { owner, hasProductionSession };\n}\n\nexport interface OAuthSessionResult {\n sessionToken: string | undefined;\n}\n\n/**\n * Create a session token after a successful OAuth exchange.\n *\n * Desktop and mobile apps have separate cookie jars from the system\n * browser, so they always get a fresh session token (even if the browser\n * already has one). The token is then passed via deep link so the native\n * app can inject it.\n */\nexport async function createOAuthSession(\n event: H3Event,\n email: string,\n opts: {\n hasProductionSession: boolean;\n desktop?: boolean;\n trackSignup?: {\n authProvider: string;\n authUserId?: string;\n name?: string | null;\n attribution?: Record<string, string | undefined>;\n };\n },\n): Promise<OAuthSessionResult> {\n const mobile = isMobile(event);\n const needsDeepLink = opts.desktop || mobile;\n const maxAge = getSessionMaxAge();\n\n let sessionToken: string | undefined;\n let shouldTrackSignup = false;\n if (!opts.hasProductionSession || needsDeepLink) {\n if (opts.trackSignup && !opts.hasProductionSession) {\n const [hasLegacySession, hasBetterAuthUser] = await Promise.all([\n hasLegacySessionForEmail(email).catch(() => true),\n hasBetterAuthUserEmail(email).catch(() => true),\n ]);\n shouldTrackSignup = !hasLegacySession && !hasBetterAuthUser;\n }\n\n sessionToken = crypto.randomBytes(32).toString(\"hex\");\n await addSession(sessionToken, email);\n setFrameworkSessionCookie(event, sessionToken);\n if (shouldTrackSignup && opts.trackSignup) {\n const attribution =\n opts.trackSignup.attribution ??\n signupAttributionFromCookieHeader(getHeader(event, \"cookie\") ?? null);\n await trackSignupEvent({\n authProvider: opts.trackSignup.authProvider,\n authUserId: opts.trackSignup.authUserId,\n email,\n name: opts.trackSignup.name,\n attribution,\n });\n }\n // Desktop SSO: record this session in the home-dir broker file so\n // sibling templates (each with its own database) can resolve the\n // same token without a DB row of their own. Only the PRIMARY\n // sign-in writes the broker — if a production session already\n // exists, this is an add-account flow (connecting a secondary\n // Google account for scraping) and must never switch the active\n // user across sibling templates.\n if (opts.desktop && !opts.hasProductionSession) {\n await writeDesktopSso({\n email,\n token: sessionToken,\n expiresAt: Date.now() + maxAge * 1000,\n });\n }\n }\n\n return { sessionToken };\n}\n\n// ─── Callback Responses ──────────────────────────────────────────────────────\n\n/**\n * Return the appropriate response after a successful OAuth callback.\n *\n * Handles mobile deep links, desktop deep links, add-account close-tab\n * pages, and plain web redirects — so templates don't have to.\n */\nexport function oauthCallbackResponse(\n event: H3Event,\n email: string,\n opts: {\n sessionToken?: string;\n desktop?: boolean;\n addAccount?: boolean;\n /**\n * Same-origin path to return the viewer to after a successful web\n * sign-in. Validated via safeReturnPath; falls back to \"/\" for any\n * shape that escapes same-origin. Has no effect on desktop / mobile\n * / add-account flows — those use their own deep-link handling.\n */\n returnUrl?: string;\n flowId?: string;\n appName?: string;\n },\n): Response | string | unknown | Promise<Response | string | unknown> {\n const mobile = isMobile(event);\n const query = getQuery(event);\n const callbackState =\n typeof query.state === \"string\" && query.state.length > 0\n ? query.state\n : undefined;\n\n // Mobile: deep link back to the native app. `isMobile` is UA-only, so this\n // also fires for a plain mobile web browser with no app to handle the deep\n // link — there it no-ops, so the fallback must return to the post-login URL,\n // not the app root (else signed-out visitors land on the homepage).\n if (mobile) {\n const deepLink = buildOAuthCompleteDeepLink(\n opts.sessionToken,\n callbackState,\n );\n const webFallback = appendSessionToOAuthReturnUrl(\n opts.returnUrl,\n opts.sessionToken,\n );\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"><title>Connected</title></head><body style=\"background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0\"><p>Connected! Returning to app…</p><script>window.location.href=${JSON.stringify(deepLink)};setTimeout(function(){window.location.href=${JSON.stringify(webFallback)}},1500)</script></body></html>`,\n );\n }\n\n // Desktop add-account: close-tab page (must come before general desktop check\n // to ensure no deep link fires and the existing session is never switched).\n if (opts.desktop && opts.addAccount) {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const safeAppName = escapeHtml(resolveOAuthAppName(opts.appName));\n const msg = safeEmail ? `Connected ${safeEmail}!` : \"Connected!\";\n return htmlResponse(\n oauthSuccessCloseTabHtml(\n msg,\n `You can close this tab and return to ${safeAppName}.`,\n oauthDebugFlowId(opts.flowId),\n ),\n );\n }\n\n // Electron desktop exchange flow: mail/calendar still pass a flow id so the\n // renderer can poll as a fallback, but the main handoff should use the\n // protocol deep link so the popup returns focus to the desktop app.\n if (opts.desktop && opts.flowId && isElectron(event) && opts.sessionToken) {\n return desktopSuccessPage(event, email, opts.sessionToken, callbackState);\n }\n\n // Desktop exchange flow (non-Electron tray app): the tray app polls the\n // desktop-exchange endpoint for the token — no deep link needed.\n if (opts.desktop && opts.flowId) {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const safeAppName = escapeHtml(resolveOAuthAppName(opts.appName));\n const msg = safeEmail ? `Signed in as ${safeEmail}!` : \"Signed in!\";\n return htmlResponse(\n oauthSuccessCloseTabHtml(\n msg,\n `You can close this tab and return to ${safeAppName}.`,\n oauthDebugFlowId(opts.flowId),\n ),\n );\n }\n\n // Desktop login: deep link back to Electron app — only when the callback\n // request actually carries the AgentNativeDesktop UA marker. Without this\n // check, any client whose OAuth state was minted with `desktop=true` (e.g.\n // a stale link, or an upstream that wrongly set `?desktop=1`) would land\n // on the `agentnative://` page where the deep link can't fire and the\n // \"Open Agent Native\" button does nothing — surfaces inside Builder.io's\n // Fusion webview hit this exact dead-end. Fall through to the web flow\n // for non-Agent-Native-Desktop clients so they get a real redirect.\n if (opts.desktop && isElectron(event)) {\n return desktopSuccessPage(event, email, opts.sessionToken, callbackState);\n }\n\n // Add-account web flow: close-tab page. The email is rendered into the\n // page via DOM `textContent` (safe), but we still JSON-stringify so a\n // payload containing `</script>` can't break out of the script tag —\n // and explicitly assert it's a string so a callbacks like `null` or\n // an object won't end up serialised into the page.\n if (opts.addAccount) {\n const safeEmail = JSON.stringify(typeof email === \"string\" ? email : \"\");\n return htmlResponse(`<!DOCTYPE html><html><body><script>\n window.close();\n var p = document.createElement('p');\n p.style.cssText = 'font-family:system-ui;text-align:center;margin-top:40vh';\n p.textContent = 'Connected ' + ${safeEmail} + '! You can close this tab.';\n document.body.appendChild(p);\n </script></body></html>`);\n }\n\n // Web: redirect to the requested return target. Path-only returns stay\n // same-origin; Builder desktop workspace returns may point back to the\n // local loopback gateway and carry the short-lived `_session` bridge so\n // the local app can promote the newly created hosted OAuth session.\n const location = appendSessionToOAuthReturnUrl(\n opts.returnUrl,\n opts.sessionToken,\n );\n setResponseStatus(event, 302);\n setResponseHeader(event, \"Location\", location);\n setResponseHeader(event, \"Referrer-Policy\", \"no-referrer\");\n // Return a real 302 so the browser lands on the clean return URL instead of\n // lingering on the provider callback URL with its `code`/`state` query\n // params. But h3 hands a non-2xx web `Response` straight back WITHOUT merging\n // the `Set-Cookie` staged earlier in the callback (the framework session\n // cookie), so mirror those staged cookies onto the redirect Response —\n // otherwise the sign-in succeeds but the browser arrives back logged out.\n const headers = new Headers({\n Location: location,\n \"Referrer-Policy\": \"no-referrer\",\n });\n for (const cookie of event.res?.headers?.getSetCookie?.() ?? []) {\n headers.append(\"set-cookie\", cookie);\n }\n return new Response(null, { status: 302, headers });\n}\n\n/** HTML error page for OAuth failures. The message is HTML-escaped — most\n * callers pass `error.message` from a token-exchange or userinfo failure,\n * which can echo upstream provider strings (and historically attacker-\n * controlled query params via the `error_description` field). */\nexport function oauthErrorPage(message: string): Response {\n const safe = escapeHtml(message);\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connection failed</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;text-align:center\"><svg width=\"44\" height=\"44\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ef4444\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-bottom:14px\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M15 9l-6 6\"/><path d=\"M9 9l6 6\"/></svg><p style=\"font-size:16px;margin:0 0 12px 0;color:#ddd\">${safe}</p><p style=\"font-size:13px;color:#888;margin:0\"><a href=\"/\" style=\"color:#888;text-decoration:underline;text-underline-offset:3px\">Back to login</a></p></body></html>`,\n 400,\n );\n}\n\nexport function oauthDesktopExchangePage(\n message = \"Returning to the app...\",\n): Response {\n const safe = escapeHtml(message);\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Returning</title></head><body style=\"background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0\"><p style=\"font-size:14px\">${safe}</p><script>window.close()</script></body></html>`,\n );\n}\n\n// ─── Internal ────────────────────────────────────────────────────────────────\n\nfunction resolveOAuthAppName(explicit?: string): string {\n const raw = explicit || getAppName() || \"Agent Native\";\n if (!/^[a-z0-9_-]+$/.test(raw)) return raw;\n return raw\n .split(/[-_]+/)\n .filter(Boolean)\n .map((word) => word[0].toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\nfunction buildOAuthCompleteDeepLink(\n sessionToken?: string,\n state?: string,\n): string {\n const params = new URLSearchParams();\n if (sessionToken) params.set(\"token\", sessionToken);\n if (state) params.set(\"state\", state);\n const suffix = params.toString();\n return suffix\n ? `agentnative://oauth-complete?${suffix}`\n : \"agentnative://oauth-complete\";\n}\n\nfunction desktopSuccessPage(\n _event: H3Event,\n email?: string,\n sessionToken?: string,\n state?: string,\n): Response {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const msg = safeEmail ? `Connected ${safeEmail}!` : \"Connected!\";\n if (sessionToken) {\n const deepLink = buildOAuthCompleteDeepLink(sessionToken, state);\n const deepLinkJson = JSON.stringify(deepLink);\n // Defence in depth: if this page somehow gets served to a UA that isn't\n // the Agent Native desktop app (server gate bypassed, stale link, etc.),\n // skip the `agentnative://` deep link entirely and bounce to the app\n // root. The deep link silently fails outside the desktop app and the\n // \"Open Agent Native\" button is a dead end in a generic browser/webview.\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title><style>@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.spinner{width:28px;height:28px;border:2px solid #333;border-top-color:#fff;border-radius:50%;animation:spin .8s linear infinite}.fallback{display:none;flex-direction:column;align-items:center;gap:8px;animation:fadeIn .2s ease-out}.fallback.show{display:flex}</style></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:16px\"><p style=\"font-size:16px;margin:0\">${msg}</p><div id=\"loading\" class=\"spinner\"></div><div id=\"fallback\" class=\"fallback\"><a href=${deepLinkJson} style=\"display:inline-block;padding:10px 24px;background:#fff;color:#000;border-radius:8px;text-decoration:none;font-size:14px;font-weight:500\">Open Agent Native</a><p style=\"font-size:12px;color:#666;margin:0\">If the app didn\\u2019t open automatically, click the button above.</p></div><script>(function(){var ua=(navigator.userAgent||\"\");if(ua.indexOf(\"AgentNativeDesktop\")===-1){window.location.replace(\"/\");return}window.location.href=${deepLinkJson};setTimeout(function(){document.getElementById(\"loading\").style.display=\"none\";document.getElementById(\"fallback\").classList.add(\"show\")},3000)})()</script></body></html>`,\n );\n }\n return htmlResponse(\n oauthSuccessCloseTabHtml(\n msg,\n \"You can close this tab and return to Agent Native.\",\n ),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"google-oauth.js","sourceRoot":"","sources":["../../src/server/google-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EACL,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,iBAAiB,GAElB,MAAM,IAAI,CAAC;AAEZ,OAAO,EAAE,6BAA6B,EAAE,MAAM,qCAAqC,CAAC;AACpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EACL,wBAAwB,EACxB,iCAAiC,GAClC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,oCAAoC,EAAE,MAAM,sBAAsB,CAAC;AAE5E,+EAA+E;AAE/E;;oDAEoD;AACpD,SAAS,YAAY,CAAC,IAAY,EAAE,MAAM,GAAG,GAAG;IAC9C,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE;KACxD,CAAC,CAAC;AACL,CAAC;AAED;;;gCAGgC;AAChC,SAAS,gBAAgB,CAAC,MAAe;IACvC,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAChD,CAAC;AAED,SAAS,wBAAwB,CAC/B,QAAgB,EAChB,QAAgB,EAChB,WAAoB;IAEpB,MAAM,KAAK,GAAG,WAAW;QACvB,CAAC,CAAC,sEAAsE,UAAU,CAAC,WAAW,CAAC,MAAM;QACrG,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,4hBAA4hB,QAAQ,qDAAqD,QAAQ,OAAO,KAAK,iFAAiF,IAAI,CAAC,SAAS,CAAC,WAAW,IAAI,IAAI,CAAC,qFAAqF,CAAC;AACh0B,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;SACnB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,2BAA2B,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;AAChF,CAAC;AAED;;;;;GAKG;AACH,MAAM,+BAA+B,GAAG;IACtC,wBAAwB;IACxB,6BAA6B;IAC7B,SAAS;IACT,cAAc;IACd,iBAAiB;IACjB,sBAAsB;IACtB,KAAK;IACL,YAAY;CACJ,CAAC;AAEX,MAAM,iCAAiC,GAAG;IACxC,uBAAuB;IACvB,4BAA4B;CACpB,CAAC;AAEX,SAAS,eAAe,CAAC,GAAuB;IAC9C,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAC1B,GAAgB,EAChB,GAAuB,EACvB,OAAmC;IAEnC,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,gBAAgB,CAAC,MAAM,CAAC;QAAE,OAAO;IAC/D,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,kBAAkB,CACzB,IAAuB,EACvB,OAAmC;IAEnC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,gBAAgB,CAAC,MAAM,CAAC;YAAE,SAAS;QACjE,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,4BAA4B;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,+BAA+B,EAAE,CAAC;QAClD,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,iCAAiC,EAAE,CAAC;QACpD,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,qBAAqB,CAAC,KAAyB;IAC7D,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,CAAC,MAAM,IAAI,4BAA4B,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,0BAA0B;IACjC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,+BAA+B,EAAE;QAC3E,aAAa,EAAE,IAAI;KACpB,CAAC,CAAC;IACH,IAAI,gBAAgB;QAAE,OAAO,gBAAgB,CAAC;IAE9C,OAAO,kBAAkB,CAAC,iCAAiC,EAAE;QAC3D,aAAa,EAAE,KAAK;KACrB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,cAAc,CAAC,IAAwB;IAC9C,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QACzC,OAAO,CACL,MAAM,CAAC,QAAQ,KAAK,WAAW;YAC/B,MAAM,CAAC,QAAQ,KAAK,WAAW;YAC/B,MAAM,CAAC,QAAQ,KAAK,KAAK;YACzB,MAAM,CAAC,QAAQ,KAAK,OAAO,CAC5B,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,MAA0B;IAClD,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,IAAI,CAAC;QACH,OAAO,cAAc,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAwB;IACpD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC/C,OAAO,CACL,QAAQ,KAAK,eAAe;YAC5B,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACnC,QAAQ,KAAK,eAAe;YAC5B,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACnC,QAAQ,KAAK,eAAe;YAC5B,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACnC,QAAQ,KAAK,YAAY;YACzB,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC;YAChC,QAAQ,KAAK,YAAY;YACzB,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,CACjC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,MAAM,UAAU,GACd,SAAS,CAAC,KAAK,EAAE,kBAAkB,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;IACrD,MAAM,WAAW,GACf,SAAS,CAAC,KAAK,EAAE,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACvE,MAAM,uBAAuB,GAAG,oCAAoC,EAAE;QACpE,CAAC,CAAC,0BAA0B,EAAE;QAC9B,CAAC,CAAC,SAAS,CAAC;IAEd,IACE,uBAAuB;QACvB,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,oBAAoB,CAAC,UAAU,CAAC,CAAC,EAChE,CAAC;QACD,OAAO,uBAAuB,CAAC;IACjC,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,4BAA4B,EAAE,CAAC;QAC7C,yEAAyE;QACzE,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,WAAW,MAAM,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,IAAI,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,OAAO,OAAO,CAAC;YAClD,mEAAmE;YACnE,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC;QACD,kEAAkE;QAClE,+DAA+D;QAC/D,OAAO,GAAG,WAAW,MAAM,UAAU,IAAI,EAAE,EAAE,CAAC;IAChD,CAAC;IAED,OAAO,GAAG,WAAW,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;AACzD,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,cAAc;IAC5B,yEAAyE;IACzE,2EAA2E;IAC3E,6EAA6E;IAC7E,wDAAwD;IACxD,OAAO,yBAAyB,EAAE,CAAC;AACrC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,IAAI,GAAG,GAAG;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAC3D,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,cAAc,EAAE,GAAG,SAAS,EAAE,CAAC;AAC9D,CAAC;AAED,SAAS,4BAA4B,CAAC,QAAgB;IACpD,OAAO,CACL,QAAQ,CAAC,UAAU,CAAC,iBAAiB,CAAC;QACtC,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CACpE,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAc;IAC5C,MAAM,eAAe,GAAI,KAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACjE,IAAI,OAAO,eAAe,KAAK,QAAQ,IAAI,eAAe,EAAE,CAAC;QAC3D,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,MAAM,WAAW,GAAI,KAAa,CAAC,GAAG,EAAE,QAAQ,CAAC;IACjD,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEvE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC;IACrC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAClE,CAAC;IAED,MAAM,SAAS,GAAI,KAAa,CAAC,IAAI,CAAC;IACtC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC/C,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1C,OAAO,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,yBAAyB,CAAC,KAAc;IAC/C,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,MAAM,WAAW,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAClD,OAAO,CACL,WAAW,KAAK,GAAG,QAAQ,gBAAgB;QAC3C,WAAW,CAAC,UAAU,CAAC,GAAG,QAAQ,iBAAiB,CAAC,CACrD,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CAAC,KAAc,EAAE,IAAY;IAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAC3D,IACE,oCAAoC,EAAE;QACtC,4BAA4B,CAAC,SAAS,CAAC,EACvC,CAAC;QACD,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,SAAS,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,QAAQ,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,QAAQ,GAAG,SAAS,EAAE,CAAC;AACtD,CAAC;AAED,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,yBAAyB,CACvC,SAAiB,EACjB,KAAc;IAEd,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1E,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,qCAAqC;IACrC,MAAM,cAAc,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,WAAgB,CAAC;IACrB,IAAI,CAAC;QACH,WAAW,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IAChD,yEAAyE;IACzE,4EAA4E;IAC5E,wEAAwE;IACxE,mCAAmC;IACnC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,MAAM,eAAe,GACnB,QAAQ,IAAI,yBAAyB,CAAC,KAAK,CAAC;QAC1C,CAAC,CAAC;YACE,GAAG,QAAQ,iBAAiB;YAC5B,GAAG,CAAC,oCAAoC,EAAE;gBAC1C,4BAA4B,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACxC,CAAC,CAAC,CAAC,iBAAiB,CAAC;gBACrB,CAAC,CAAC,EAAE,CAAC;SACR;QACH,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC1B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAc,EACd,WAAW,GAAG,gCAAgC;IAE9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC;IAC9C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACtE,CAAC;IACD,OAAO,0BAA0B,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;AACxD,CAAC;AAwBD;;;;;GAKG;AACH,IAAI,mBAAuC,CAAC;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,kBAAkB;IACzB,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAC9B,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAC9B,4BAA4B,CAAC,aAAa,CAAC,CAAC;IAC9C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;IACrD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,gDAAgD;YAC9C,4FAA4F,CAC/F,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,mBAAmB,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAqBD,SAAS,wBAAwB,CAC/B,KAAc;IAEd,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,GAAG,GAAuC,EAAE,CAAC;IACnD,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAc;IAC9C,OAAO,6BAA6B,CAAC,KAAK,CAAC,CAAC;AAC9C,CAAC;AA0BD,MAAM,UAAU,gBAAgB,CAC9B,iBAAmD,EACnD,KAAc,EACd,OAAiB,EACjB,UAAoB,EACpB,GAAY,EACZ,SAAkB,EAClB,MAAe;IAEf,MAAM,IAAI,GACR,OAAO,iBAAiB,KAAK,QAAQ;QACnC,CAAC,CAAC;YACE,WAAW,EAAE,iBAAiB;YAC9B,KAAK;YACL,OAAO;YACP,UAAU;YACV,GAAG;YACH,SAAS;YACT,MAAM;SACP;QACH,CAAC,CAAC,iBAAiB,CAAC;IAExB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,OAAO,GAA4B;QACvC,CAAC,EAAE,KAAK;QACR,CAAC,EAAE,IAAI,CAAC,WAAW;KACpB,CAAC;IACF,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;IACvC,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;IACvC,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IACnC,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IACtC,IAAI,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IACrC,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;IAChD,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACzC,IAAI,IAAI,CAAC,iBAAiB;QAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC;IAChE,MAAM,iBAAiB,GAAG,6BAA6B,CACrD,IAAI,CAAC,iBAAiB,CACvB,CAAC;IACF,IAAI,iBAAiB;QAAE,OAAO,CAAC,EAAE,GAAG,iBAAiB,CAAC;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxE,MAAM,GAAG,GAAG,MAAM;SACf,UAAU,CAAC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;SAC1C,MAAM,CAAC,IAAI,CAAC;SACZ,MAAM,CAAC,WAAW,CAAC,CAAC;IACvB,OAAO,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAA8B,EAC9B,WAAmB;IAEnB,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,MAAM,KAAK,CAAC,CAAC;gBAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;YAEvD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YACzC,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,MAAM;iBACpB,UAAU,CAAC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;iBAC1C,MAAM,CAAC,IAAI,CAAC;iBACZ,MAAM,CAAC,WAAW,CAAC,CAAC;YAEvB,IACE,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;gBAC9B,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAChE,CAAC;gBACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;YACtC,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YACrE,OAAO;gBACL,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,WAAW;gBACpC,KAAK,EAAE,MAAM,CAAC,CAAC,IAAI,SAAS;gBAC5B,KAAK,EAAE,OAAO,MAAM,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC1D,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;gBACnB,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;gBACtB,GAAG,EAAE,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;gBAC5D,oEAAoE;gBACpE,kEAAkE;gBAClE,kEAAkE;gBAClE,4CAA4C;gBAC5C,SAAS,EAAE,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;gBAChE,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,SAAS;gBAC7B,iBAAiB,EAAE,wBAAwB,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,wBAAwB,CAAC,MAAM,CAAC,EAAE,CAAC;aACvD,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AACtC,CAAC;AASD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAc,EACd,UAAmB;IAEnB,MAAM,eAAe,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,oBAAoB,GAAG,CAAC,CAAC,eAAe,EAAE,KAAK,CAAC;IACtD,MAAM,KAAK,GAAG,oBAAoB;QAChC,CAAC,CAAC,eAAgB,CAAC,KAAK;QACxB,CAAC,CAAC,UAAU,IAAI,SAAS,CAAC;IAE5B,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;AACzC,CAAC;AAMD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAc,EACd,KAAa,EACb,IAUC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC;IAC7C,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAElC,IAAI,YAAgC,CAAC;IACrC,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAC9B,IAAI,CAAC,IAAI,CAAC,oBAAoB,IAAI,aAAa,EAAE,CAAC;QAChD,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACnD,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC9D,wBAAwB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;gBACjD,sBAAsB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;aAChD,CAAC,CAAC;YACH,iBAAiB,GAAG,CAAC,gBAAgB,IAAI,CAAC,iBAAiB,CAAC;QAC9D,CAAC;QAED,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtD,MAAM,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACtC,yBAAyB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAC/C,IAAI,iBAAiB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,WAAW,GACf,IAAI,CAAC,WAAW,CAAC,WAAW;gBAC5B,iCAAiC,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC;YACxE,MAAM,WAAW,GACf,IAAI,CAAC,WAAW,CAAC,iBAAiB;gBAClC,wBAAwB,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC;YAC/D,MAAM,gBAAgB,CAAC;gBACrB,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,YAAY;gBAC3C,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,UAAU;gBACvC,KAAK;gBACL,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI;gBAC3B,WAAW;gBACX,WAAW;aACZ,CAAC,CAAC;QACL,CAAC;QACD,kEAAkE;QAClE,iEAAiE;QACjE,6DAA6D;QAC7D,8DAA8D;QAC9D,8DAA8D;QAC9D,gEAAgE;QAChE,iCAAiC;QACjC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC/C,MAAM,eAAe,CAAC;gBACpB,KAAK;gBACL,KAAK,EAAE,YAAY;gBACnB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI;aACtC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,CAAC;AAC1B,CAAC;AAED,gFAAgF;AAEhF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAc,EACd,KAAa,EACb,IAaC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,aAAa,GACjB,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACvD,CAAC,CAAC,KAAK,CAAC,KAAK;QACb,CAAC,CAAC,SAAS,CAAC;IAEhB,2EAA2E;IAC3E,2EAA2E;IAC3E,6EAA6E;IAC7E,oEAAoE;IACpE,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,QAAQ,GAAG,0BAA0B,CACzC,IAAI,CAAC,YAAY,EACjB,aAAa,CACd,CAAC;QACF,MAAM,WAAW,GAAG,6BAA6B,CAC/C,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,YAAY,CAClB,CAAC;QACF,OAAO,YAAY,CACjB,sYAAsY,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,+CAA+C,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,gCAAgC,CACzgB,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,4EAA4E;IAC5E,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,WAAW,GAAG,UAAU,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QAClE,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,aAAa,SAAS,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;QACjE,OAAO,YAAY,CACjB,wBAAwB,CACtB,GAAG,EACH,wCAAwC,WAAW,GAAG,EACtD,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAC9B,CACF,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,uEAAuE;IACvE,oEAAoE;IACpE,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1E,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IAC5E,CAAC;IAED,wEAAwE;IACxE,iEAAiE;IACjE,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,WAAW,GAAG,UAAU,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QAClE,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,gBAAgB,SAAS,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;QACpE,OAAO,YAAY,CACjB,wBAAwB,CACtB,GAAG,EACH,wCAAwC,WAAW,GAAG,EACtD,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAC9B,CACF,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,sEAAsE;IACtE,yEAAyE;IACzE,uEAAuE;IACvE,oEAAoE;IACpE,IAAI,IAAI,CAAC,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IAC5E,CAAC;IAED,uEAAuE;IACvE,sEAAsE;IACtE,qEAAqE;IACrE,oEAAoE;IACpE,mDAAmD;IACnD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACzE,OAAO,YAAY,CAAC;;;;yCAIiB,SAAS;;8BAEpB,CAAC,CAAC;IAC9B,CAAC;IAED,uEAAuE;IACvE,uEAAuE;IACvE,wEAAwE;IACxE,oEAAoE;IACpE,MAAM,QAAQ,GAAG,6BAA6B,CAC5C,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,YAAY,CAClB,CAAC;IACF,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC9B,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC/C,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,EAAE,aAAa,CAAC,CAAC;IAC3D,4EAA4E;IAC5E,uEAAuE;IACvE,8EAA8E;IAC9E,yEAAyE;IACzE,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC;QAC1B,QAAQ,EAAE,QAAQ;QAClB,iBAAiB,EAAE,aAAa;KACjC,CAAC,CAAC;IACH,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;QAChE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;kEAGkE;AAClE,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,YAAY,CACjB,ilBAAilB,IAAI,0KAA0K,EAC/vB,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,OAAO,GAAG,yBAAyB;IAEnC,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,YAAY,CACjB,yPAAyP,IAAI,mDAAmD,CACjT,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF,SAAS,mBAAmB,CAAC,QAAiB;IAC5C,MAAM,GAAG,GAAG,QAAQ,IAAI,UAAU,EAAE,IAAI,cAAc,CAAC;IACvD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC3C,OAAO,GAAG;SACP,KAAK,CAAC,OAAO,CAAC;SACd,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACpD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,SAAS,0BAA0B,CACjC,YAAqB,EACrB,KAAc;IAEd,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,IAAI,YAAY;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACpD,IAAI,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IACjC,OAAO,MAAM;QACX,CAAC,CAAC,gCAAgC,MAAM,EAAE;QAC1C,CAAC,CAAC,8BAA8B,CAAC;AACrC,CAAC;AAED,SAAS,kBAAkB,CACzB,MAAe,EACf,KAAc,EACd,YAAqB,EACrB,KAAc;IAEd,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,aAAa,SAAS,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;IACjE,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,QAAQ,GAAG,0BAA0B,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACjE,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC9C,wEAAwE;QACxE,yEAAyE;QACzE,qEAAqE;QACrE,qEAAqE;QACrE,yEAAyE;QACzE,OAAO,YAAY,CACjB,isBAAisB,GAAG,2FAA2F,YAAY,2bAA2b,YAAY,4KAA4K,CAC/5C,CAAC;IACJ,CAAC;IACD,OAAO,YAAY,CACjB,wBAAwB,CACtB,GAAG,EACH,oDAAoD,CACrD,CACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Shared Google OAuth utilities for all templates.\n *\n * Handles platform detection (desktop/mobile), state encoding,\n * session token creation, and deep-link responses — the logic\n * that was previously copy-pasted across every template's\n * google-auth.ts handler.\n */\n\nimport crypto from \"node:crypto\";\n\nimport {\n getHeader,\n getQuery,\n setResponseStatus,\n setResponseHeader,\n type H3Event,\n} from \"h3\";\n\nimport { normalizeAnalyticsAnonymousId } from \"../shared/analytics-anonymous-id.js\";\nimport { getAppBasePathFromViteEnv } from \"./app-base-path.js\";\nimport { getAppName } from \"./app-name.js\";\nimport {\n readAnalyticsAnonymousId,\n signupAttributionFromCookieHeader,\n} from \"./attribution.js\";\nimport {\n addSession,\n getSession,\n getSessionMaxAge,\n hasLegacySessionForEmail,\n setFrameworkSessionCookie,\n} from \"./auth.js\";\nimport {\n hasBetterAuthUserEmail,\n trackSignupEvent,\n} from \"./better-auth-instance.js\";\nimport { getWorkspaceA2ADerivedSecret } from \"./derived-secret.js\";\nimport { writeDesktopSso } from \"./desktop-sso.js\";\nimport { appendSessionToOAuthReturnUrl } from \"./oauth-return-url.js\";\nimport { isWorkspaceOAuthCallbackRelayEnabled } from \"./workspace-oauth.js\";\n\n// ─── Platform Detection ─────────────────────────────────────────────────────\n\n/** Return an HTML response with the correct Content-Type.\n * Uses a web-standard Response to ensure the header survives\n * Nitro dev mode's mock-node-response pipeline. */\nfunction htmlResponse(html: string, status = 200): Response {\n return new Response(html, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\n/** Shared markup for OAuth success \"close this tab\" pages. Renders a green\n * check icon above the message, with a little breathing room between the\n * headline and secondary line. Used by every template that goes through the\n * shared Google OAuth flow. */\nfunction oauthDebugFlowId(flowId?: string): string | undefined {\n return flowId ? flowId.slice(-10) : undefined;\n}\n\nfunction oauthSuccessCloseTabHtml(\n headline: string,\n footnote: string,\n debugFlowId?: string,\n): string {\n const debug = debugFlowId\n ? `<p style=\"font-size:11px;color:#555;margin:12px 0 0 0\">Debug flow: ${escapeHtml(debugFlowId)}</p>`\n : \"\";\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column\"><svg width=\"44\" height=\"44\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#22c55e\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-bottom:14px\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M9 12l2 2l4 -4\"/></svg><p style=\"font-size:16px;margin:0 0 12px 0\">${headline}</p><p style=\"font-size:13px;color:#888;margin:0\">${footnote}</p>${debug}<script>console.info(\"[agent-native][google-oauth] success page loaded\",{flow:${JSON.stringify(debugFlowId || null)}});setTimeout(function(){try{window.close()}catch(e){}},250)</script></body></html>`;\n}\n\n/**\n * HTML escape — minimal but covers the cases that matter when interpolating\n * user-controlled values into our OAuth callback HTML. Mirrors the helper in\n * email-template.ts; kept inline here to avoid a circular import.\n */\nfunction escapeHtml(s: string): string {\n return String(s ?? \"\")\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n/**\n * Detect requests from the Agent Native desktop app specifically.\n *\n * The desktop app appends `AgentNativeDesktop/<version>` to its user-agent\n * (see `packages/desktop-app/src/main/index.ts`). We check for that marker\n * rather than matching generic `Electron`, which would also match other\n * Electron-based webviews like Builder.io's Fusion, Slack desktop, Discord,\n * etc. Falsely treating those as \"the desktop app\" sends users to the\n * `agentnative://oauth-complete` deep-link success page after Google sign-in,\n * where the protocol handler can't fire and the \"Open Agent Native\" button\n * does nothing.\n *\n * Kept exported as `isElectron` for backwards compatibility with consumers.\n */\nexport function isElectron(event: H3Event): boolean {\n return /AgentNativeDesktop/i.test(getHeader(event, \"user-agent\") || \"\");\n}\n\n/** Detect requests from a mobile browser (iOS/Android). */\nexport function isMobile(event: H3Event): boolean {\n return /iPhone|iPad|iPod|Android/i.test(getHeader(event, \"user-agent\") || \"\");\n}\n\n/**\n * Build the static allowlist of origins we trust for `getOrigin`. Reads\n * deployment-known public URLs. Each entry is normalised to\n * `${proto}://${host}` (no path). Duplicates collapse, invalid entries are\n * dropped silently.\n */\nconst EXPLICIT_PUBLIC_ORIGIN_ENV_KEYS = [\n \"WORKSPACE_OAUTH_ORIGIN\",\n \"VITE_WORKSPACE_OAUTH_ORIGIN\",\n \"APP_URL\",\n \"VITE_APP_URL\",\n \"BETTER_AUTH_URL\",\n \"VITE_BETTER_AUTH_URL\",\n \"URL\",\n \"DEPLOY_URL\",\n] as const;\n\nconst WORKSPACE_GATEWAY_ORIGIN_ENV_KEYS = [\n \"WORKSPACE_GATEWAY_URL\",\n \"VITE_WORKSPACE_GATEWAY_URL\",\n] as const;\n\nfunction normalizeOrigin(raw: string | undefined): string | undefined {\n if (!raw) return undefined;\n try {\n const u = new URL(raw);\n return `${u.protocol}//${u.host}`;\n } catch {\n return undefined;\n }\n}\n\nfunction addNormalizedOrigin(\n out: Set<string>,\n raw: string | undefined,\n options: { allowLoopback: boolean },\n): void {\n const origin = normalizeOrigin(raw);\n if (!origin) return;\n if (!options.allowLoopback && isLoopbackOrigin(origin)) return;\n out.add(origin);\n}\n\nfunction firstOriginFromEnv(\n keys: readonly string[],\n options: { allowLoopback: boolean },\n): string | undefined {\n for (const key of keys) {\n const origin = normalizeOrigin(process.env[key]);\n if (!origin) continue;\n if (!options.allowLoopback && isLoopbackOrigin(origin)) continue;\n return origin;\n }\n return undefined;\n}\n\nfunction getConfiguredOriginAllowlist(): Set<string> {\n const out = new Set<string>();\n for (const key of EXPLICIT_PUBLIC_ORIGIN_ENV_KEYS) {\n addNormalizedOrigin(out, process.env[key], { allowLoopback: true });\n }\n for (const key of WORKSPACE_GATEWAY_ORIGIN_ENV_KEYS) {\n addNormalizedOrigin(out, process.env[key], { allowLoopback: false });\n }\n return out;\n}\n\n/** Return whether a candidate is one of this deployment's configured origins. */\nexport function isConfiguredAppOrigin(value: string | undefined): boolean {\n const origin = normalizeOrigin(value);\n return !!origin && getConfiguredOriginAllowlist().has(origin);\n}\n\nfunction getWorkspaceCallbackOrigin(): string | undefined {\n const publicAuthOrigin = firstOriginFromEnv(EXPLICIT_PUBLIC_ORIGIN_ENV_KEYS, {\n allowLoopback: true,\n });\n if (publicAuthOrigin) return publicAuthOrigin;\n\n return firstOriginFromEnv(WORKSPACE_GATEWAY_ORIGIN_ENV_KEYS, {\n allowLoopback: false,\n });\n}\n\nfunction isLoopbackHost(host: string | undefined): boolean {\n if (!host) return false;\n try {\n const parsed = new URL(`http://${host}`);\n return (\n parsed.hostname === \"localhost\" ||\n parsed.hostname === \"127.0.0.1\" ||\n parsed.hostname === \"::1\" ||\n parsed.hostname === \"[::1]\"\n );\n } catch {\n return false;\n }\n}\n\nfunction isLoopbackOrigin(origin: string | undefined): boolean {\n if (!origin) return false;\n try {\n return isLoopbackHost(new URL(origin).host);\n } catch {\n return false;\n }\n}\n\nfunction isBuilderPreviewHost(host: string | undefined): boolean {\n if (!host) return false;\n try {\n const parsed = new URL(`http://${host}`);\n const hostname = parsed.hostname.toLowerCase();\n return (\n hostname === \"builderio.xyz\" ||\n hostname.endsWith(\".builderio.xyz\") ||\n hostname === \"builderio.dev\" ||\n hostname.endsWith(\".builderio.dev\") ||\n hostname === \"builder.codes\" ||\n hostname.endsWith(\".builder.codes\") ||\n hostname === \"builder.io\" ||\n hostname.endsWith(\".builder.io\") ||\n hostname === \"builder.my\" ||\n hostname.endsWith(\".builder.my\")\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Get the origin from forwarded headers or Host.\n *\n * Defends against Host-header injection: in production we require the resolved\n * origin to match `APP_URL` / `BETTER_AUTH_URL` / `WORKSPACE_GATEWAY_URL`,\n * falling back to those values when inbound headers are missing or don't match.\n * In dev we accept inbound `Host` so localhost / ngrok / preview hosts keep\n * working without configuration, except workspace OAuth requests from loopback\n * or Builder preview hosts use the configured gateway origin when one exists.\n * The protocol defaults to `https` in production (so a TLS-terminating proxy\n * that drops `x-forwarded-proto` doesn't downgrade us to plain HTTP).\n */\nexport function getOrigin(event: H3Event): string {\n const headerHost =\n getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const isProd = process.env.NODE_ENV === \"production\";\n const headerProto =\n getHeader(event, \"x-forwarded-proto\") || (isProd ? \"https\" : \"http\");\n const workspaceCallbackOrigin = isWorkspaceOAuthCallbackRelayEnabled()\n ? getWorkspaceCallbackOrigin()\n : undefined;\n\n if (\n workspaceCallbackOrigin &&\n (isLoopbackHost(headerHost) || isBuilderPreviewHost(headerHost))\n ) {\n return workspaceCallbackOrigin;\n }\n\n if (isProd) {\n const allow = getConfiguredOriginAllowlist();\n // If the deploy declares its public URL, prefer it over inbound headers.\n if (allow.size > 0) {\n const inbound = headerHost ? `${headerProto}://${headerHost}` : \"\";\n if (inbound && allow.has(inbound)) return inbound;\n // Inbound didn't match — fall back to the first configured origin.\n return [...allow][0];\n }\n // No allowlist configured: still default to https, but accept the\n // inbound Host (best we can do without a configured base URL).\n return `${headerProto}://${headerHost ?? \"\"}`;\n }\n\n return `${headerProto}://${headerHost ?? \"localhost\"}`;\n}\n\n/** App mount prefix, if the template is served under APP_BASE_PATH. */\nexport function getAppBasePath(): string {\n // Vite statically replaces VITE_* values in the server bundle during the\n // build, but Netlify/Nitro does not necessarily expose those build vars at\n // runtime. Keep auth and OAuth path matching aligned with the SSR handler by\n // falling back to import.meta.env (including BASE_URL).\n return getAppBasePathFromViteEnv();\n}\n\n/** Build an absolute same-origin URL that preserves APP_BASE_PATH. */\nexport function getAppUrl(event: H3Event, path = \"/\"): string {\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return `${getOrigin(event)}${getAppBasePath()}${cleanPath}`;\n}\n\nfunction isFrameworkOAuthCallbackPath(pathname: string): boolean {\n return (\n pathname.startsWith(\"/_agent-native/\") &&\n (pathname.endsWith(\"/callback\") || pathname.includes(\"/callback/\"))\n );\n}\n\nfunction getOriginalRequestPath(event: H3Event): string {\n const mountedPathname = (event as any).context?._mountedPathname;\n if (typeof mountedPathname === \"string\" && mountedPathname) {\n return mountedPathname;\n }\n\n const urlPathname = (event as any).url?.pathname;\n if (typeof urlPathname === \"string\" && urlPathname) return urlPathname;\n\n const nodeUrl = event.node?.req?.url;\n if (typeof nodeUrl === \"string\" && nodeUrl) {\n const queryStart = nodeUrl.indexOf(\"?\");\n return queryStart >= 0 ? nodeUrl.slice(0, queryStart) : nodeUrl;\n }\n\n const eventPath = (event as any).path;\n if (typeof eventPath === \"string\" && eventPath) {\n const queryStart = eventPath.indexOf(\"?\");\n return queryStart >= 0 ? eventPath.slice(0, queryStart) : eventPath;\n }\n\n return \"/\";\n}\n\nfunction isRequestUnderAppBasePath(event: H3Event): boolean {\n const basePath = getAppBasePath();\n if (!basePath) return false;\n const requestPath = getOriginalRequestPath(event);\n return (\n requestPath === `${basePath}/_agent-native` ||\n requestPath.startsWith(`${basePath}/_agent-native/`)\n );\n}\n\nfunction getDefaultOAuthRedirectUrl(event: H3Event, path: string): string {\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n if (\n isWorkspaceOAuthCallbackRelayEnabled() &&\n isFrameworkOAuthCallbackPath(cleanPath)\n ) {\n return `${getOrigin(event)}${cleanPath}`;\n }\n const basePath = isRequestUnderAppBasePath(event) ? getAppBasePath() : \"\";\n return `${getOrigin(event)}${basePath}${cleanPath}`;\n}\n\n// ─── redirect_uri Allowlist ──────────────────────────────────────────────────\n\n/**\n * Validate a user-supplied `redirect_uri` for OAuth flows.\n *\n * Defends against authorization-code interception (RFC 6819 §4.4.1.7):\n * even though the upstream provider (Google/Atlassian/Zoom) refuses\n * unregistered redirect URIs, prefix-style registrations and side\n * registrations on the same host let a malicious caller swap in an\n * attacker-controlled URI that the provider still accepts. We reject any\n * candidate that isn't on this server's own origin AND under the\n * framework's `/_agent-native/` namespace. Returns the validated URI on\n * success, or `undefined` on rejection — callers must treat `undefined`\n * as a 400.\n *\n * The intentional shape is exact-prefix:\n * - Origin must equal `getOrigin(event)` — no Host-header injection\n * reusing somebody else's registered redirect URI.\n * - Path must start with `${appBasePath}/_agent-native/` so we never\n * hand auth codes to a public marketing or open-redirect endpoint\n * on the same registered host.\n *\n * For desktop / native flows that need ephemeral `http://127.0.0.1:<port>`\n * loopback URIs, callers should validate those at the template level\n * with a dedicated allowlist — this helper rejects them by design.\n */\nexport function isAllowedOAuthRedirectUri(\n candidate: string,\n event: H3Event,\n): boolean {\n if (typeof candidate !== \"string\" || candidate.length === 0) return false;\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n return false;\n }\n // Must be same origin as our server.\n const expectedOrigin = getOrigin(event);\n let expectedUrl: URL;\n try {\n expectedUrl = new URL(expectedOrigin);\n } catch {\n return false;\n }\n if (url.protocol !== expectedUrl.protocol) return false;\n if (url.host !== expectedUrl.host) return false;\n // Must live under the framework's namespace. Workspace deploys can route\n // root /_agent-native/* to Dispatch even when Dispatch itself is mounted at\n // /dispatch, but app-prefixed requests should not be able to swap their\n // callback to that root namespace.\n const basePath = getAppBasePath();\n const allowedPrefixes =\n basePath && isRequestUnderAppBasePath(event)\n ? [\n `${basePath}/_agent-native/`,\n ...(isWorkspaceOAuthCallbackRelayEnabled() &&\n isFrameworkOAuthCallbackPath(url.pathname)\n ? [\"/_agent-native/\"]\n : []),\n ]\n : [\"/_agent-native/\"];\n if (!allowedPrefixes.some((prefix) => url.pathname.startsWith(prefix))) {\n return false;\n }\n return true;\n}\n\n/**\n * Resolve the `redirect_uri` for an outbound OAuth `auth-url` request.\n *\n * Reads `?redirect_uri=` from the query and validates it via\n * `isAllowedOAuthRedirectUri`. Returns:\n * - the validated URI when supplied and allowed, OR\n * - the framework default when no override was supplied, OR\n * - `null` when an override was supplied but rejected — callers must\n * respond with 400 in that case.\n *\n * Templates that need a non-default redirect path can pass it via\n * `defaultPath` (e.g. `\"/_agent-native/google/desktop-callback\"` for\n * desktop flows).\n */\nexport function resolveOAuthRedirectUri(\n event: H3Event,\n defaultPath = \"/_agent-native/google/callback\",\n): string | null {\n const supplied = getQuery(event).redirect_uri;\n if (typeof supplied === \"string\" && supplied.length > 0) {\n return isAllowedOAuthRedirectUri(supplied, event) ? supplied : null;\n }\n return getDefaultOAuthRedirectUrl(event, defaultPath);\n}\n\n// ─── OAuth State ─────────────────────────────────────────────────────────────\n\nexport interface OAuthStatePayload {\n redirectUri: string;\n owner?: string;\n orgId?: string;\n desktop?: boolean;\n addAccount?: boolean;\n app?: string;\n /**\n * Same-origin path to redirect to after a successful web-flow sign-in.\n * Threaded through the (HMAC-signed) state so it survives the round trip\n * to Google. Validated again on decode via safeReturnPath as defence in\n * depth. Has no effect on desktop / mobile / add-account flows, which\n * use their own deep-link / close-tab handling.\n */\n returnUrl?: string;\n flowId?: string;\n signupAttribution?: Record<string, string | undefined>;\n signupAnonymousId?: string;\n}\n\n/**\n * Ephemeral in-memory state-signing key for development. Generated lazily\n * on first read so dev sessions don't depend on filesystem writability or\n * env-var configuration. Sessions reset on each restart, which is fine\n * for dev — no real users / production data are involved.\n */\nlet _devStateSigningKey: string | undefined;\n\n/**\n * Derive a server-only signing key for HMAC verification of OAuth state.\n *\n * Uses a dedicated secret — never an OAuth client secret. Reusing a\n * client_secret (which is shared with Google / GitHub / Atlassian) as our\n * own HMAC key conflates two trust domains: rotating the client secret\n * silently invalidates every in-flight OAuth state, and any leak of the\n * client secret also lets an attacker forge our state envelopes.\n *\n * Resolution order:\n * 1. OAUTH_STATE_SECRET (preferred — dedicated to this purpose)\n * 2. BETTER_AUTH_SECRET (already used by Better Auth as a server secret)\n * 3. Hosted workspace deploys derive a per-purpose key from A2A_SECRET\n * 4. In dev only, an ephemeral random key (per-process)\n *\n * In production, throws if no usable server secret is set.\n */\nfunction getStateSigningKey(): string {\n const secret =\n process.env.OAUTH_STATE_SECRET ||\n process.env.BETTER_AUTH_SECRET ||\n getWorkspaceA2ADerivedSecret(\"oauth-state\");\n if (secret) return secret;\n\n const isProd = process.env.NODE_ENV === \"production\";\n if (isProd) {\n throw new Error(\n \"OAuth state signing requires a server secret. \" +\n \"Set OAUTH_STATE_SECRET, BETTER_AUTH_SECRET, or A2A_SECRET in production workspace deploys.\",\n );\n }\n\n if (!_devStateSigningKey) {\n _devStateSigningKey = crypto.randomBytes(32).toString(\"hex\");\n }\n return _devStateSigningKey;\n}\n\n/**\n * Options for the named-argument form of {@link encodeOAuthState}.\n * Prefer this form — the positional overload is easy to misuse (the mail\n * and calendar templates historically passed `flowId` in the `returnUrl`\n * slot, smuggling state into a defence-in-depth path).\n */\nexport interface EncodeOAuthStateOptions {\n redirectUri: string;\n owner?: string;\n orgId?: string;\n desktop?: boolean;\n addAccount?: boolean;\n app?: string;\n returnUrl?: string;\n flowId?: string;\n signupAttribution?: Record<string, string | undefined>;\n signupAnonymousId?: string;\n}\n\nfunction sanitizeStateAttribution(\n value: unknown,\n): Record<string, string | undefined> | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return undefined;\n }\n const out: Record<string, string | undefined> = {};\n for (const [key, raw] of Object.entries(value)) {\n if (typeof raw === \"string\") out[key] = raw;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction sanitizeStateAnonymousId(value: unknown): string | undefined {\n return normalizeAnalyticsAnonymousId(value);\n}\n\n/**\n * Encode OAuth state into a signed base64url string.\n * The state is HMAC-signed so the callback can verify it wasn't forged,\n * preventing CSRF attacks on the OAuth flow.\n *\n * Two call shapes are supported:\n * - Recommended: pass an options object — clear, mismatch-proof.\n * `encodeOAuthState({ redirectUri, owner, desktop, ... })`\n * - Legacy positional form (kept working for backward compatibility):\n * `encodeOAuthState(redirectUri, owner, desktop, addAccount, app, returnUrl, flowId)`.\n * Callers should migrate to the options form — see the audit on\n * templates/mail and templates/calendar where the positional shape\n * led to `flowId` being smuggled in via the `returnUrl` slot.\n */\nexport function encodeOAuthState(opts: EncodeOAuthStateOptions): string;\nexport function encodeOAuthState(\n redirectUri: string,\n owner?: string,\n desktop?: boolean,\n addAccount?: boolean,\n app?: string,\n returnUrl?: string,\n flowId?: string,\n): string;\nexport function encodeOAuthState(\n redirectUriOrOpts: string | EncodeOAuthStateOptions,\n owner?: string,\n desktop?: boolean,\n addAccount?: boolean,\n app?: string,\n returnUrl?: string,\n flowId?: string,\n): string {\n const opts: EncodeOAuthStateOptions =\n typeof redirectUriOrOpts === \"string\"\n ? {\n redirectUri: redirectUriOrOpts,\n owner,\n desktop,\n addAccount,\n app,\n returnUrl,\n flowId,\n }\n : redirectUriOrOpts;\n\n const nonce = crypto.randomBytes(8).toString(\"hex\");\n const payload: Record<string, unknown> = {\n n: nonce,\n r: opts.redirectUri,\n };\n if (opts.owner) payload.o = opts.owner;\n if (opts.orgId) payload.g = opts.orgId;\n if (opts.desktop) payload.d = true;\n if (opts.addAccount) payload.a = true;\n if (opts.app) payload.app = opts.app;\n if (opts.returnUrl) payload.r2 = opts.returnUrl;\n if (opts.flowId) payload.f = opts.flowId;\n if (opts.signupAttribution) payload.ft = opts.signupAttribution;\n const signupAnonymousId = normalizeAnalyticsAnonymousId(\n opts.signupAnonymousId,\n );\n if (signupAnonymousId) payload.ai = signupAnonymousId;\n const data = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const sig = crypto\n .createHmac(\"sha256\", getStateSigningKey())\n .update(data)\n .digest(\"base64url\");\n return `${data}.${sig}`;\n}\n\n/**\n * Decode and verify OAuth state from the callback's state query parameter.\n * Rejects forged or tampered state by checking the HMAC signature.\n * Falls back to the provided URI if decoding or verification fails.\n */\nexport function decodeOAuthState(\n stateParam: string | undefined,\n fallbackUri: string,\n): OAuthStatePayload {\n if (stateParam) {\n try {\n const dotIdx = stateParam.lastIndexOf(\".\");\n if (dotIdx === -1) return { redirectUri: fallbackUri };\n\n const data = stateParam.slice(0, dotIdx);\n const sig = stateParam.slice(dotIdx + 1);\n const expected = crypto\n .createHmac(\"sha256\", getStateSigningKey())\n .update(data)\n .digest(\"base64url\");\n\n if (\n sig.length !== expected.length ||\n !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))\n ) {\n return { redirectUri: fallbackUri };\n }\n\n const parsed = JSON.parse(Buffer.from(data, \"base64url\").toString());\n return {\n redirectUri: parsed.r || fallbackUri,\n owner: parsed.o || undefined,\n orgId: typeof parsed.g === \"string\" ? parsed.g : undefined,\n desktop: !!parsed.d,\n addAccount: !!parsed.a,\n app: typeof parsed.app === \"string\" ? parsed.app : undefined,\n // Pass returnUrl through as-is — same-origin validation runs at the\n // consumer (oauthCallbackResponse → safeReturnPath). The state is\n // HMAC-signed, but we still validate at consumption as defence in\n // depth in case the signing key ever leaks.\n returnUrl: typeof parsed.r2 === \"string\" ? parsed.r2 : undefined,\n flowId: parsed.f || undefined,\n signupAttribution: sanitizeStateAttribution(parsed.ft),\n signupAnonymousId: sanitizeStateAnonymousId(parsed.ai),\n };\n } catch {}\n }\n return { redirectUri: fallbackUri };\n}\n\n// ─── Session Creation ────────────────────────────────────────────────────────\n\nexport interface OAuthOwnerResult {\n owner: string | undefined;\n hasProductionSession: boolean;\n}\n\n/**\n * Determine the token owner from the current session and OAuth state.\n * Call this BEFORE exchangeCode to get the owner parameter.\n */\nexport async function resolveOAuthOwner(\n event: H3Event,\n stateOwner?: string,\n): Promise<OAuthOwnerResult> {\n const existingSession = await getSession(event);\n const hasProductionSession = !!existingSession?.email;\n const owner = hasProductionSession\n ? existingSession!.email\n : stateOwner || undefined;\n\n return { owner, hasProductionSession };\n}\n\nexport interface OAuthSessionResult {\n sessionToken: string | undefined;\n}\n\n/**\n * Create a session token after a successful OAuth exchange.\n *\n * Desktop and mobile apps have separate cookie jars from the system\n * browser, so they always get a fresh session token (even if the browser\n * already has one). The token is then passed via deep link so the native\n * app can inject it.\n */\nexport async function createOAuthSession(\n event: H3Event,\n email: string,\n opts: {\n hasProductionSession: boolean;\n desktop?: boolean;\n trackSignup?: {\n authProvider: string;\n authUserId?: string;\n name?: string | null;\n attribution?: Record<string, string | undefined>;\n signupAnonymousId?: string;\n };\n },\n): Promise<OAuthSessionResult> {\n const mobile = isMobile(event);\n const needsDeepLink = opts.desktop || mobile;\n const maxAge = getSessionMaxAge();\n\n let sessionToken: string | undefined;\n let shouldTrackSignup = false;\n if (!opts.hasProductionSession || needsDeepLink) {\n if (opts.trackSignup && !opts.hasProductionSession) {\n const [hasLegacySession, hasBetterAuthUser] = await Promise.all([\n hasLegacySessionForEmail(email).catch(() => true),\n hasBetterAuthUserEmail(email).catch(() => true),\n ]);\n shouldTrackSignup = !hasLegacySession && !hasBetterAuthUser;\n }\n\n sessionToken = crypto.randomBytes(32).toString(\"hex\");\n await addSession(sessionToken, email);\n setFrameworkSessionCookie(event, sessionToken);\n if (shouldTrackSignup && opts.trackSignup) {\n const attribution =\n opts.trackSignup.attribution ??\n signupAttributionFromCookieHeader(getHeader(event, \"cookie\") ?? null);\n const anonymousId =\n opts.trackSignup.signupAnonymousId ??\n readAnalyticsAnonymousId(getHeader(event, \"cookie\") ?? null);\n await trackSignupEvent({\n authProvider: opts.trackSignup.authProvider,\n authUserId: opts.trackSignup.authUserId,\n email,\n name: opts.trackSignup.name,\n attribution,\n anonymousId,\n });\n }\n // Desktop SSO: record this session in the home-dir broker file so\n // sibling templates (each with its own database) can resolve the\n // same token without a DB row of their own. Only the PRIMARY\n // sign-in writes the broker — if a production session already\n // exists, this is an add-account flow (connecting a secondary\n // Google account for scraping) and must never switch the active\n // user across sibling templates.\n if (opts.desktop && !opts.hasProductionSession) {\n await writeDesktopSso({\n email,\n token: sessionToken,\n expiresAt: Date.now() + maxAge * 1000,\n });\n }\n }\n\n return { sessionToken };\n}\n\n// ─── Callback Responses ──────────────────────────────────────────────────────\n\n/**\n * Return the appropriate response after a successful OAuth callback.\n *\n * Handles mobile deep links, desktop deep links, add-account close-tab\n * pages, and plain web redirects — so templates don't have to.\n */\nexport function oauthCallbackResponse(\n event: H3Event,\n email: string,\n opts: {\n sessionToken?: string;\n desktop?: boolean;\n addAccount?: boolean;\n /**\n * Same-origin path to return the viewer to after a successful web\n * sign-in. Validated via safeReturnPath; falls back to \"/\" for any\n * shape that escapes same-origin. Has no effect on desktop / mobile\n * / add-account flows — those use their own deep-link handling.\n */\n returnUrl?: string;\n flowId?: string;\n appName?: string;\n },\n): Response | string | unknown | Promise<Response | string | unknown> {\n const mobile = isMobile(event);\n const query = getQuery(event);\n const callbackState =\n typeof query.state === \"string\" && query.state.length > 0\n ? query.state\n : undefined;\n\n // Mobile: deep link back to the native app. `isMobile` is UA-only, so this\n // also fires for a plain mobile web browser with no app to handle the deep\n // link — there it no-ops, so the fallback must return to the post-login URL,\n // not the app root (else signed-out visitors land on the homepage).\n if (mobile) {\n const deepLink = buildOAuthCompleteDeepLink(\n opts.sessionToken,\n callbackState,\n );\n const webFallback = appendSessionToOAuthReturnUrl(\n opts.returnUrl,\n opts.sessionToken,\n );\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"><title>Connected</title></head><body style=\"background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0\"><p>Connected! Returning to app…</p><script>window.location.href=${JSON.stringify(deepLink)};setTimeout(function(){window.location.href=${JSON.stringify(webFallback)}},1500)</script></body></html>`,\n );\n }\n\n // Desktop add-account: close-tab page (must come before general desktop check\n // to ensure no deep link fires and the existing session is never switched).\n if (opts.desktop && opts.addAccount) {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const safeAppName = escapeHtml(resolveOAuthAppName(opts.appName));\n const msg = safeEmail ? `Connected ${safeEmail}!` : \"Connected!\";\n return htmlResponse(\n oauthSuccessCloseTabHtml(\n msg,\n `You can close this tab and return to ${safeAppName}.`,\n oauthDebugFlowId(opts.flowId),\n ),\n );\n }\n\n // Electron desktop exchange flow: mail/calendar still pass a flow id so the\n // renderer can poll as a fallback, but the main handoff should use the\n // protocol deep link so the popup returns focus to the desktop app.\n if (opts.desktop && opts.flowId && isElectron(event) && opts.sessionToken) {\n return desktopSuccessPage(event, email, opts.sessionToken, callbackState);\n }\n\n // Desktop exchange flow (non-Electron tray app): the tray app polls the\n // desktop-exchange endpoint for the token — no deep link needed.\n if (opts.desktop && opts.flowId) {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const safeAppName = escapeHtml(resolveOAuthAppName(opts.appName));\n const msg = safeEmail ? `Signed in as ${safeEmail}!` : \"Signed in!\";\n return htmlResponse(\n oauthSuccessCloseTabHtml(\n msg,\n `You can close this tab and return to ${safeAppName}.`,\n oauthDebugFlowId(opts.flowId),\n ),\n );\n }\n\n // Desktop login: deep link back to Electron app — only when the callback\n // request actually carries the AgentNativeDesktop UA marker. Without this\n // check, any client whose OAuth state was minted with `desktop=true` (e.g.\n // a stale link, or an upstream that wrongly set `?desktop=1`) would land\n // on the `agentnative://` page where the deep link can't fire and the\n // \"Open Agent Native\" button does nothing — surfaces inside Builder.io's\n // Fusion webview hit this exact dead-end. Fall through to the web flow\n // for non-Agent-Native-Desktop clients so they get a real redirect.\n if (opts.desktop && isElectron(event)) {\n return desktopSuccessPage(event, email, opts.sessionToken, callbackState);\n }\n\n // Add-account web flow: close-tab page. The email is rendered into the\n // page via DOM `textContent` (safe), but we still JSON-stringify so a\n // payload containing `</script>` can't break out of the script tag —\n // and explicitly assert it's a string so a callbacks like `null` or\n // an object won't end up serialised into the page.\n if (opts.addAccount) {\n const safeEmail = JSON.stringify(typeof email === \"string\" ? email : \"\");\n return htmlResponse(`<!DOCTYPE html><html><body><script>\n window.close();\n var p = document.createElement('p');\n p.style.cssText = 'font-family:system-ui;text-align:center;margin-top:40vh';\n p.textContent = 'Connected ' + ${safeEmail} + '! You can close this tab.';\n document.body.appendChild(p);\n </script></body></html>`);\n }\n\n // Web: redirect to the requested return target. Path-only returns stay\n // same-origin; Builder desktop workspace returns may point back to the\n // local loopback gateway and carry the short-lived `_session` bridge so\n // the local app can promote the newly created hosted OAuth session.\n const location = appendSessionToOAuthReturnUrl(\n opts.returnUrl,\n opts.sessionToken,\n );\n setResponseStatus(event, 302);\n setResponseHeader(event, \"Location\", location);\n setResponseHeader(event, \"Referrer-Policy\", \"no-referrer\");\n // Return a real 302 so the browser lands on the clean return URL instead of\n // lingering on the provider callback URL with its `code`/`state` query\n // params. But h3 hands a non-2xx web `Response` straight back WITHOUT merging\n // the `Set-Cookie` staged earlier in the callback (the framework session\n // cookie), so mirror those staged cookies onto the redirect Response —\n // otherwise the sign-in succeeds but the browser arrives back logged out.\n const headers = new Headers({\n Location: location,\n \"Referrer-Policy\": \"no-referrer\",\n });\n for (const cookie of event.res?.headers?.getSetCookie?.() ?? []) {\n headers.append(\"set-cookie\", cookie);\n }\n return new Response(null, { status: 302, headers });\n}\n\n/** HTML error page for OAuth failures. The message is HTML-escaped — most\n * callers pass `error.message` from a token-exchange or userinfo failure,\n * which can echo upstream provider strings (and historically attacker-\n * controlled query params via the `error_description` field). */\nexport function oauthErrorPage(message: string): Response {\n const safe = escapeHtml(message);\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connection failed</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;text-align:center\"><svg width=\"44\" height=\"44\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ef4444\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-bottom:14px\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M15 9l-6 6\"/><path d=\"M9 9l6 6\"/></svg><p style=\"font-size:16px;margin:0 0 12px 0;color:#ddd\">${safe}</p><p style=\"font-size:13px;color:#888;margin:0\"><a href=\"/\" style=\"color:#888;text-decoration:underline;text-underline-offset:3px\">Back to login</a></p></body></html>`,\n 400,\n );\n}\n\nexport function oauthDesktopExchangePage(\n message = \"Returning to the app...\",\n): Response {\n const safe = escapeHtml(message);\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Returning</title></head><body style=\"background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0\"><p style=\"font-size:14px\">${safe}</p><script>window.close()</script></body></html>`,\n );\n}\n\n// ─── Internal ────────────────────────────────────────────────────────────────\n\nfunction resolveOAuthAppName(explicit?: string): string {\n const raw = explicit || getAppName() || \"Agent Native\";\n if (!/^[a-z0-9_-]+$/.test(raw)) return raw;\n return raw\n .split(/[-_]+/)\n .filter(Boolean)\n .map((word) => word[0].toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\nfunction buildOAuthCompleteDeepLink(\n sessionToken?: string,\n state?: string,\n): string {\n const params = new URLSearchParams();\n if (sessionToken) params.set(\"token\", sessionToken);\n if (state) params.set(\"state\", state);\n const suffix = params.toString();\n return suffix\n ? `agentnative://oauth-complete?${suffix}`\n : \"agentnative://oauth-complete\";\n}\n\nfunction desktopSuccessPage(\n _event: H3Event,\n email?: string,\n sessionToken?: string,\n state?: string,\n): Response {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const msg = safeEmail ? `Connected ${safeEmail}!` : \"Connected!\";\n if (sessionToken) {\n const deepLink = buildOAuthCompleteDeepLink(sessionToken, state);\n const deepLinkJson = JSON.stringify(deepLink);\n // Defence in depth: if this page somehow gets served to a UA that isn't\n // the Agent Native desktop app (server gate bypassed, stale link, etc.),\n // skip the `agentnative://` deep link entirely and bounce to the app\n // root. The deep link silently fails outside the desktop app and the\n // \"Open Agent Native\" button is a dead end in a generic browser/webview.\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title><style>@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.spinner{width:28px;height:28px;border:2px solid #333;border-top-color:#fff;border-radius:50%;animation:spin .8s linear infinite}.fallback{display:none;flex-direction:column;align-items:center;gap:8px;animation:fadeIn .2s ease-out}.fallback.show{display:flex}</style></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:16px\"><p style=\"font-size:16px;margin:0\">${msg}</p><div id=\"loading\" class=\"spinner\"></div><div id=\"fallback\" class=\"fallback\"><a href=${deepLinkJson} style=\"display:inline-block;padding:10px 24px;background:#fff;color:#000;border-radius:8px;text-decoration:none;font-size:14px;font-weight:500\">Open Agent Native</a><p style=\"font-size:12px;color:#666;margin:0\">If the app didn\\u2019t open automatically, click the button above.</p></div><script>(function(){var ua=(navigator.userAgent||\"\");if(ua.indexOf(\"AgentNativeDesktop\")===-1){window.location.replace(\"/\");return}window.location.href=${deepLinkJson};setTimeout(function(){document.getElementById(\"loading\").style.display=\"none\";document.getElementById(\"fallback\").classList.add(\"show\")},3000)})()</script></body></html>`,\n );\n }\n return htmlResponse(\n oauthSuccessCloseTabHtml(\n msg,\n \"You can close this tab and return to Agent Native.\",\n ),\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding-html.d.ts","sourceRoot":"","sources":["../../src/server/onboarding-html.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyBH,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAqrC/B,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAmBD,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE;QACV,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,wBAAwB,GAAG,KAAK,CAAC;IACrD;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,qBAA0B,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"onboarding-html.d.ts","sourceRoot":"","sources":["../../src/server/onboarding-html.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyBH,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAqrC/B,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAmBD,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE;QACV,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,wBAAwB,GAAG,KAAK,CAAC;IACrD;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,qBAA0B,GAAG,MAAM,CA0hF1E;AAED,kDAAkD;AAClD,eAAO,MAAM,eAAe,QAAsB,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CA0G7C"}
|
|
@@ -2668,6 +2668,23 @@ ${identitySsoScript}
|
|
|
2668
2668
|
function __anSafeAttributionValue(value) {
|
|
2669
2669
|
return typeof value === 'string' ? value.trim().slice(0, 120) : '';
|
|
2670
2670
|
}
|
|
2671
|
+
function __anGenerateAnalyticsAnonymousId() {
|
|
2672
|
+
try {
|
|
2673
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID();
|
|
2674
|
+
} catch(e) {}
|
|
2675
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
|
|
2676
|
+
}
|
|
2677
|
+
function __anSyncAnalyticsAnonymousId() {
|
|
2678
|
+
try {
|
|
2679
|
+
var anonymousId = '';
|
|
2680
|
+
try { anonymousId = localStorage.getItem('agent-native.anonymous_id') || ''; } catch(e) {}
|
|
2681
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(anonymousId)) {
|
|
2682
|
+
anonymousId = __anGenerateAnalyticsAnonymousId();
|
|
2683
|
+
try { localStorage.setItem('agent-native.anonymous_id', anonymousId); } catch(e) {}
|
|
2684
|
+
}
|
|
2685
|
+
document.cookie = 'an_aid=' + encodeURIComponent(anonymousId) + '; path=/; max-age=2592000; SameSite=Lax';
|
|
2686
|
+
} catch(e) {}
|
|
2687
|
+
}
|
|
2671
2688
|
function __anFirstTouchCookiePresent() {
|
|
2672
2689
|
try {
|
|
2673
2690
|
return document.cookie.split(';').some(function(part) {
|
|
@@ -2717,6 +2734,7 @@ ${identitySsoScript}
|
|
|
2717
2734
|
__anWriteFirstTouchCookie(json);
|
|
2718
2735
|
} catch(e) {}
|
|
2719
2736
|
}
|
|
2737
|
+
__anSyncAnalyticsAnonymousId();
|
|
2720
2738
|
__anCaptureSignupAttribution();
|
|
2721
2739
|
var __anBuilderPreviewSeen = false;
|
|
2722
2740
|
function __anRememberBuilderPreview() {
|