@agent-native/core 0.92.11 → 0.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +45 -0
- package/corpus/core/docs/content/automation-connectors.mdx +80 -0
- package/corpus/core/docs/content/extensions.mdx +8 -0
- package/corpus/core/docs/content/locales/ar-SA/extensions.mdx +8 -0
- package/corpus/core/docs/content/locales/ar-SA/messaging.mdx +84 -15
- package/corpus/core/docs/content/locales/ar-SA/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/de-DE/extensions.mdx +10 -0
- package/corpus/core/docs/content/locales/de-DE/messaging.mdx +92 -15
- package/corpus/core/docs/content/locales/de-DE/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/es-ES/extensions.mdx +10 -0
- package/corpus/core/docs/content/locales/es-ES/messaging.mdx +91 -15
- package/corpus/core/docs/content/locales/es-ES/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/fr-FR/extensions.mdx +10 -0
- package/corpus/core/docs/content/locales/fr-FR/messaging.mdx +91 -15
- package/corpus/core/docs/content/locales/fr-FR/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/hi-IN/extensions.mdx +8 -0
- package/corpus/core/docs/content/locales/hi-IN/messaging.mdx +81 -15
- package/corpus/core/docs/content/locales/hi-IN/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/ja-JP/extensions.mdx +9 -0
- package/corpus/core/docs/content/locales/ja-JP/messaging.mdx +81 -15
- package/corpus/core/docs/content/locales/ja-JP/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/ko-KR/extensions.mdx +8 -0
- package/corpus/core/docs/content/locales/ko-KR/messaging.mdx +80 -15
- package/corpus/core/docs/content/locales/ko-KR/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/pt-BR/extensions.mdx +9 -0
- package/corpus/core/docs/content/locales/pt-BR/messaging.mdx +88 -15
- package/corpus/core/docs/content/locales/pt-BR/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/zh-CN/extensions.mdx +6 -0
- package/corpus/core/docs/content/locales/zh-CN/messaging.mdx +78 -15
- package/corpus/core/docs/content/locales/zh-CN/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +1 -0
- package/corpus/core/docs/content/locales/zh-TW/extensions.mdx +6 -0
- package/corpus/core/docs/content/locales/zh-TW/messaging.mdx +78 -15
- package/corpus/core/docs/content/locales/zh-TW/template-clips.mdx +1 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +1 -0
- package/corpus/core/docs/content/messaging.mdx +227 -45
- package/corpus/core/docs/content/template-clips.mdx +3 -0
- package/corpus/core/docs/content/template-design.mdx +4 -0
- package/corpus/core/package.json +5 -1
- package/corpus/core/src/a2a/artifact-response.ts +131 -2
- package/corpus/core/src/a2a/client.ts +10 -0
- package/corpus/core/src/a2a/handlers.ts +40 -76
- package/corpus/core/src/a2a/types.ts +2 -0
- package/corpus/core/src/agent/durable-background.ts +24 -0
- package/corpus/core/src/agent/production-agent.ts +48 -5
- package/corpus/core/src/agent/run-manager.ts +171 -20
- package/corpus/core/src/agent/run-store.ts +297 -42
- package/corpus/core/src/agent/types.ts +23 -0
- package/corpus/core/src/audit/record.ts +31 -0
- package/corpus/core/src/audit/store.ts +69 -3
- package/corpus/core/src/audit/types.ts +13 -0
- package/corpus/core/src/automation/index.ts +802 -0
- package/corpus/core/src/cli/create.ts +12 -5
- package/corpus/core/src/cli/design-connect.ts +596 -36
- package/corpus/core/src/client/AgentPanel.tsx +51 -6
- package/corpus/core/src/client/AssistantChat.tsx +274 -147
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +3 -0
- package/corpus/core/src/client/RunStuckBanner.tsx +69 -20
- package/corpus/core/src/client/agent-chat-adapter.ts +110 -17
- package/corpus/core/src/client/automation.ts +22 -0
- package/corpus/core/src/client/chat/run-recovery.tsx +67 -50
- package/corpus/core/src/client/composer/TiptapComposer.tsx +8 -0
- package/corpus/core/src/client/index.ts +30 -0
- package/corpus/core/src/client/integrations/api.ts +295 -0
- package/corpus/core/src/client/integrations/index.ts +26 -0
- package/corpus/core/src/client/transcription/use-live-transcription.ts +68 -5
- package/corpus/core/src/client/use-run-stuck-detection.ts +41 -3
- package/corpus/core/src/deploy/build.ts +59 -3
- package/corpus/core/src/deploy/workspace-deploy.ts +37 -2
- package/corpus/core/src/file-upload/types.ts +2 -0
- package/corpus/core/src/index.ts +37 -0
- package/corpus/core/src/integrations/a2a-continuations-store.ts +6 -4
- package/corpus/core/src/integrations/adapters/discord.ts +362 -0
- package/corpus/core/src/integrations/adapters/index.ts +29 -0
- package/corpus/core/src/integrations/adapters/microsoft-teams.ts +411 -0
- package/corpus/core/src/integrations/adapters/slack.ts +861 -27
- package/corpus/core/src/integrations/adapters/telegram.ts +54 -15
- package/corpus/core/src/integrations/adapters/whatsapp.ts +41 -17
- package/corpus/core/src/integrations/catalog.ts +571 -0
- package/corpus/core/src/integrations/controls-store.ts +187 -0
- package/corpus/core/src/integrations/index.ts +100 -0
- package/corpus/core/src/integrations/installations-store.ts +677 -0
- package/corpus/core/src/integrations/integration-memory.ts +187 -0
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +6 -6
- package/corpus/core/src/integrations/pending-tasks-store.ts +83 -5
- package/corpus/core/src/integrations/plugin.ts +685 -47
- package/corpus/core/src/integrations/scope-store.ts +498 -0
- package/corpus/core/src/integrations/slack-manifest.ts +78 -0
- package/corpus/core/src/integrations/slack-oauth.ts +343 -0
- package/corpus/core/src/integrations/types.ts +179 -0
- package/corpus/core/src/integrations/usage-budget-store.ts +906 -0
- package/corpus/core/src/integrations/webhook-handler.ts +562 -51
- package/corpus/core/src/jobs/scheduler.ts +100 -2
- package/corpus/core/src/jobs/tools.ts +45 -9
- package/corpus/core/src/provider-api/index.ts +72 -1
- package/corpus/core/src/resources/handlers.ts +88 -14
- package/corpus/core/src/resources/script-helpers.ts +36 -7
- package/corpus/core/src/resources/store.ts +59 -52
- package/corpus/core/src/scripts/call-agent.ts +107 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +294 -119
- package/corpus/core/src/server/auth.ts +10 -14
- package/corpus/core/src/server/builder-design-systems.ts +16 -1
- package/corpus/core/src/server/edge.ts +18 -0
- package/corpus/core/src/server/index.ts +26 -0
- package/corpus/core/src/server/request-context.ts +18 -0
- package/corpus/core/src/server/social-og-image.ts +3 -2
- package/corpus/core/src/styles/agent-native.css +62 -44
- package/corpus/core/src/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/core/src/templates/default/AGENTS.md +7 -3
- package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/core/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
- package/corpus/core/src/templates/workspace-core/AGENTS.md +3 -2
- package/corpus/core/src/usage/store.ts +37 -2
- package/corpus/templates/analytics/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +23 -0
- package/corpus/templates/analytics/.builder/skills/provider-api/SKILL.md +15 -0
- package/corpus/templates/analytics/actions/save-monitor.ts +25 -2
- package/corpus/templates/analytics/app/lib/data-sources.ts +28 -0
- package/corpus/templates/analytics/changelog/2026-07-10-agent-created-uptime-monitors-now-return-a-direct-link-to-th.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-dashboard-email-reports-still-arrive-when-screenshot-capture-temporarily-fails.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +3 -7
- package/corpus/templates/analytics/server/lib/credential-keys.ts +12 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +5 -1
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +2 -0
- package/corpus/templates/analytics/server/routes/api/test-connection.post.ts +16 -0
- package/corpus/templates/assets/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/brain/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/calendar/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/calendar/app/components/calendar/CommandPalette.tsx +98 -108
- package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +97 -90
- package/corpus/templates/calendar/changelog/2026-07-10-event-guest-details-and-option-menus-now-use-valid-accessibl.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-10-natural-language-event-phrases-stay-available-as-quick-creat.md +6 -0
- package/corpus/templates/chat/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
- package/corpus/templates/clips/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +17 -1
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +8 -0
- package/corpus/templates/clips/AGENTS.md +8 -3
- package/corpus/templates/clips/actions/finalize-recording.ts +63 -23
- package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -2
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +72 -9
- package/corpus/templates/clips/actions/list-recordings.ts +19 -3
- package/corpus/templates/clips/actions/navigate.ts +2 -0
- package/corpus/templates/clips/actions/reprocess-recording.ts +20 -6
- package/corpus/templates/clips/actions/request-transcript.ts +16 -31
- package/corpus/templates/clips/actions/view-screen.ts +57 -5
- package/corpus/templates/clips/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/clips/app/components/library/empty-state.tsx +2 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +69 -42
- package/corpus/templates/clips/app/components/library/library-layout.tsx +15 -1
- package/corpus/templates/clips/app/components/library/recording-card.tsx +81 -68
- package/corpus/templates/clips/app/components/library/search-bar.tsx +4 -3
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +9 -3
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +1 -1
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +27 -5
- package/corpus/templates/clips/app/hooks/use-library.ts +1 -1
- package/corpus/templates/clips/app/hooks/use-navigation-state.ts +10 -2
- package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
- package/corpus/templates/clips/app/i18n/ar-SA.ts +7 -1
- package/corpus/templates/clips/app/i18n/de-DE.ts +7 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -1
- package/corpus/templates/clips/app/i18n/es-ES.ts +7 -1
- package/corpus/templates/clips/app/i18n/fr-FR.ts +7 -1
- package/corpus/templates/clips/app/i18n/hi-IN.ts +7 -1
- package/corpus/templates/clips/app/i18n/ja-JP.ts +7 -1
- package/corpus/templates/clips/app/i18n/ko-KR.ts +7 -1
- package/corpus/templates/clips/app/i18n/pt-BR.ts +7 -1
- package/corpus/templates/clips/app/i18n/zh-CN.ts +8 -1
- package/corpus/templates/clips/app/i18n/zh-TW.ts +8 -1
- package/corpus/templates/clips/app/lib/recording-visibility.ts +70 -0
- package/corpus/templates/clips/app/routes/_app.shared.tsx +20 -0
- package/corpus/templates/clips/app/routes/download.tsx +24 -8
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +115 -2
- package/corpus/templates/clips/app/routes/record.tsx +62 -0
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +25 -16
- package/corpus/templates/clips/changelog/2026-07-10-clips-desktop-is-now-available-for-linux-with-appimage-debia.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-clips-downloads-updates-sooner-in-the-background-and-offers-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-clips-shared-with-you-now-appear-in-a-searchable-shared-with.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-clips-with-missing-mobile-video-frames-can-be-repaired-witho.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-mobile-camera-recordings-pause-while-backgrounded.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-mp4-downloads-now-show-progress-immediately-after-the-option.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-recording-pages-now-open-as-soon-as-you-stop-with-a-share-li.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-recordings-now-seek-smoothly-downloads-use-the-correct-file-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-s3-compatible-storage-now-saves-hosted-recordings-without-re.md +6 -0
- package/corpus/templates/clips/desktop/README.md +22 -6
- package/corpus/templates/clips/desktop/package.json +1 -1
- package/corpus/templates/clips/desktop/src/app.tsx +32 -19
- package/corpus/templates/clips/desktop/src/components/ReadinessPanel.tsx +15 -9
- package/corpus/templates/clips/desktop/src/lib/finalization-guard.ts +22 -0
- package/corpus/templates/clips/desktop/src/lib/permissions.ts +3 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +97 -120
- package/corpus/templates/clips/desktop/src/lib/updater.ts +26 -4
- package/corpus/templates/clips/desktop/src/overlays/finalizing.tsx +4 -8
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +2 -2
- package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +11 -7
- package/corpus/templates/clips/desktop/src-tauri/tauri.linux.conf.json +11 -0
- package/corpus/templates/clips/netlify.toml +3 -0
- package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +106 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +9 -1
- package/corpus/templates/clips/server/lib/resumable-upload-provider.ts +52 -0
- package/corpus/templates/clips/server/lib/s3-upload-provider.ts +433 -85
- package/corpus/templates/clips/server/lib/video-remux.ts +114 -0
- package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +109 -0
- package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +22 -11
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +4 -2
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +47 -5
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +10 -2
- package/corpus/templates/clips/shared/share-loader-response.ts +12 -0
- package/corpus/templates/clips/shared/transcript-status.ts +40 -0
- package/corpus/templates/content/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/content/.agents/skills/content/SKILL.md +57 -5
- package/corpus/templates/content/AGENTS.md +65 -55
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +92 -23
- package/corpus/templates/content/actions/_database-source-utils.ts +207 -41
- package/corpus/templates/content/actions/_property-utils.ts +30 -2
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +316 -121
- package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
- package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
- package/corpus/templates/content/actions/submit-content-database-form.ts +497 -0
- package/corpus/templates/content/actions/suggest-source-join-key.ts +8 -1
- package/corpus/templates/content/actions/update-content-database-view.ts +8 -1
- package/corpus/templates/content/actions/view-screen.ts +4 -1
- package/corpus/templates/content/agent-native.app-skill.json +1 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +250 -64
- package/corpus/templates/content/app/components/editor/database/FormView.tsx +460 -0
- package/corpus/templates/content/app/components/editor/database/navigation-state.ts +3 -0
- package/corpus/templates/content/app/components/editor/database/settings.tsx +30 -37
- package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
- package/corpus/templates/content/app/components/editor/database/view-config.ts +29 -1
- package/corpus/templates/content/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +22 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +21 -0
- package/corpus/templates/content/app/i18n-data.ts +225 -0
- package/corpus/templates/content/changelog/2026-07-09-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-builder-backed-properties-now-load-current-source-values-as-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-builder-database-refreshes-now-stay-explicit-and-accurate.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-database-form-views-now-collect-ordered-required-answers-and.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-database-tables-now-use-a-quieter-more-compact-layout-with-c.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-slack-intake-routes-to-content-database-forms.md +6 -0
- package/corpus/templates/content/parity/matrix.md +1 -0
- package/corpus/templates/content/parity/matrix.ts +18 -0
- package/corpus/templates/content/shared/api.ts +28 -1
- package/corpus/templates/content/shared/database-form.ts +53 -0
- package/corpus/templates/design/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +26 -0
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +85 -12
- package/corpus/templates/design/AGENTS.md +72 -18
- package/corpus/templates/design/DESIGN.md +1 -1
- package/corpus/templates/design/DEVELOPING.md +1 -1
- package/corpus/templates/design/FIGMA_INTEROPERABILITY.md +116 -0
- package/corpus/templates/design/actions/create-design-from-template.ts +220 -0
- package/corpus/templates/design/actions/create-design-system.ts +92 -16
- package/corpus/templates/design/actions/delete-design-template.ts +28 -0
- package/corpus/templates/design/actions/delete-file.ts +7 -0
- package/corpus/templates/design/actions/edit-design.ts +3 -0
- package/corpus/templates/design/actions/export-design-as-figma-svg.ts +137 -5
- package/corpus/templates/design/actions/export-pdf.ts +1 -1
- package/corpus/templates/design/actions/generate-design.ts +3 -0
- package/corpus/templates/design/actions/get-design-snapshot.ts +9 -0
- package/corpus/templates/design/actions/get-figma-connection-status.ts +26 -0
- package/corpus/templates/design/actions/get-figma-design-context.ts +138 -0
- package/corpus/templates/design/actions/get-figma-styles.ts +2 -2
- package/corpus/templates/design/actions/import-figma-clipboard.ts +112 -16
- package/corpus/templates/design/actions/import-figma-frame.ts +15 -4
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +4 -0
- package/corpus/templates/design/actions/list-design-templates.ts +118 -0
- package/corpus/templates/design/actions/navigate.ts +16 -2
- package/corpus/templates/design/actions/provider-api-request.ts +11 -1
- package/corpus/templates/design/actions/save-design-as-template.ts +156 -0
- package/corpus/templates/design/actions/set-active-breakpoint.ts +13 -4
- package/corpus/templates/design/actions/take-design-screenshot.ts +73 -3
- package/corpus/templates/design/actions/update-file.ts +39 -22
- package/corpus/templates/design/actions/view-screen.ts +36 -1
- package/corpus/templates/design/actions/write-local-file.ts +50 -38
- package/corpus/templates/design/app/components/design/AutoLayoutSuggestionDialog.tsx +151 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +20 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +251 -22
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +3 -3
- package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +384 -9
- package/corpus/templates/design/app/components/design/EditPanel.tsx +105 -32
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +58 -28
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +6 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +539 -53
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +40 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +1 -0
- package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +1 -0
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +5 -1
- package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +2 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +3 -1
- package/corpus/templates/design/app/components/design/code-workbench/workspace/types.ts +2 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +19 -0
- package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +1 -0
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +27 -0
- package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +137 -81
- package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +25 -5
- package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +1 -1
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +220 -3
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +13 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +22 -1
- package/corpus/templates/design/app/components/design/types.ts +4 -0
- package/corpus/templates/design/app/components/design-system/ProductionDesignSystemShowcase.tsx +208 -0
- package/corpus/templates/design/app/components/editor/FigmaLinkComposerBubble.tsx +321 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +24 -4
- package/corpus/templates/design/app/components/layout/Layout.tsx +16 -2
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -0
- package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +72 -0
- package/corpus/templates/design/app/global.css +2 -35
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +10 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +127 -5
- package/corpus/templates/design/app/i18n-breakpoints.ts +209 -0
- package/corpus/templates/design/app/i18n-data.ts +1317 -59
- package/corpus/templates/design/app/i18n-template-feature.ts +458 -0
- package/corpus/templates/design/app/lib/design-clipboard.ts +202 -0
- package/corpus/templates/design/app/lib/design-file-upload.ts +87 -0
- package/corpus/templates/design/app/lib/design-import.ts +168 -11
- package/corpus/templates/design/app/lib/desktop-design-preview.ts +423 -0
- package/corpus/templates/design/app/lib/figma-clipboard.ts +74 -8
- package/corpus/templates/design/app/lib/figma-connection.ts +161 -0
- package/corpus/templates/design/app/lib/figma-svg-copy.ts +814 -22
- package/corpus/templates/design/app/lib/figma-url.ts +77 -0
- package/corpus/templates/design/app/lib/pending-generation.ts +35 -0
- package/corpus/templates/design/app/pages/DesignSystems.tsx +236 -177
- package/corpus/templates/design/app/pages/Index.tsx +38 -0
- package/corpus/templates/design/app/pages/Templates.tsx +498 -0
- package/corpus/templates/design/app/pages/design-editor/auto-layout-suggestion.ts +328 -0
- package/corpus/templates/design/app/pages/design-editor/clone-idrefs.ts +113 -0
- package/corpus/templates/design/app/pages/design-editor/created-screen-navigation.ts +55 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +32 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +49 -2
- package/corpus/templates/design/app/pages/design-editor/export-capture.ts +296 -0
- package/corpus/templates/design/app/pages/design-editor/history.ts +101 -2
- package/corpus/templates/design/app/pages/design-editor/layout-operations.ts +28 -1
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +34 -0
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +202 -6
- package/corpus/templates/design/app/pages/design-editor/pending-structure-verification.ts +92 -0
- package/corpus/templates/design/app/pages/design-editor/save-failure.ts +58 -0
- package/corpus/templates/design/app/routes/templates.tsx +4 -6
- package/corpus/templates/design/changelog/2026-07-10-app-workspaces-now-use-clean-borderless-main-surfaces-throug.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-ask-chat-what-s-in-a-figma-file-or-frame-structure-screensho.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-canvas-edits-now-reject-malformed-html-before-it-can-corrupt.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-complex-designs-now-export-as-valid-secure-svgs-and-crisp-ra.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-complex-figma-masks-arcs-rich-text-transformed-images-and-ad.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-copied-layers-paste-across-designs-and-browser-tabs.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-copy-and-download-editable-figma-svg.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-copying-design-text-now-pastes-readable-text-into-other-apps.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-deleted-screens-can-be-restored-with-undo-and-removed-again-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-design-changes-now-save-before-switching-apps-and-retry-when.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-design-stays-usable-on-phones-by-keeping-editor-panels-and-f.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-download-all-screens-as-one-multi-page-pdf-from-the-overview.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-duplicated-layers-now-keep-labels-aria-relationships-fragmen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-fig-import-results-stay-compact.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-figma-imports-now-load-real-fonts-fix-rotated-gradient-dashe.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-figma-links-in-chat-and-the-import-tab-now-offer-secure-one-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-fixed-cmd-z-not-restoring-content-after-an-agent-driven-desi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-fixed-download-pdf-being-unreachable-from-the-export-panel-e.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-fixed-dragging-a-layer-out-of-an-auto-layout-row-column-no-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-fixed-figma-svg-export-text-wrapping-sizing-and-inner-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-fixed-hiding-or-locking-one-layer-no-longer-hides-or-blocks-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-fixed-png-screenshots-of-tall-screens-being-cropped-to-one-v.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-fixed-png-svg-exports-capturing-broken-layout-when-webfonts-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-frame-tool-now-offers-standard-ad-unit-size-presets-medium-r.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-full-view-controls-stay-within-their-frame-instead-of-coveri.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-hover-focus-focus-visible-pressed-and-disabled-states-now-pr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-layer-drops-now-land-in-the-intended-grid-row-stay-visually-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-local-code-workspaces-now-keep-unsaved-edits-across-panel-sw.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-local-visual-editing-now-keeps-authenticated-sessions-shared.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-long-running-agent-work-no-longer-shows-a-premature-stuck-wa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-managed-figma-connections-are-recognized-automatically.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-new-designs-can-open-directly-in-the-editor-without-a-pr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-new-screens-are-selected-and-revealed-immediately.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-new-text-layers-undo-as-one-safe-creation.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-preview-and-apply-inferred-auto-layout-without-changing-yo.md +5 -0
- package/corpus/templates/design/changelog/2026-07-10-print-pdf-exports-and-paper-size-presets-letter-a4-now-match.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-responsive-frame-editing.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-reusable-design-templates.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-screen-overview-frames-now-use-a-clearer-interact-action-wit.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-start-designs-from-source-linked-material-3-carbon-or-primer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-tweaks-guidance.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-uploaded-fig-files-can-now-become-editable-design-screens-wi.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +3 -3
- package/corpus/templates/design/package.json +2 -0
- package/corpus/templates/design/scripts/qa-figma-stress-import.ts +96 -0
- package/corpus/templates/design/server/db/index.ts +10 -0
- package/corpus/templates/design/server/db/schema.ts +44 -0
- package/corpus/templates/design/server/handlers/import-design-file.ts +114 -80
- package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +19 -6
- package/corpus/templates/design/server/lib/design-export.ts +240 -3
- package/corpus/templates/design/server/lib/design-template-data.ts +89 -0
- package/corpus/templates/design/server/lib/design-to-figma-svg.ts +250 -26
- package/corpus/templates/design/server/lib/fig-file-decoder.ts +839 -0
- package/corpus/templates/design/server/lib/fig-file-import.ts +287 -0
- package/corpus/templates/design/server/lib/fig-file-limits.ts +3 -0
- package/corpus/templates/design/server/lib/fig-file-to-html.ts +2568 -0
- package/corpus/templates/design/server/lib/figma-design-context.ts +359 -0
- package/corpus/templates/design/server/lib/figma-node-import.ts +478 -22
- package/corpus/templates/design/server/lib/figma-node-to-html.ts +554 -29
- package/corpus/templates/design/server/lib/local-figma-qa-upload.ts +112 -0
- package/corpus/templates/design/server/lib/verify-write-grant.ts +16 -2
- package/corpus/templates/design/server/lib/visible-clipboard-html.ts +13 -3
- package/corpus/templates/design/server/plugins/agent-chat.ts +7 -0
- package/corpus/templates/design/server/plugins/db.ts +42 -0
- package/corpus/templates/design/server/plugins/local-figma-qa-upload.ts +7 -0
- package/corpus/templates/design/server/register-secrets.ts +8 -8
- package/corpus/templates/design/server/routes/api/qa-figma-import-assets/[assetId].get.ts +46 -0
- package/corpus/templates/design/server/source-workspace.ts +104 -12
- package/corpus/templates/design/shared/code-layer.ts +105 -28
- package/corpus/templates/design/shared/design-system-templates.ts +301 -0
- package/corpus/templates/design/shared/design-template-presets.ts +195 -0
- package/corpus/templates/design/shared/figma-url.ts +8 -0
- package/corpus/templates/design/shared/html-integrity.ts +306 -0
- package/corpus/templates/design/shared/interaction-states.ts +318 -23
- package/corpus/templates/design/shared/locked-layers.ts +151 -0
- package/corpus/templates/dispatch/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/dispatch/app/i18n/zh-TW.ts +48 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +536 -1
- package/corpus/templates/dispatch/changelog/2026-07-10-connect-slack-workspaces-with-oauth-and-manage-channel-ident.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-10-microsoft-teams-bots-and-discord-slash-commands-can-now-conn.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-10-slack-and-telegram-now-route-uptime-monitor-requests-to-anal.md +6 -0
- package/corpus/templates/forms/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +7 -0
- package/corpus/templates/forms/AGENTS.md +4 -0
- package/corpus/templates/forms/actions/create-form.ts +44 -7
- package/corpus/templates/forms/actions/lib/assert-publishable-form.ts +38 -0
- package/corpus/templates/forms/actions/patch-form-fields.ts +4 -0
- package/corpus/templates/forms/actions/update-form.ts +2 -36
- package/corpus/templates/forms/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/forms/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/forms/app/i18n/en-US.ts +3 -0
- package/corpus/templates/forms/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/forms/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/forms/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/forms/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/forms/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/forms/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +17 -0
- package/corpus/templates/forms/changelog/2026-07-10-agent-created-published-forms-now-return-the-anonymous-publi.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-10-published-forms-can-no-longer-be-edited-into-an-unusable-emp.md +6 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +4 -3
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/forms/shared/types.ts +5 -0
- package/corpus/templates/mail/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
- package/corpus/templates/mail/changelog/2026-07-10-mail-navigation-now-uses-a-clean-borderless-drawer.md +6 -0
- package/corpus/templates/plan/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/corpus/templates/plan/app/lib/plan-local-bridge.ts +4 -1
- package/corpus/templates/plan/changelog/2026-07-10-local-plans-now-keep-valid-content-visible-when-one-generate.md +6 -0
- package/corpus/templates/plan/server/lib/local-plan-files.ts +4 -1
- package/corpus/templates/slides/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/dist/a2a/artifact-response.d.ts.map +1 -1
- package/dist/a2a/artifact-response.js +88 -2
- package/dist/a2a/artifact-response.js.map +1 -1
- package/dist/a2a/client.d.ts +9 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +1 -0
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +24 -69
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/types.d.ts +2 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/agent/durable-background.d.ts +11 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +17 -0
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts +5 -3
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +34 -8
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +44 -2
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +124 -18
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +109 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +274 -37
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/types.d.ts +22 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/audit/record.d.ts.map +1 -1
- package/dist/audit/record.js +29 -0
- package/dist/audit/record.js.map +1 -1
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +62 -3
- package/dist/audit/store.js.map +1 -1
- package/dist/audit/types.d.ts +13 -0
- package/dist/audit/types.d.ts.map +1 -1
- package/dist/audit/types.js.map +1 -1
- package/dist/automation/index.d.ts +193 -0
- package/dist/automation/index.d.ts.map +1 -0
- package/dist/automation/index.js +464 -0
- package/dist/automation/index.js.map +1 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +10 -3
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +492 -35
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +7 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +32 -8
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +15 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +115 -47
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts +14 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +35 -7
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +4 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +97 -16
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/automation.d.ts +14 -0
- package/dist/client/automation.d.ts.map +1 -0
- package/dist/client/automation.js +9 -0
- package/dist/client/automation.js.map +1 -0
- package/dist/client/chat/run-recovery.d.ts +4 -0
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +12 -10
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +8 -0
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/integrations/api.d.ts +115 -0
- package/dist/client/integrations/api.d.ts.map +1 -0
- package/dist/client/integrations/api.js +123 -0
- package/dist/client/integrations/api.js.map +1 -0
- package/dist/client/integrations/index.d.ts +1 -0
- package/dist/client/integrations/index.d.ts.map +1 -1
- package/dist/client/integrations/index.js +1 -0
- package/dist/client/integrations/index.js.map +1 -1
- package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
- package/dist/client/transcription/use-live-transcription.js +59 -5
- package/dist/client/transcription/use-live-transcription.js.map +1 -1
- package/dist/client/use-run-stuck-detection.d.ts +16 -1
- package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
- package/dist/client/use-run-stuck-detection.js +23 -4
- package/dist/client/use-run-stuck-detection.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +49 -4
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/workspace-deploy.d.ts.map +1 -1
- package/dist/deploy/workspace-deploy.js +31 -2
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/file-upload/types.d.ts +2 -0
- package/dist/file-upload/types.d.ts.map +1 -1
- package/dist/file-upload/types.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +6 -4
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/adapters/discord.d.ts +3 -0
- package/dist/integrations/adapters/discord.d.ts.map +1 -0
- package/dist/integrations/adapters/discord.js +261 -0
- package/dist/integrations/adapters/discord.js.map +1 -0
- package/dist/integrations/adapters/index.d.ts +4 -0
- package/dist/integrations/adapters/index.d.ts.map +1 -0
- package/dist/integrations/adapters/index.js +26 -0
- package/dist/integrations/adapters/index.js.map +1 -0
- package/dist/integrations/adapters/microsoft-teams.d.ts +5 -0
- package/dist/integrations/adapters/microsoft-teams.d.ts.map +1 -0
- package/dist/integrations/adapters/microsoft-teams.js +295 -0
- package/dist/integrations/adapters/microsoft-teams.js.map +1 -0
- package/dist/integrations/adapters/slack.d.ts +8 -2
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +736 -27
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/adapters/telegram.d.ts +0 -2
- package/dist/integrations/adapters/telegram.d.ts.map +1 -1
- package/dist/integrations/adapters/telegram.js +49 -15
- package/dist/integrations/adapters/telegram.js.map +1 -1
- package/dist/integrations/adapters/whatsapp.d.ts +0 -2
- package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
- package/dist/integrations/adapters/whatsapp.js +40 -17
- package/dist/integrations/adapters/whatsapp.js.map +1 -1
- package/dist/integrations/catalog.d.ts +75 -0
- package/dist/integrations/catalog.d.ts.map +1 -0
- package/dist/integrations/catalog.js +451 -0
- package/dist/integrations/catalog.js.map +1 -0
- package/dist/integrations/controls-store.d.ts +44 -0
- package/dist/integrations/controls-store.d.ts.map +1 -0
- package/dist/integrations/controls-store.js +129 -0
- package/dist/integrations/controls-store.js.map +1 -0
- package/dist/integrations/index.d.ts +13 -1
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +9 -0
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/installations-store.d.ts +112 -0
- package/dist/integrations/installations-store.d.ts.map +1 -0
- package/dist/integrations/installations-store.js +493 -0
- package/dist/integrations/installations-store.js.map +1 -0
- package/dist/integrations/integration-memory.d.ts +24 -0
- package/dist/integrations/integration-memory.d.ts.map +1 -0
- package/dist/integrations/integration-memory.js +136 -0
- package/dist/integrations/integration-memory.js.map +1 -0
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +6 -5
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts +17 -0
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +71 -5
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +520 -35
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/scope-store.d.ts +65 -0
- package/dist/integrations/scope-store.d.ts.map +1 -0
- package/dist/integrations/scope-store.js +361 -0
- package/dist/integrations/scope-store.js.map +1 -0
- package/dist/integrations/slack-manifest.d.ts +66 -0
- package/dist/integrations/slack-manifest.d.ts.map +1 -0
- package/dist/integrations/slack-manifest.js +70 -0
- package/dist/integrations/slack-manifest.js.map +1 -0
- package/dist/integrations/slack-oauth.d.ts +99 -0
- package/dist/integrations/slack-oauth.d.ts.map +1 -0
- package/dist/integrations/slack-oauth.js +226 -0
- package/dist/integrations/slack-oauth.js.map +1 -0
- package/dist/integrations/types.d.ts +144 -0
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js +16 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/integrations/usage-budget-store.d.ts +84 -0
- package/dist/integrations/usage-budget-store.d.ts.map +1 -0
- package/dist/integrations/usage-budget-store.js +650 -0
- package/dist/integrations/usage-budget-store.js.map +1 -0
- package/dist/integrations/webhook-handler.d.ts +4 -0
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +415 -47
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/jobs/scheduler.d.ts +6 -0
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +83 -4
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/jobs/tools.d.ts.map +1 -1
- package/dist/jobs/tools.js +43 -11
- package/dist/jobs/tools.js.map +1 -1
- package/dist/provider-api/index.d.ts +4 -4
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +72 -1
- package/dist/provider-api/index.js.map +1 -1
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +57 -15
- package/dist/resources/handlers.js.map +1 -1
- package/dist/resources/script-helpers.d.ts.map +1 -1
- package/dist/resources/script-helpers.js +34 -8
- package/dist/resources/script-helpers.js.map +1 -1
- package/dist/resources/store.d.ts +10 -0
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +54 -47
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +106 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +15 -4
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +248 -113
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +10 -14
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts +3 -1
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +9 -2
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/edge.d.ts +1 -1
- package/dist/server/edge.d.ts.map +1 -1
- package/dist/server/edge.js +1 -1
- package/dist/server/edge.js.map +1 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/request-context.d.ts +18 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/social-og-image.d.ts.map +1 -1
- package/dist/server/social-og-image.js +3 -2
- package/dist/server/social-og-image.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +62 -44
- package/dist/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/dist/templates/default/AGENTS.md +7 -3
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/dist/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
- package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
- package/dist/templates/workspace-core/AGENTS.md +3 -2
- package/dist/usage/store.d.ts +7 -0
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +24 -3
- package/dist/usage/store.js.map +1 -1
- package/docs/content/automation-connectors.mdx +80 -0
- package/docs/content/extensions.mdx +8 -0
- package/docs/content/locales/ar-SA/extensions.mdx +8 -0
- package/docs/content/locales/ar-SA/messaging.mdx +84 -15
- package/docs/content/locales/ar-SA/template-clips.mdx +1 -0
- package/docs/content/locales/ar-SA/template-design.mdx +1 -0
- package/docs/content/locales/de-DE/extensions.mdx +10 -0
- package/docs/content/locales/de-DE/messaging.mdx +92 -15
- package/docs/content/locales/de-DE/template-clips.mdx +1 -0
- package/docs/content/locales/de-DE/template-design.mdx +1 -0
- package/docs/content/locales/es-ES/extensions.mdx +10 -0
- package/docs/content/locales/es-ES/messaging.mdx +91 -15
- package/docs/content/locales/es-ES/template-clips.mdx +1 -0
- package/docs/content/locales/es-ES/template-design.mdx +1 -0
- package/docs/content/locales/fr-FR/extensions.mdx +10 -0
- package/docs/content/locales/fr-FR/messaging.mdx +91 -15
- package/docs/content/locales/fr-FR/template-clips.mdx +1 -0
- package/docs/content/locales/fr-FR/template-design.mdx +1 -0
- package/docs/content/locales/hi-IN/extensions.mdx +8 -0
- package/docs/content/locales/hi-IN/messaging.mdx +81 -15
- package/docs/content/locales/hi-IN/template-clips.mdx +1 -0
- package/docs/content/locales/hi-IN/template-design.mdx +1 -0
- package/docs/content/locales/ja-JP/extensions.mdx +9 -0
- package/docs/content/locales/ja-JP/messaging.mdx +81 -15
- package/docs/content/locales/ja-JP/template-clips.mdx +1 -0
- package/docs/content/locales/ja-JP/template-design.mdx +1 -0
- package/docs/content/locales/ko-KR/extensions.mdx +8 -0
- package/docs/content/locales/ko-KR/messaging.mdx +80 -15
- package/docs/content/locales/ko-KR/template-clips.mdx +1 -0
- package/docs/content/locales/ko-KR/template-design.mdx +1 -0
- package/docs/content/locales/pt-BR/extensions.mdx +9 -0
- package/docs/content/locales/pt-BR/messaging.mdx +88 -15
- package/docs/content/locales/pt-BR/template-clips.mdx +1 -0
- package/docs/content/locales/pt-BR/template-design.mdx +1 -0
- package/docs/content/locales/zh-CN/extensions.mdx +6 -0
- package/docs/content/locales/zh-CN/messaging.mdx +78 -15
- package/docs/content/locales/zh-CN/template-clips.mdx +1 -0
- package/docs/content/locales/zh-CN/template-design.mdx +1 -0
- package/docs/content/locales/zh-TW/extensions.mdx +6 -0
- package/docs/content/locales/zh-TW/messaging.mdx +78 -15
- package/docs/content/locales/zh-TW/template-clips.mdx +1 -0
- package/docs/content/locales/zh-TW/template-design.mdx +1 -0
- package/docs/content/messaging.mdx +227 -45
- package/docs/content/template-clips.mdx +3 -0
- package/docs/content/template-design.mdx +4 -0
- package/package.json +5 -1
- package/src/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/src/templates/default/AGENTS.md +7 -3
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
- package/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
- package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
- package/src/templates/workspace-core/AGENTS.md +3 -2
|
@@ -8,6 +8,7 @@ import http, {
|
|
|
8
8
|
type Server,
|
|
9
9
|
type ServerResponse,
|
|
10
10
|
} from "node:http";
|
|
11
|
+
import https from "node:https";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
|
|
13
14
|
const DEFAULT_BRIDGE_PORT = 7331;
|
|
@@ -142,6 +143,7 @@ export interface DesignConnectBridgeOptions {
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
const PREVIEW_TOKEN_DOMAIN = "agent-native-design-preview-v1\0";
|
|
146
|
+
const PREVIEW_SESSION_COOKIE_NAME = "agent-native-preview-token";
|
|
145
147
|
|
|
146
148
|
/**
|
|
147
149
|
* Derive a read-only preview credential from the stronger filesystem token.
|
|
@@ -749,9 +751,10 @@ function configureBridgeCors(
|
|
|
749
751
|
(res as CorsAwareResponse)[BRIDGE_CORS_HEADERS] = approved
|
|
750
752
|
? {
|
|
751
753
|
"access-control-allow-origin": origin,
|
|
752
|
-
"access-control-allow-methods":
|
|
754
|
+
"access-control-allow-methods":
|
|
755
|
+
"GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
753
756
|
"access-control-allow-headers":
|
|
754
|
-
"content-type, x-bridge-token, x-design-preview-token",
|
|
757
|
+
"authorization, content-type, x-bridge-token, x-design-preview-token, x-csrf-token, x-xsrf-token, x-requested-with",
|
|
755
758
|
"access-control-allow-private-network": "true",
|
|
756
759
|
vary: "Origin",
|
|
757
760
|
}
|
|
@@ -780,9 +783,11 @@ function sendText(
|
|
|
780
783
|
statusCode: number,
|
|
781
784
|
body: string,
|
|
782
785
|
contentType: string,
|
|
786
|
+
setCookieHeaders: string[] = [],
|
|
783
787
|
) {
|
|
784
788
|
res.writeHead(statusCode, {
|
|
785
789
|
"content-type": contentType,
|
|
790
|
+
...(setCookieHeaders.length > 0 ? { "set-cookie": setCookieHeaders } : {}),
|
|
786
791
|
...bridgeCorsHeaders(res),
|
|
787
792
|
});
|
|
788
793
|
res.end(body);
|
|
@@ -794,10 +799,12 @@ function sendBytes(
|
|
|
794
799
|
body: Buffer,
|
|
795
800
|
headers: Headers,
|
|
796
801
|
contentLength = body.length,
|
|
802
|
+
setCookieHeaders: string[] = [],
|
|
797
803
|
) {
|
|
798
|
-
const responseHeaders: Record<string, string> = {
|
|
804
|
+
const responseHeaders: Record<string, string | string[]> = {
|
|
799
805
|
...bridgeCorsHeaders(res),
|
|
800
806
|
"content-length": String(contentLength),
|
|
807
|
+
...(setCookieHeaders.length > 0 ? { "set-cookie": setCookieHeaders } : {}),
|
|
801
808
|
};
|
|
802
809
|
for (const name of [
|
|
803
810
|
"content-type",
|
|
@@ -842,25 +849,249 @@ function readHeader(req: IncomingMessage, name: string): string {
|
|
|
842
849
|
return typeof value === "string" ? value : "";
|
|
843
850
|
}
|
|
844
851
|
|
|
852
|
+
function readRequestCookie(req: IncomingMessage, name: string): string {
|
|
853
|
+
for (const rawPair of readHeader(req, "cookie").split(";")) {
|
|
854
|
+
const pair = rawPair.trim();
|
|
855
|
+
const separator = pair.indexOf("=");
|
|
856
|
+
if (separator <= 0 || pair.slice(0, separator).trim() !== name) continue;
|
|
857
|
+
return pair.slice(separator + 1).trim();
|
|
858
|
+
}
|
|
859
|
+
return "";
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function previewSessionSetCookie(previewToken: string): string {
|
|
863
|
+
// Partitioned SameSite=None keeps the read-only bridge credential available
|
|
864
|
+
// to nested Design frames without granting an unpartitioned third-party
|
|
865
|
+
// cookie. Loopback origins are potentially trustworthy in Chromium, which
|
|
866
|
+
// permits Secure cookies for local development.
|
|
867
|
+
return `${PREVIEW_SESSION_COOKIE_NAME}=${previewToken}; HttpOnly; Path=/; SameSite=None; Secure; Partitioned`;
|
|
868
|
+
}
|
|
869
|
+
|
|
845
870
|
/**
|
|
846
|
-
* Preserve only
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
* the
|
|
850
|
-
*
|
|
851
|
-
*
|
|
871
|
+
* Preserve only browser metadata a local dev server needs to classify module
|
|
872
|
+
* requests and parse ordinary same-origin app mutations. Browser cookies and
|
|
873
|
+
* Authorization are forwarded only when Sec-Fetch-Site says the request came
|
|
874
|
+
* from the proxied app itself; cross-site callers cannot relay them. Bridge
|
|
875
|
+
* control tokens and referrers are never forwarded, and mutation origins are
|
|
876
|
+
* replaced with the connected dev-server origin. Cookie-backed login also
|
|
877
|
+
* uses the isolated in-memory upstream jar below so iframe cookie restrictions
|
|
878
|
+
* cannot split the session across URL-backed screens.
|
|
852
879
|
*/
|
|
853
880
|
function previewProxyRequestHeaders(
|
|
854
881
|
req: IncomingMessage,
|
|
882
|
+
devServerUrl: string,
|
|
883
|
+
cookieHeader = "",
|
|
855
884
|
): Record<string, string> {
|
|
856
885
|
const headers: Record<string, string> = {};
|
|
857
|
-
for (const name of [
|
|
886
|
+
for (const name of [
|
|
887
|
+
"accept",
|
|
888
|
+
"accept-language",
|
|
889
|
+
"content-type",
|
|
890
|
+
"sec-fetch-dest",
|
|
891
|
+
"sec-fetch-mode",
|
|
892
|
+
"sec-fetch-site",
|
|
893
|
+
"x-csrf-token",
|
|
894
|
+
"x-xsrf-token",
|
|
895
|
+
"x-requested-with",
|
|
896
|
+
] as const) {
|
|
858
897
|
const value = readHeader(req, name);
|
|
859
898
|
if (value) headers[name] = value;
|
|
860
899
|
}
|
|
900
|
+
const browserSameOrigin = readHeader(req, "sec-fetch-site") === "same-origin";
|
|
901
|
+
const browserCookie = browserSameOrigin ? readHeader(req, "cookie") : "";
|
|
902
|
+
const mergedCookie = mergePreviewCookieHeaders(cookieHeader, browserCookie);
|
|
903
|
+
if (mergedCookie) headers["cookie"] = mergedCookie;
|
|
904
|
+
// Same-origin app code may use localStorage-backed bearer auth. Forward it
|
|
905
|
+
// only from a browser request already classified as same-origin; cross-site
|
|
906
|
+
// callers still authenticate to the bridge with the separate preview token,
|
|
907
|
+
// which is never copied upstream.
|
|
908
|
+
if (browserSameOrigin) {
|
|
909
|
+
const authorization = readHeader(req, "authorization");
|
|
910
|
+
if (authorization && authorization.length <= 16 * 1024) {
|
|
911
|
+
headers["authorization"] = authorization;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (req.method && !["GET", "HEAD"].includes(req.method)) {
|
|
915
|
+
headers["origin"] = new URL(devServerUrl).origin;
|
|
916
|
+
}
|
|
861
917
|
return headers;
|
|
862
918
|
}
|
|
863
919
|
|
|
920
|
+
function mergePreviewCookieHeaders(
|
|
921
|
+
jarCookieHeader: string,
|
|
922
|
+
browserCookieHeader: string,
|
|
923
|
+
): string {
|
|
924
|
+
const values = new Map<string, string>();
|
|
925
|
+
const absorb = (header: string) => {
|
|
926
|
+
if (!header || Buffer.byteLength(header) > MAX_PREVIEW_COOKIE_BYTES) return;
|
|
927
|
+
for (const rawPair of header.split(";")) {
|
|
928
|
+
const pair = rawPair.trim();
|
|
929
|
+
const separator = pair.indexOf("=");
|
|
930
|
+
if (separator <= 0) continue;
|
|
931
|
+
const name = pair.slice(0, separator).trim();
|
|
932
|
+
const value = pair.slice(separator + 1).trim();
|
|
933
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) continue;
|
|
934
|
+
if (/^(?:x-)?agent-native-(?:bridge|preview)(?:-|$)/i.test(name)) {
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
values.set(name, value);
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
absorb(jarCookieHeader);
|
|
941
|
+
// A client-readable same-origin cookie is allowed to update the jar's stale
|
|
942
|
+
// value for this request. HttpOnly cookies arrive with the same value and
|
|
943
|
+
// remain effectively jar-owned.
|
|
944
|
+
absorb(browserCookieHeader);
|
|
945
|
+
return [...values].map(([name, value]) => `${name}=${value}`).join("; ");
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
const MAX_PREVIEW_SESSION_COOKIES = 128;
|
|
949
|
+
const MAX_PREVIEW_COOKIE_BYTES = 8 * 1024;
|
|
950
|
+
|
|
951
|
+
type PreviewSessionCookie = {
|
|
952
|
+
name: string;
|
|
953
|
+
value: string;
|
|
954
|
+
path: string;
|
|
955
|
+
secure: boolean;
|
|
956
|
+
expiresAt?: number;
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
function responseSetCookieHeaders(headers: Headers): string[] {
|
|
960
|
+
const withGetSetCookie = headers as Headers & {
|
|
961
|
+
getSetCookie?: () => string[];
|
|
962
|
+
};
|
|
963
|
+
if (typeof withGetSetCookie.getSetCookie === "function") {
|
|
964
|
+
return withGetSetCookie.getSetCookie();
|
|
965
|
+
}
|
|
966
|
+
const single = headers.get("set-cookie");
|
|
967
|
+
return single ? [single] : [];
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function defaultCookiePath(url: URL): string {
|
|
971
|
+
const pathname = url.pathname || "/";
|
|
972
|
+
const lastSlash = pathname.lastIndexOf("/");
|
|
973
|
+
return lastSlash <= 0 ? "/" : pathname.slice(0, lastSlash);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
function sanitizedBridgeSetCookie(raw: string): string {
|
|
977
|
+
return raw
|
|
978
|
+
.split(";")
|
|
979
|
+
.filter((part) => !/^\s*domain\s*=/i.test(part))
|
|
980
|
+
.join(";");
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Ephemeral cookie session for one connected dev-server origin. Cookies never
|
|
985
|
+
* enter SQL, disk, logs, or another target origin, and disappear when the
|
|
986
|
+
* local bridge exits. This keeps authenticated URL screens in one bridge
|
|
987
|
+
* process on the same upstream session even when browser third-party-cookie
|
|
988
|
+
* rules would otherwise isolate or reject the nested iframe cookies.
|
|
989
|
+
*/
|
|
990
|
+
class PreviewSessionCookieJar {
|
|
991
|
+
private readonly origin: string;
|
|
992
|
+
private readonly cookies = new Map<string, PreviewSessionCookie>();
|
|
993
|
+
|
|
994
|
+
constructor(devServerUrl: string) {
|
|
995
|
+
this.origin = new URL(devServerUrl).origin;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
store(headers: Headers, responseUrl: string): string[] {
|
|
999
|
+
const parsedUrl = new URL(responseUrl);
|
|
1000
|
+
if (parsedUrl.origin !== this.origin) return [];
|
|
1001
|
+
const browserCookies: string[] = [];
|
|
1002
|
+
for (const raw of responseSetCookieHeaders(headers)) {
|
|
1003
|
+
if (!raw || Buffer.byteLength(raw) > MAX_PREVIEW_COOKIE_BYTES) continue;
|
|
1004
|
+
const parts = raw.split(";");
|
|
1005
|
+
const first = parts.shift()?.trim() ?? "";
|
|
1006
|
+
const separator = first.indexOf("=");
|
|
1007
|
+
if (separator <= 0) continue;
|
|
1008
|
+
const name = first.slice(0, separator).trim();
|
|
1009
|
+
const value = first.slice(separator + 1).trim();
|
|
1010
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) continue;
|
|
1011
|
+
let cookiePath = defaultCookiePath(parsedUrl);
|
|
1012
|
+
let secure = false;
|
|
1013
|
+
let expiresAt: number | undefined;
|
|
1014
|
+
let deleteCookie = false;
|
|
1015
|
+
for (const attribute of parts) {
|
|
1016
|
+
const trimmed = attribute.trim();
|
|
1017
|
+
const equals = trimmed.indexOf("=");
|
|
1018
|
+
const key = (equals < 0 ? trimmed : trimmed.slice(0, equals))
|
|
1019
|
+
.trim()
|
|
1020
|
+
.toLowerCase();
|
|
1021
|
+
const attributeValue =
|
|
1022
|
+
equals < 0 ? "" : trimmed.slice(equals + 1).trim();
|
|
1023
|
+
if (key === "path" && attributeValue.startsWith("/")) {
|
|
1024
|
+
cookiePath = attributeValue;
|
|
1025
|
+
} else if (key === "secure") {
|
|
1026
|
+
secure = true;
|
|
1027
|
+
} else if (key === "max-age") {
|
|
1028
|
+
const seconds = Number(attributeValue);
|
|
1029
|
+
if (Number.isFinite(seconds)) {
|
|
1030
|
+
deleteCookie = seconds <= 0;
|
|
1031
|
+
expiresAt = Date.now() + seconds * 1000;
|
|
1032
|
+
}
|
|
1033
|
+
} else if (key === "expires" && expiresAt === undefined) {
|
|
1034
|
+
const parsed = Date.parse(attributeValue);
|
|
1035
|
+
if (Number.isFinite(parsed)) expiresAt = parsed;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
const mapKey = `${cookiePath}\u0000${name}`;
|
|
1039
|
+
if (
|
|
1040
|
+
deleteCookie ||
|
|
1041
|
+
(expiresAt !== undefined && expiresAt <= Date.now())
|
|
1042
|
+
) {
|
|
1043
|
+
this.cookies.delete(mapKey);
|
|
1044
|
+
} else {
|
|
1045
|
+
this.cookies.delete(mapKey);
|
|
1046
|
+
this.cookies.set(mapKey, {
|
|
1047
|
+
name,
|
|
1048
|
+
value,
|
|
1049
|
+
path: cookiePath,
|
|
1050
|
+
secure,
|
|
1051
|
+
...(expiresAt !== undefined ? { expiresAt } : {}),
|
|
1052
|
+
});
|
|
1053
|
+
while (this.cookies.size > MAX_PREVIEW_SESSION_COOKIES) {
|
|
1054
|
+
const oldest = this.cookies.keys().next().value;
|
|
1055
|
+
if (typeof oldest !== "string") break;
|
|
1056
|
+
this.cookies.delete(oldest);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
browserCookies.push(sanitizedBridgeSetCookie(raw));
|
|
1060
|
+
}
|
|
1061
|
+
return browserCookies;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
headerFor(targetUrl: string): string {
|
|
1065
|
+
const parsed = new URL(targetUrl);
|
|
1066
|
+
if (parsed.origin !== this.origin) return "";
|
|
1067
|
+
const now = Date.now();
|
|
1068
|
+
const matches: PreviewSessionCookie[] = [];
|
|
1069
|
+
for (const [key, cookie] of this.cookies) {
|
|
1070
|
+
if (cookie.expiresAt !== undefined && cookie.expiresAt <= now) {
|
|
1071
|
+
this.cookies.delete(key);
|
|
1072
|
+
continue;
|
|
1073
|
+
}
|
|
1074
|
+
if (
|
|
1075
|
+
cookie.secure &&
|
|
1076
|
+
parsed.protocol !== "https:" &&
|
|
1077
|
+
!isLoopbackOrigin(parsed)
|
|
1078
|
+
) {
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
const pathMatches =
|
|
1082
|
+
cookie.path === "/" ||
|
|
1083
|
+
parsed.pathname === cookie.path ||
|
|
1084
|
+
(cookie.path.endsWith("/")
|
|
1085
|
+
? parsed.pathname.startsWith(cookie.path)
|
|
1086
|
+
: parsed.pathname.startsWith(`${cookie.path}/`));
|
|
1087
|
+
if (!pathMatches) continue;
|
|
1088
|
+
matches.push(cookie);
|
|
1089
|
+
}
|
|
1090
|
+
matches.sort((a, b) => b.path.length - a.path.length);
|
|
1091
|
+
return matches.map((cookie) => `${cookie.name}=${cookie.value}`).join("; ");
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
864
1095
|
function resolvePreviewSnapshotUrl(
|
|
865
1096
|
devServerUrl: string,
|
|
866
1097
|
rawUrl: string | null,
|
|
@@ -897,12 +1128,14 @@ function resolvePreviewProxyUrl(
|
|
|
897
1128
|
async function fetchPreviewSnapshot(
|
|
898
1129
|
devServerUrl: string,
|
|
899
1130
|
targetUrl: string,
|
|
1131
|
+
cookieJar?: PreviewSessionCookieJar,
|
|
900
1132
|
redirects = 0,
|
|
901
1133
|
): Promise<{
|
|
902
1134
|
url: string;
|
|
903
1135
|
status: number;
|
|
904
1136
|
contentType: string;
|
|
905
1137
|
html: string;
|
|
1138
|
+
setCookieHeaders: string[];
|
|
906
1139
|
}> {
|
|
907
1140
|
if (redirects > 5) {
|
|
908
1141
|
throw new Error("Too many redirects while fetching preview snapshot.");
|
|
@@ -916,8 +1149,13 @@ async function fetchPreviewSnapshot(
|
|
|
916
1149
|
signal: controller.signal,
|
|
917
1150
|
headers: {
|
|
918
1151
|
accept: "text/html,application/xhtml+xml;q=0.9,*/*;q=0.8",
|
|
1152
|
+
...(cookieJar?.headerFor(targetUrl)
|
|
1153
|
+
? { cookie: cookieJar.headerFor(targetUrl) }
|
|
1154
|
+
: {}),
|
|
919
1155
|
},
|
|
920
1156
|
});
|
|
1157
|
+
const setCookieHeaders =
|
|
1158
|
+
cookieJar?.store(response.headers, targetUrl) ?? [];
|
|
921
1159
|
const location = response.headers.get("location");
|
|
922
1160
|
if (location && response.status >= 300 && response.status < 400) {
|
|
923
1161
|
const redirected = new URL(location, targetUrl);
|
|
@@ -928,6 +1166,7 @@ async function fetchPreviewSnapshot(
|
|
|
928
1166
|
return fetchPreviewSnapshot(
|
|
929
1167
|
devServerUrl,
|
|
930
1168
|
redirected.toString(),
|
|
1169
|
+
cookieJar,
|
|
931
1170
|
redirects + 1,
|
|
932
1171
|
);
|
|
933
1172
|
}
|
|
@@ -936,6 +1175,7 @@ async function fetchPreviewSnapshot(
|
|
|
936
1175
|
status: response.status,
|
|
937
1176
|
contentType: response.headers.get("content-type") ?? "",
|
|
938
1177
|
html: await response.text(),
|
|
1178
|
+
setCookieHeaders,
|
|
939
1179
|
};
|
|
940
1180
|
} finally {
|
|
941
1181
|
clearTimeout(timeout);
|
|
@@ -945,13 +1185,19 @@ async function fetchPreviewSnapshot(
|
|
|
945
1185
|
async function fetchPreviewProxyResource(
|
|
946
1186
|
devServerUrl: string,
|
|
947
1187
|
targetUrl: string,
|
|
948
|
-
|
|
1188
|
+
request: {
|
|
1189
|
+
method: string;
|
|
1190
|
+
headers?: Record<string, string>;
|
|
1191
|
+
body?: Buffer;
|
|
1192
|
+
},
|
|
1193
|
+
cookieJar?: PreviewSessionCookieJar,
|
|
949
1194
|
redirects = 0,
|
|
950
1195
|
): Promise<{
|
|
951
1196
|
url: string;
|
|
952
1197
|
status: number;
|
|
953
1198
|
headers: Headers;
|
|
954
1199
|
body: Buffer;
|
|
1200
|
+
setCookieHeaders: string[];
|
|
955
1201
|
}> {
|
|
956
1202
|
if (redirects > 5) {
|
|
957
1203
|
throw new Error("Too many redirects while proxying preview resource.");
|
|
@@ -960,11 +1206,17 @@ async function fetchPreviewProxyResource(
|
|
|
960
1206
|
const timeout = setTimeout(() => controller.abort(), 10_000);
|
|
961
1207
|
try {
|
|
962
1208
|
const response = await fetch(targetUrl, {
|
|
963
|
-
method:
|
|
1209
|
+
method: request.method,
|
|
964
1210
|
redirect: "manual",
|
|
965
1211
|
signal: controller.signal,
|
|
966
|
-
headers:
|
|
1212
|
+
headers: request.headers,
|
|
1213
|
+
body:
|
|
1214
|
+
request.method === "GET" || request.method === "HEAD"
|
|
1215
|
+
? undefined
|
|
1216
|
+
: request.body,
|
|
967
1217
|
});
|
|
1218
|
+
const setCookieHeaders =
|
|
1219
|
+
cookieJar?.store(response.headers, targetUrl) ?? [];
|
|
968
1220
|
const location = response.headers.get("location");
|
|
969
1221
|
if (location && response.status >= 300 && response.status < 400) {
|
|
970
1222
|
const redirected = new URL(location, targetUrl);
|
|
@@ -972,18 +1224,40 @@ async function fetchPreviewProxyResource(
|
|
|
972
1224
|
if (!sameOrigin(redirected.toString(), devServerUrl)) {
|
|
973
1225
|
throw new Error("Proxy redirect left the connected dev server.");
|
|
974
1226
|
}
|
|
975
|
-
|
|
1227
|
+
const switchToGet =
|
|
1228
|
+
response.status === 303 ||
|
|
1229
|
+
((response.status === 301 || response.status === 302) &&
|
|
1230
|
+
request.method === "POST");
|
|
1231
|
+
const redirectedResponse = await fetchPreviewProxyResource(
|
|
976
1232
|
devServerUrl,
|
|
977
1233
|
redirected.toString(),
|
|
978
|
-
|
|
1234
|
+
{
|
|
1235
|
+
method: switchToGet ? "GET" : request.method,
|
|
1236
|
+
headers: {
|
|
1237
|
+
...(request.headers ?? {}),
|
|
1238
|
+
...(cookieJar?.headerFor(redirected.toString())
|
|
1239
|
+
? { cookie: cookieJar.headerFor(redirected.toString()) }
|
|
1240
|
+
: {}),
|
|
1241
|
+
},
|
|
1242
|
+
body: switchToGet ? undefined : request.body,
|
|
1243
|
+
},
|
|
1244
|
+
cookieJar,
|
|
979
1245
|
redirects + 1,
|
|
980
1246
|
);
|
|
1247
|
+
return {
|
|
1248
|
+
...redirectedResponse,
|
|
1249
|
+
setCookieHeaders: [
|
|
1250
|
+
...setCookieHeaders,
|
|
1251
|
+
...redirectedResponse.setCookieHeaders,
|
|
1252
|
+
],
|
|
1253
|
+
};
|
|
981
1254
|
}
|
|
982
1255
|
return {
|
|
983
1256
|
url: response.url || targetUrl,
|
|
984
1257
|
status: response.status,
|
|
985
1258
|
headers: response.headers,
|
|
986
1259
|
body: Buffer.from(await response.arrayBuffer()),
|
|
1260
|
+
setCookieHeaders,
|
|
987
1261
|
};
|
|
988
1262
|
} finally {
|
|
989
1263
|
clearTimeout(timeout);
|
|
@@ -1062,6 +1336,50 @@ async function readRequestBody(req: IncomingMessage): Promise<string> {
|
|
|
1062
1336
|
});
|
|
1063
1337
|
}
|
|
1064
1338
|
|
|
1339
|
+
const MAX_PREVIEW_PROXY_REQUEST_BYTES = 8 * 1024 * 1024;
|
|
1340
|
+
|
|
1341
|
+
class PreviewProxyRequestTooLargeError extends Error {}
|
|
1342
|
+
|
|
1343
|
+
async function readPreviewProxyRequestBody(
|
|
1344
|
+
req: IncomingMessage,
|
|
1345
|
+
): Promise<Buffer> {
|
|
1346
|
+
const declaredLength = Number(readHeader(req, "content-length"));
|
|
1347
|
+
if (
|
|
1348
|
+
Number.isFinite(declaredLength) &&
|
|
1349
|
+
declaredLength > MAX_PREVIEW_PROXY_REQUEST_BYTES
|
|
1350
|
+
) {
|
|
1351
|
+
throw new PreviewProxyRequestTooLargeError(
|
|
1352
|
+
"Preview request body exceeds the 8 MB limit.",
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
return new Promise<Buffer>((resolve, reject) => {
|
|
1356
|
+
const chunks: Buffer[] = [];
|
|
1357
|
+
let size = 0;
|
|
1358
|
+
let tooLarge = false;
|
|
1359
|
+
req.on("data", (chunk: Buffer) => {
|
|
1360
|
+
size += chunk.length;
|
|
1361
|
+
if (size > MAX_PREVIEW_PROXY_REQUEST_BYTES) {
|
|
1362
|
+
tooLarge = true;
|
|
1363
|
+
chunks.length = 0;
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
if (!tooLarge) chunks.push(chunk);
|
|
1367
|
+
});
|
|
1368
|
+
req.on("end", () => {
|
|
1369
|
+
if (tooLarge) {
|
|
1370
|
+
reject(
|
|
1371
|
+
new PreviewProxyRequestTooLargeError(
|
|
1372
|
+
"Preview request body exceeds the 8 MB limit.",
|
|
1373
|
+
),
|
|
1374
|
+
);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
resolve(Buffer.concat(chunks));
|
|
1378
|
+
});
|
|
1379
|
+
req.on("error", reject);
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1065
1383
|
/**
|
|
1066
1384
|
* Resolve `targetDir` under `rootPath` with realpath so that symlinks and
|
|
1067
1385
|
* traversal sequences (../../etc) cannot escape the root. Throws if the
|
|
@@ -1362,8 +1680,13 @@ async function atomicWriteBridgeFile(args: {
|
|
|
1362
1680
|
}
|
|
1363
1681
|
}
|
|
1364
1682
|
|
|
1365
|
-
/**
|
|
1366
|
-
|
|
1683
|
+
/**
|
|
1684
|
+
* Extensions that are safe for creating a brand-new text/code file through
|
|
1685
|
+
* the bridge. Existing files are classified by their bytes instead, so the
|
|
1686
|
+
* Code workbench can edit languages and extensionless config files without a
|
|
1687
|
+
* permanently incomplete allowlist.
|
|
1688
|
+
*/
|
|
1689
|
+
const ALLOWED_NEW_TEXT_FILE_EXTENSIONS = new Set([
|
|
1367
1690
|
".html",
|
|
1368
1691
|
".htm",
|
|
1369
1692
|
".css",
|
|
@@ -1385,13 +1708,103 @@ const ALLOWED_WRITE_EXTENSIONS = new Set([
|
|
|
1385
1708
|
".yml",
|
|
1386
1709
|
".yaml",
|
|
1387
1710
|
".svg",
|
|
1711
|
+
".py",
|
|
1712
|
+
".rb",
|
|
1713
|
+
".php",
|
|
1714
|
+
".java",
|
|
1715
|
+
".kt",
|
|
1716
|
+
".kts",
|
|
1717
|
+
".go",
|
|
1718
|
+
".rs",
|
|
1719
|
+
".c",
|
|
1720
|
+
".h",
|
|
1721
|
+
".cc",
|
|
1722
|
+
".cpp",
|
|
1723
|
+
".hpp",
|
|
1724
|
+
".cs",
|
|
1725
|
+
".swift",
|
|
1726
|
+
".sh",
|
|
1727
|
+
".bash",
|
|
1728
|
+
".zsh",
|
|
1729
|
+
".fish",
|
|
1730
|
+
".sql",
|
|
1731
|
+
".toml",
|
|
1732
|
+
".xml",
|
|
1733
|
+
".graphql",
|
|
1734
|
+
".gql",
|
|
1735
|
+
".prisma",
|
|
1736
|
+
".properties",
|
|
1737
|
+
".ini",
|
|
1738
|
+
".conf",
|
|
1739
|
+
".env.example",
|
|
1740
|
+
]);
|
|
1741
|
+
|
|
1742
|
+
const ALLOWED_NEW_EXTENSIONLESS_TEXT_FILES = new Set([
|
|
1743
|
+
"dockerfile",
|
|
1744
|
+
"makefile",
|
|
1745
|
+
"procfile",
|
|
1746
|
+
"gemfile",
|
|
1747
|
+
"rakefile",
|
|
1748
|
+
"license",
|
|
1749
|
+
"readme",
|
|
1388
1750
|
]);
|
|
1389
1751
|
|
|
1390
|
-
|
|
1752
|
+
const BLOCKED_BINARY_WRITE_EXTENSIONS = new Set([
|
|
1753
|
+
".png",
|
|
1754
|
+
".jpg",
|
|
1755
|
+
".jpeg",
|
|
1756
|
+
".gif",
|
|
1757
|
+
".webp",
|
|
1758
|
+
".ico",
|
|
1759
|
+
".woff",
|
|
1760
|
+
".woff2",
|
|
1761
|
+
".ttf",
|
|
1762
|
+
".otf",
|
|
1763
|
+
".eot",
|
|
1764
|
+
".mp3",
|
|
1765
|
+
".mp4",
|
|
1766
|
+
".mov",
|
|
1767
|
+
".webm",
|
|
1768
|
+
".zip",
|
|
1769
|
+
".gz",
|
|
1770
|
+
".tar",
|
|
1771
|
+
".pdf",
|
|
1772
|
+
".wasm",
|
|
1773
|
+
".fig",
|
|
1774
|
+
".sketch",
|
|
1775
|
+
".exe",
|
|
1776
|
+
".dll",
|
|
1777
|
+
".dylib",
|
|
1778
|
+
".so",
|
|
1779
|
+
".class",
|
|
1780
|
+
".jar",
|
|
1781
|
+
]);
|
|
1782
|
+
|
|
1783
|
+
function assertEditableTextFile(
|
|
1784
|
+
relPath: string,
|
|
1785
|
+
existing: BridgeFileSnapshot | null,
|
|
1786
|
+
): void {
|
|
1391
1787
|
const ext = path.extname(relPath).toLowerCase();
|
|
1392
|
-
if (
|
|
1788
|
+
if (BLOCKED_BINARY_WRITE_EXTENSIONS.has(ext)) {
|
|
1393
1789
|
throw new Error(
|
|
1394
|
-
`Write rejected: extension "${ext
|
|
1790
|
+
`Write rejected: extension "${ext}" is a binary file type.`,
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
if (existing) {
|
|
1794
|
+
if (existing.content.includes("\0")) {
|
|
1795
|
+
throw new Error(
|
|
1796
|
+
"Write rejected: the existing file contains binary data.",
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
const basename = path.basename(relPath).toLowerCase();
|
|
1802
|
+
if (
|
|
1803
|
+
!ALLOWED_NEW_TEXT_FILE_EXTENSIONS.has(ext) &&
|
|
1804
|
+
!ALLOWED_NEW_EXTENSIONLESS_TEXT_FILES.has(basename)
|
|
1805
|
+
) {
|
|
1806
|
+
throw new Error(
|
|
1807
|
+
`Write rejected: extension "${ext || "(no extension)"}" is not recognized for a new text/code file.`,
|
|
1395
1808
|
);
|
|
1396
1809
|
}
|
|
1397
1810
|
}
|
|
@@ -1774,6 +2187,9 @@ export async function startDesignConnectBridge(
|
|
|
1774
2187
|
// registered" (id changed, safe to transparently re-POST `/live-edit-bridge`
|
|
1775
2188
|
// and retry) — instead of guessing from the error text or retrying forever.
|
|
1776
2189
|
const bridgeInstanceId = crypto.randomBytes(16).toString("hex");
|
|
2190
|
+
const previewSessionCookies = new PreviewSessionCookieJar(
|
|
2191
|
+
manifest.devServerUrl,
|
|
2192
|
+
);
|
|
1777
2193
|
|
|
1778
2194
|
const server = http.createServer(
|
|
1779
2195
|
(req: IncomingMessage, res: ServerResponse) => {
|
|
@@ -1794,6 +2210,7 @@ export async function startDesignConnectBridge(
|
|
|
1794
2210
|
const providedPreviewToken =
|
|
1795
2211
|
readHeader(req, "x-design-preview-token") ||
|
|
1796
2212
|
requestUrl.searchParams.get("previewToken") ||
|
|
2213
|
+
readRequestCookie(req, PREVIEW_SESSION_COOKIE_NAME) ||
|
|
1797
2214
|
"";
|
|
1798
2215
|
const previewTokenValid = constantTimeTokenMatches(
|
|
1799
2216
|
providedPreviewToken,
|
|
@@ -1915,6 +2332,7 @@ export async function startDesignConnectBridge(
|
|
|
1915
2332
|
const snapshot = await fetchPreviewSnapshot(
|
|
1916
2333
|
manifest.devServerUrl,
|
|
1917
2334
|
targetUrl,
|
|
2335
|
+
previewSessionCookies,
|
|
1918
2336
|
);
|
|
1919
2337
|
const includeEditorBridge =
|
|
1920
2338
|
requestUrl.searchParams.get("bridge") !== "0";
|
|
@@ -1963,6 +2381,10 @@ export async function startDesignConnectBridge(
|
|
|
1963
2381
|
snapshot.contentType.includes("html")
|
|
1964
2382
|
? snapshot.contentType
|
|
1965
2383
|
: "text/html; charset=utf-8",
|
|
2384
|
+
[
|
|
2385
|
+
...snapshot.setCookieHeaders,
|
|
2386
|
+
previewSessionSetCookie(previewToken),
|
|
2387
|
+
],
|
|
1966
2388
|
);
|
|
1967
2389
|
} catch (err: unknown) {
|
|
1968
2390
|
sendJson(res, 400, {
|
|
@@ -1989,6 +2411,7 @@ export async function startDesignConnectBridge(
|
|
|
1989
2411
|
const snapshot = await fetchPreviewSnapshot(
|
|
1990
2412
|
manifest.devServerUrl,
|
|
1991
2413
|
targetUrl,
|
|
2414
|
+
previewSessionCookies,
|
|
1992
2415
|
);
|
|
1993
2416
|
sendJson(res, snapshot.status >= 400 ? snapshot.status : 200, {
|
|
1994
2417
|
ok: snapshot.status < 400,
|
|
@@ -2088,9 +2511,6 @@ export async function startDesignConnectBridge(
|
|
|
2088
2511
|
return;
|
|
2089
2512
|
}
|
|
2090
2513
|
|
|
2091
|
-
// write-file and apply-edit only allow known text/code extensions.
|
|
2092
|
-
assertAllowedExtension(relPath);
|
|
2093
|
-
|
|
2094
2514
|
const expectedVersionHash =
|
|
2095
2515
|
typeof body["expectedVersionHash"] === "string"
|
|
2096
2516
|
? body["expectedVersionHash"]
|
|
@@ -2110,6 +2530,7 @@ export async function startDesignConnectBridge(
|
|
|
2110
2530
|
const existing = await readBridgeFileSnapshot(
|
|
2111
2531
|
lockedTarget.absolutePath,
|
|
2112
2532
|
);
|
|
2533
|
+
assertEditableTextFile(relPath, existing);
|
|
2113
2534
|
assertExpectedBridgeVersion(
|
|
2114
2535
|
expectedVersionHash,
|
|
2115
2536
|
existing?.versionHash,
|
|
@@ -2251,10 +2672,11 @@ export async function startDesignConnectBridge(
|
|
|
2251
2672
|
return;
|
|
2252
2673
|
}
|
|
2253
2674
|
|
|
2254
|
-
if (
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2675
|
+
if (
|
|
2676
|
+
req.method &&
|
|
2677
|
+
["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"].includes(req.method)
|
|
2678
|
+
) {
|
|
2679
|
+
if (!previewTokenValid) {
|
|
2258
2680
|
sendJson(res, 401, {
|
|
2259
2681
|
ok: false,
|
|
2260
2682
|
error: "invalid or missing preview token",
|
|
@@ -2263,27 +2685,66 @@ export async function startDesignConnectBridge(
|
|
|
2263
2685
|
}
|
|
2264
2686
|
void (async () => {
|
|
2265
2687
|
try {
|
|
2688
|
+
const proxyRequestUrl = new URL(req.url ?? "/", manifest.bridgeUrl);
|
|
2689
|
+
proxyRequestUrl.searchParams.delete("previewToken");
|
|
2266
2690
|
const targetUrl = resolvePreviewProxyUrl(
|
|
2267
2691
|
manifest.devServerUrl,
|
|
2268
|
-
|
|
2692
|
+
`${proxyRequestUrl.pathname}${proxyRequestUrl.search}`,
|
|
2269
2693
|
);
|
|
2694
|
+
const method = req.method ?? "GET";
|
|
2695
|
+
const requestBody =
|
|
2696
|
+
method === "GET" || method === "HEAD"
|
|
2697
|
+
? undefined
|
|
2698
|
+
: await readPreviewProxyRequestBody(req);
|
|
2699
|
+
const cookieHeader = previewSessionCookies.headerFor(targetUrl);
|
|
2270
2700
|
const proxied = await fetchPreviewProxyResource(
|
|
2271
2701
|
manifest.devServerUrl,
|
|
2272
2702
|
targetUrl,
|
|
2273
|
-
|
|
2703
|
+
{
|
|
2704
|
+
method,
|
|
2705
|
+
headers: previewProxyRequestHeaders(
|
|
2706
|
+
req,
|
|
2707
|
+
manifest.devServerUrl,
|
|
2708
|
+
cookieHeader,
|
|
2709
|
+
),
|
|
2710
|
+
body: requestBody,
|
|
2711
|
+
},
|
|
2712
|
+
previewSessionCookies,
|
|
2274
2713
|
);
|
|
2714
|
+
const contentType = proxied.headers.get("content-type") ?? "";
|
|
2715
|
+
const documentNavigation =
|
|
2716
|
+
readHeader(req, "sec-fetch-dest") === "document";
|
|
2717
|
+
const responseBody =
|
|
2718
|
+
documentNavigation && contentType.includes("html")
|
|
2719
|
+
? Buffer.from(
|
|
2720
|
+
injectLiveEditBridge(
|
|
2721
|
+
proxied.body.toString("utf8"),
|
|
2722
|
+
new URL("/", manifest.bridgeUrl).toString(),
|
|
2723
|
+
liveEditBridgeScript,
|
|
2724
|
+
(() => {
|
|
2725
|
+
const parsed = new URL(proxied.url);
|
|
2726
|
+
return `${parsed.pathname}${parsed.search}` || "/";
|
|
2727
|
+
})(),
|
|
2728
|
+
),
|
|
2729
|
+
)
|
|
2730
|
+
: proxied.body;
|
|
2275
2731
|
sendBytes(
|
|
2276
2732
|
res,
|
|
2277
|
-
proxied.status
|
|
2278
|
-
|
|
2733
|
+
proxied.status,
|
|
2734
|
+
method === "HEAD" ? Buffer.alloc(0) : responseBody,
|
|
2279
2735
|
proxied.headers,
|
|
2280
|
-
|
|
2736
|
+
responseBody.length,
|
|
2737
|
+
proxied.setCookieHeaders,
|
|
2281
2738
|
);
|
|
2282
2739
|
} catch (err: unknown) {
|
|
2283
|
-
sendJson(
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2740
|
+
sendJson(
|
|
2741
|
+
res,
|
|
2742
|
+
err instanceof PreviewProxyRequestTooLargeError ? 413 : 400,
|
|
2743
|
+
{
|
|
2744
|
+
ok: false,
|
|
2745
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2746
|
+
},
|
|
2747
|
+
);
|
|
2287
2748
|
}
|
|
2288
2749
|
})();
|
|
2289
2750
|
return;
|
|
@@ -2293,6 +2754,105 @@ export async function startDesignConnectBridge(
|
|
|
2293
2754
|
},
|
|
2294
2755
|
);
|
|
2295
2756
|
|
|
2757
|
+
// Vite's proxied /@vite/client derives its HMR socket from the document's
|
|
2758
|
+
// bridge origin. Tunnel WebSocket upgrades to the one connected dev-server
|
|
2759
|
+
// origin so Fast Refresh remains live inside URL-backed screens. The target
|
|
2760
|
+
// is never caller-controlled, bridge credentials are stripped, and only a
|
|
2761
|
+
// same-origin iframe (or an explicit preview-token caller) can open it.
|
|
2762
|
+
server.on("upgrade", (req, clientSocket, clientHead) => {
|
|
2763
|
+
const requestUrl = new URL(req.url ?? "/", manifest.bridgeUrl);
|
|
2764
|
+
const providedPreviewToken =
|
|
2765
|
+
readHeader(req, "x-design-preview-token") ||
|
|
2766
|
+
requestUrl.searchParams.get("previewToken") ||
|
|
2767
|
+
readRequestCookie(req, PREVIEW_SESSION_COOKIE_NAME) ||
|
|
2768
|
+
"";
|
|
2769
|
+
const browserOrigin = readHeader(req, "origin");
|
|
2770
|
+
const sameBridgeOrigin =
|
|
2771
|
+
browserOrigin === new URL(manifest.bridgeUrl).origin;
|
|
2772
|
+
if (!constantTimeTokenMatches(providedPreviewToken, previewToken)) {
|
|
2773
|
+
clientSocket.end(
|
|
2774
|
+
"HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 0\r\n\r\n",
|
|
2775
|
+
);
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
requestUrl.searchParams.delete("previewToken");
|
|
2779
|
+
let targetUrl: URL;
|
|
2780
|
+
try {
|
|
2781
|
+
targetUrl = new URL(
|
|
2782
|
+
resolvePreviewProxyUrl(
|
|
2783
|
+
manifest.devServerUrl,
|
|
2784
|
+
`${requestUrl.pathname}${requestUrl.search}`,
|
|
2785
|
+
),
|
|
2786
|
+
);
|
|
2787
|
+
} catch {
|
|
2788
|
+
clientSocket.end(
|
|
2789
|
+
"HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n",
|
|
2790
|
+
);
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
const upstreamHeaders = { ...req.headers };
|
|
2795
|
+
delete upstreamHeaders["x-bridge-token"];
|
|
2796
|
+
delete upstreamHeaders["x-design-preview-token"];
|
|
2797
|
+
delete upstreamHeaders["authorization"];
|
|
2798
|
+
delete upstreamHeaders["cookie"];
|
|
2799
|
+
upstreamHeaders.host = targetUrl.host;
|
|
2800
|
+
upstreamHeaders.origin = new URL(manifest.devServerUrl).origin;
|
|
2801
|
+
const cookie = mergePreviewCookieHeaders(
|
|
2802
|
+
previewSessionCookies.headerFor(targetUrl.toString()),
|
|
2803
|
+
sameBridgeOrigin ? readHeader(req, "cookie") : "",
|
|
2804
|
+
);
|
|
2805
|
+
if (cookie) upstreamHeaders.cookie = cookie;
|
|
2806
|
+
|
|
2807
|
+
const requestImpl = targetUrl.protocol === "https:" ? https : http;
|
|
2808
|
+
const upstreamRequest = requestImpl.request({
|
|
2809
|
+
protocol: targetUrl.protocol,
|
|
2810
|
+
hostname: targetUrl.hostname,
|
|
2811
|
+
port: targetUrl.port,
|
|
2812
|
+
path: `${targetUrl.pathname}${targetUrl.search}`,
|
|
2813
|
+
method: "GET",
|
|
2814
|
+
headers: upstreamHeaders,
|
|
2815
|
+
});
|
|
2816
|
+
upstreamRequest.on(
|
|
2817
|
+
"upgrade",
|
|
2818
|
+
(upstreamResponse, upstreamSocket, upstreamHead) => {
|
|
2819
|
+
const statusLine = `HTTP/1.1 ${upstreamResponse.statusCode ?? 101} ${upstreamResponse.statusMessage || "Switching Protocols"}\r\n`;
|
|
2820
|
+
const headerLines: string[] = [];
|
|
2821
|
+
for (
|
|
2822
|
+
let index = 0;
|
|
2823
|
+
index < upstreamResponse.rawHeaders.length;
|
|
2824
|
+
index += 2
|
|
2825
|
+
) {
|
|
2826
|
+
const name = upstreamResponse.rawHeaders[index];
|
|
2827
|
+
const value = upstreamResponse.rawHeaders[index + 1];
|
|
2828
|
+
if (name && value !== undefined)
|
|
2829
|
+
headerLines.push(`${name}: ${value}`);
|
|
2830
|
+
}
|
|
2831
|
+
clientSocket.write(`${statusLine}${headerLines.join("\r\n")}\r\n\r\n`);
|
|
2832
|
+
if (clientHead.length > 0) upstreamSocket.write(clientHead);
|
|
2833
|
+
if (upstreamHead.length > 0) clientSocket.write(upstreamHead);
|
|
2834
|
+
clientSocket.once("close", () => upstreamSocket.destroy());
|
|
2835
|
+
upstreamSocket.once("close", () => clientSocket.destroy());
|
|
2836
|
+
upstreamSocket.pipe(clientSocket);
|
|
2837
|
+
clientSocket.pipe(upstreamSocket);
|
|
2838
|
+
},
|
|
2839
|
+
);
|
|
2840
|
+
upstreamRequest.on("response", (upstreamResponse) => {
|
|
2841
|
+
clientSocket.end(
|
|
2842
|
+
`HTTP/1.1 ${upstreamResponse.statusCode ?? 502} ${upstreamResponse.statusMessage || "WebSocket upgrade failed"}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`,
|
|
2843
|
+
);
|
|
2844
|
+
upstreamResponse.resume();
|
|
2845
|
+
});
|
|
2846
|
+
upstreamRequest.on("error", () => {
|
|
2847
|
+
if (!clientSocket.destroyed) {
|
|
2848
|
+
clientSocket.end(
|
|
2849
|
+
"HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\nContent-Length: 0\r\n\r\n",
|
|
2850
|
+
);
|
|
2851
|
+
}
|
|
2852
|
+
});
|
|
2853
|
+
upstreamRequest.end();
|
|
2854
|
+
});
|
|
2855
|
+
|
|
2296
2856
|
await new Promise<void>((resolve, reject) => {
|
|
2297
2857
|
server.once("error", reject);
|
|
2298
2858
|
server.listen(new URL(manifest.bridgeUrl).port, "127.0.0.1", () => {
|