@agent-native/core 0.92.9 → 0.92.11
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 +1 -1
- package/corpus/core/CHANGELOG.md +29 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +181 -15
- package/corpus/core/src/agent/run-manager.ts +31 -4
- package/corpus/core/src/agent/run-store.ts +80 -0
- package/corpus/core/src/cli/design-connect.ts +433 -151
- package/corpus/core/src/cli/skills.ts +14 -0
- package/corpus/core/src/client/AssistantChat.tsx +25 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
- package/corpus/core/src/client/agent-chat.ts +165 -1
- package/corpus/core/src/client/chat/index.ts +9 -1
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
- package/corpus/core/src/collab/awareness-store.ts +23 -12
- package/corpus/core/src/collab/client.ts +16 -5
- package/corpus/core/src/collab/index.ts +4 -0
- package/corpus/core/src/collab/recent-edits.ts +65 -2
- package/corpus/core/src/deploy/build.ts +133 -5
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
- package/corpus/core/src/server/builder-design-systems.ts +30 -4
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/create-recording.ts +2 -0
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
- package/corpus/templates/clips/actions/request-transcript.ts +102 -33
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +48 -4
- package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
- package/corpus/templates/clips/app/routes/record.tsx +17 -4
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +12 -2
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +6 -3
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
- package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
- package/corpus/templates/clips/shared/recording-core.ts +12 -0
- package/corpus/templates/content/AGENTS.md +7 -6
- package/corpus/templates/content/actions/_document-flush.ts +132 -17
- package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
- package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/pull-document.ts +5 -7
- package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
- package/corpus/templates/content/actions/push-notion-page.ts +10 -0
- package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
- package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
- package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/content/app/i18n-data.ts +18 -0
- package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
- package/corpus/templates/design/AGENTS.md +45 -10
- package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
- package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
- package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
- package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
- package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
- package/corpus/templates/design/actions/generate-design.ts +79 -12
- package/corpus/templates/design/actions/generate-screens.ts +57 -12
- package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
- package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
- package/corpus/templates/design/actions/import-design-source.ts +4 -31
- package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
- package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/design/actions/list-design-components.ts +104 -0
- package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
- package/corpus/templates/design/actions/open-component-source.ts +2 -4
- package/corpus/templates/design/actions/present-design-variants.ts +412 -16
- package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
- package/corpus/templates/design/actions/run-design-audit.ts +25 -3
- package/corpus/templates/design/actions/show-design-questions.ts +10 -0
- package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
- package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
- package/corpus/templates/design/actions/write-local-file.ts +25 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
- package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
- package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
- package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
- package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
- package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
- package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
- package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
- package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
- package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
- package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
- package/corpus/templates/design/app/components/design/types.ts +33 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
- package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
- package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
- package/corpus/templates/design/app/i18n-data.ts +1217 -10
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
- package/corpus/templates/design/app/lib/design-import.ts +24 -0
- package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
- package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
- package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
- package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
- package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +3 -6
- package/corpus/templates/design/e2e/helpers.ts +27 -6
- package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
- package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
- package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
- package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
- package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
- package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
- package/corpus/templates/design/server/plugins/db.ts +41 -0
- package/corpus/templates/design/server/source-workspace.ts +64 -0
- package/corpus/templates/design/shared/board-file.ts +13 -2
- package/corpus/templates/design/shared/canvas-math.ts +231 -33
- package/corpus/templates/design/shared/code-layer.ts +122 -8
- package/corpus/templates/design/shared/component-library.ts +161 -0
- package/corpus/templates/design/shared/db-conflict.ts +27 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
- package/corpus/templates/design/shared/figma-url.ts +149 -0
- package/corpus/templates/design/shared/pen-path.ts +16 -7
- package/corpus/templates/design/shared/source-mode.ts +1 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
- package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
- package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
- package/dist/agent/production-agent.d.ts +79 -6
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +161 -14
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +29 -5
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +46 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +61 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/cli/design-connect.d.ts +6 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +336 -139
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +14 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +19 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +38 -18
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +60 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +122 -1
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +36 -2
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +5 -0
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +19 -12
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +13 -5
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/index.d.ts +1 -0
- package/dist/collab/index.d.ts.map +1 -1
- package/dist/collab/index.js +1 -0
- package/dist/collab/index.js.map +1 -1
- package/dist/collab/recent-edits.d.ts +3 -1
- package/dist/collab/recent-edits.d.ts.map +1 -1
- package/dist/collab/recent-edits.js +57 -2
- package/dist/collab/recent-edits.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +24 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +102 -5
- package/dist/deploy/build.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +87 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-design-systems.d.ts +15 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +9 -4
- package/dist/server/builder-design-systems.js.map +1 -1
- package/package.json +1 -1
|
@@ -557,11 +557,11 @@ export async function prepareDesignConnectManifest(options = {}) {
|
|
|
557
557
|
status: "available",
|
|
558
558
|
reason: operation === "resolveNodeToFile"
|
|
559
559
|
? // resolveNodeToFile maps a runtime DOM node id (from the editor's
|
|
560
|
-
// 'select' payload) to { file, line, component } provenance.
|
|
561
|
-
//
|
|
562
|
-
//
|
|
563
|
-
//
|
|
564
|
-
"
|
|
560
|
+
// 'select' payload) to { file, line, component } provenance.
|
|
561
|
+
// React development builds expose jsxDEV call sites through the
|
|
562
|
+
// Fiber debug stack; other runtimes/builds can emit explicit DOM
|
|
563
|
+
// provenance attributes — see the help text below.
|
|
564
|
+
"React development builds resolve jsxDEV call sites automatically; other runtimes can emit data-source-file / data-source-line / data-component-name attributes."
|
|
565
565
|
: undefined,
|
|
566
566
|
})),
|
|
567
567
|
};
|
|
@@ -626,9 +626,10 @@ function sendText(res, statusCode, body, contentType) {
|
|
|
626
626
|
});
|
|
627
627
|
res.end(body);
|
|
628
628
|
}
|
|
629
|
-
function sendBytes(res, statusCode, body, headers) {
|
|
629
|
+
function sendBytes(res, statusCode, body, headers, contentLength = body.length) {
|
|
630
630
|
const responseHeaders = {
|
|
631
631
|
...bridgeCorsHeaders(res),
|
|
632
|
+
"content-length": String(contentLength),
|
|
632
633
|
};
|
|
633
634
|
for (const name of [
|
|
634
635
|
"content-type",
|
|
@@ -664,6 +665,23 @@ function readHeader(req, name) {
|
|
|
664
665
|
const value = req.headers[name];
|
|
665
666
|
return typeof value === "string" ? value : "";
|
|
666
667
|
}
|
|
668
|
+
/**
|
|
669
|
+
* Preserve only the browser request metadata a local dev server needs to
|
|
670
|
+
* classify Vite source-module and stylesheet requests. Agent Native's dev
|
|
671
|
+
* gateway intentionally varies source-file handling by `Sec-Fetch-Dest`; if
|
|
672
|
+
* the bridge drops it, React Router module URLs such as `/app/root.tsx` fall
|
|
673
|
+
* through to Nitro and 404. Keep this allowlist narrow: cookies, authorization,
|
|
674
|
+
* bridge tokens, origins, and referrers must never be forwarded upstream.
|
|
675
|
+
*/
|
|
676
|
+
function previewProxyRequestHeaders(req) {
|
|
677
|
+
const headers = {};
|
|
678
|
+
for (const name of ["accept", "sec-fetch-dest"]) {
|
|
679
|
+
const value = readHeader(req, name);
|
|
680
|
+
if (value)
|
|
681
|
+
headers[name] = value;
|
|
682
|
+
}
|
|
683
|
+
return headers;
|
|
684
|
+
}
|
|
667
685
|
function resolvePreviewSnapshotUrl(devServerUrl, rawUrl) {
|
|
668
686
|
const base = normalizeHttpUrl(devServerUrl);
|
|
669
687
|
const parsed = new URL(rawUrl?.trim() || "/", `${base}/`);
|
|
@@ -721,7 +739,7 @@ async function fetchPreviewSnapshot(devServerUrl, targetUrl, redirects = 0) {
|
|
|
721
739
|
clearTimeout(timeout);
|
|
722
740
|
}
|
|
723
741
|
}
|
|
724
|
-
async function fetchPreviewProxyResource(devServerUrl, targetUrl, redirects = 0) {
|
|
742
|
+
async function fetchPreviewProxyResource(devServerUrl, targetUrl, requestHeaders = {}, redirects = 0) {
|
|
725
743
|
if (redirects > 5) {
|
|
726
744
|
throw new Error("Too many redirects while proxying preview resource.");
|
|
727
745
|
}
|
|
@@ -732,6 +750,7 @@ async function fetchPreviewProxyResource(devServerUrl, targetUrl, redirects = 0)
|
|
|
732
750
|
method: "GET",
|
|
733
751
|
redirect: "manual",
|
|
734
752
|
signal: controller.signal,
|
|
753
|
+
headers: requestHeaders,
|
|
735
754
|
});
|
|
736
755
|
const location = response.headers.get("location");
|
|
737
756
|
if (location && response.status >= 300 && response.status < 400) {
|
|
@@ -740,7 +759,7 @@ async function fetchPreviewProxyResource(devServerUrl, targetUrl, redirects = 0)
|
|
|
740
759
|
if (!sameOrigin(redirected.toString(), devServerUrl)) {
|
|
741
760
|
throw new Error("Proxy redirect left the connected dev server.");
|
|
742
761
|
}
|
|
743
|
-
return fetchPreviewProxyResource(devServerUrl, redirected.toString(), redirects + 1);
|
|
762
|
+
return fetchPreviewProxyResource(devServerUrl, redirected.toString(), requestHeaders, redirects + 1);
|
|
744
763
|
}
|
|
745
764
|
return {
|
|
746
765
|
url: response.url || targetUrl,
|
|
@@ -867,6 +886,175 @@ async function assertPathInside(rootPath, targetPath) {
|
|
|
867
886
|
}
|
|
868
887
|
}
|
|
869
888
|
}
|
|
889
|
+
/**
|
|
890
|
+
* Resolve a bridge file to a stable lock key without following a leaf
|
|
891
|
+
* symlink. Missing parent directories are represented beneath their nearest
|
|
892
|
+
* existing real ancestor, so aliases through in-root directory symlinks share
|
|
893
|
+
* one mutex while first-time file creation remains supported.
|
|
894
|
+
*/
|
|
895
|
+
async function resolveSafeBridgeFileTarget(rootPath, targetPath) {
|
|
896
|
+
await assertPathInside(rootPath, targetPath);
|
|
897
|
+
const resolvedRoot = await fs.realpath(rootPath);
|
|
898
|
+
const absolutePath = path.resolve(rootPath, targetPath);
|
|
899
|
+
let existingAncestor = path.dirname(absolutePath);
|
|
900
|
+
let resolvedAncestor = null;
|
|
901
|
+
for (let depth = 0; depth < 32; depth += 1) {
|
|
902
|
+
try {
|
|
903
|
+
resolvedAncestor = await fs.realpath(existingAncestor);
|
|
904
|
+
break;
|
|
905
|
+
}
|
|
906
|
+
catch {
|
|
907
|
+
const parent = path.dirname(existingAncestor);
|
|
908
|
+
if (parent === existingAncestor)
|
|
909
|
+
break;
|
|
910
|
+
existingAncestor = parent;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (!resolvedAncestor) {
|
|
914
|
+
throw new Error(`Cannot resolve parent directory: ${absolutePath}`);
|
|
915
|
+
}
|
|
916
|
+
// Existing regular files get their own realpath as the lock key. Besides
|
|
917
|
+
// resolving in-root directory aliases, this canonicalizes case on the
|
|
918
|
+
// default macOS filesystem so `Button.tsx` and `button.tsx` cannot acquire
|
|
919
|
+
// separate mutexes for the same inode. assertPathInside already rejected a
|
|
920
|
+
// symlink leaf before this point.
|
|
921
|
+
const canonicalPath = (await fs.realpath(absolutePath).catch(() => null)) ??
|
|
922
|
+
path.resolve(resolvedAncestor, path.relative(existingAncestor, absolutePath));
|
|
923
|
+
if (canonicalPath !== resolvedRoot &&
|
|
924
|
+
!canonicalPath.startsWith(resolvedRoot + path.sep)) {
|
|
925
|
+
throw new Error("Path traversal detected: resolved target is outside root");
|
|
926
|
+
}
|
|
927
|
+
return { absolutePath, canonicalPath };
|
|
928
|
+
}
|
|
929
|
+
const bridgeWriteLocks = new Map();
|
|
930
|
+
/** Serialize read-check-write sequences for one canonical local file. */
|
|
931
|
+
async function withBridgeWriteLock(canonicalPath, work) {
|
|
932
|
+
const previous = bridgeWriteLocks.get(canonicalPath) ?? Promise.resolve();
|
|
933
|
+
let release;
|
|
934
|
+
const gate = new Promise((resolve) => {
|
|
935
|
+
release = resolve;
|
|
936
|
+
});
|
|
937
|
+
const queued = previous.catch(() => undefined).then(() => gate);
|
|
938
|
+
bridgeWriteLocks.set(canonicalPath, queued);
|
|
939
|
+
await previous.catch(() => undefined);
|
|
940
|
+
try {
|
|
941
|
+
return await work();
|
|
942
|
+
}
|
|
943
|
+
finally {
|
|
944
|
+
release();
|
|
945
|
+
if (bridgeWriteLocks.get(canonicalPath) === queued) {
|
|
946
|
+
bridgeWriteLocks.delete(canonicalPath);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
function contentVersionHash(content) {
|
|
951
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
952
|
+
}
|
|
953
|
+
/** Read an existing regular file without ever following a leaf symlink. */
|
|
954
|
+
async function readBridgeFileSnapshot(absolutePath) {
|
|
955
|
+
const noFollow = fsSync.constants.O_NOFOLLOW ?? 0;
|
|
956
|
+
let handle = null;
|
|
957
|
+
try {
|
|
958
|
+
handle = await fs.open(absolutePath, fsSync.constants.O_RDONLY | noFollow);
|
|
959
|
+
const stat = await handle.stat();
|
|
960
|
+
if (!stat.isFile()) {
|
|
961
|
+
throw new Error(`Bridge target is not a regular file: ${absolutePath}`);
|
|
962
|
+
}
|
|
963
|
+
const bytes = await handle.readFile();
|
|
964
|
+
return {
|
|
965
|
+
content: bytes.toString("utf8"),
|
|
966
|
+
versionHash: contentVersionHash(bytes),
|
|
967
|
+
mode: stat.mode & 0o777,
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
catch (error) {
|
|
971
|
+
const code = error instanceof Error && "code" in error
|
|
972
|
+
? error.code
|
|
973
|
+
: undefined;
|
|
974
|
+
if (code === "ENOENT")
|
|
975
|
+
return null;
|
|
976
|
+
throw error;
|
|
977
|
+
}
|
|
978
|
+
finally {
|
|
979
|
+
await handle?.close().catch(() => undefined);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
class BridgeVersionConflictError extends Error {
|
|
983
|
+
currentVersionHash;
|
|
984
|
+
constructor(currentVersionHash) {
|
|
985
|
+
super("version conflict");
|
|
986
|
+
this.currentVersionHash = currentVersionHash;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
class BridgePreconditionRequiredError extends Error {
|
|
990
|
+
constructor() {
|
|
991
|
+
super("expectedVersionHash is required");
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
function assertExpectedBridgeVersion(expectedVersionHash, currentVersionHash, requireExpectedVersionHash = false) {
|
|
995
|
+
// Preserve compatibility: callers that omit a hash retain the existing
|
|
996
|
+
// last-write-wins behavior, including creation. Compiled-source callers opt
|
|
997
|
+
// into exact compare-and-swap by sending the hash returned by read-file.
|
|
998
|
+
if (requireExpectedVersionHash && expectedVersionHash === undefined) {
|
|
999
|
+
throw new BridgePreconditionRequiredError();
|
|
1000
|
+
}
|
|
1001
|
+
if (expectedVersionHash !== undefined &&
|
|
1002
|
+
(currentVersionHash !== undefined
|
|
1003
|
+
? currentVersionHash !== expectedVersionHash
|
|
1004
|
+
: requireExpectedVersionHash)) {
|
|
1005
|
+
throw new BridgeVersionConflictError(currentVersionHash);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Replace a file durably without exposing a partial write: create an
|
|
1010
|
+
* O_EXCL/O_NOFOLLOW temp sibling, fsync it, revalidate confinement and the
|
|
1011
|
+
* expected content hash, rename atomically, then fsync the parent directory.
|
|
1012
|
+
*/
|
|
1013
|
+
async function atomicWriteBridgeFile(args) {
|
|
1014
|
+
const parent = path.dirname(path.resolve(args.rootPath, args.relPath));
|
|
1015
|
+
await fs.mkdir(parent, { recursive: true });
|
|
1016
|
+
const revalidated = await resolveSafeBridgeFileTarget(args.rootPath, args.relPath);
|
|
1017
|
+
if (revalidated.canonicalPath !== args.lockedCanonicalPath) {
|
|
1018
|
+
throw new Error("Bridge target changed while waiting for the write lock");
|
|
1019
|
+
}
|
|
1020
|
+
const basename = path.basename(revalidated.absolutePath);
|
|
1021
|
+
const tempPath = path.join(parent, `.${basename}.agent-native-${process.pid}-${crypto.randomBytes(8).toString("hex")}.tmp`);
|
|
1022
|
+
const noFollow = fsSync.constants.O_NOFOLLOW ?? 0;
|
|
1023
|
+
let tempHandle = null;
|
|
1024
|
+
try {
|
|
1025
|
+
tempHandle = await fs.open(tempPath, fsSync.constants.O_CREAT |
|
|
1026
|
+
fsSync.constants.O_EXCL |
|
|
1027
|
+
fsSync.constants.O_WRONLY |
|
|
1028
|
+
noFollow, args.originalMode ?? 0o666);
|
|
1029
|
+
await tempHandle.writeFile(args.content, "utf8");
|
|
1030
|
+
await tempHandle.sync();
|
|
1031
|
+
await tempHandle.close();
|
|
1032
|
+
tempHandle = null;
|
|
1033
|
+
// Re-check after the potentially slow temp write/fsync. This catches a
|
|
1034
|
+
// parent/leaf symlink swap and an external content edit before rename.
|
|
1035
|
+
const beforeRenameTarget = await resolveSafeBridgeFileTarget(args.rootPath, args.relPath);
|
|
1036
|
+
if (beforeRenameTarget.canonicalPath !== args.lockedCanonicalPath) {
|
|
1037
|
+
throw new Error("Bridge target changed during atomic write");
|
|
1038
|
+
}
|
|
1039
|
+
const current = await readBridgeFileSnapshot(beforeRenameTarget.absolutePath);
|
|
1040
|
+
assertExpectedBridgeVersion(args.expectedVersionHash, current?.versionHash, args.requireExpectedVersionHash);
|
|
1041
|
+
await fs.rename(tempPath, beforeRenameTarget.absolutePath);
|
|
1042
|
+
const directoryHandle = await fs.open(parent, "r").catch(() => null);
|
|
1043
|
+
if (directoryHandle) {
|
|
1044
|
+
try {
|
|
1045
|
+
await directoryHandle.sync();
|
|
1046
|
+
}
|
|
1047
|
+
finally {
|
|
1048
|
+
await directoryHandle.close();
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return contentVersionHash(args.content);
|
|
1052
|
+
}
|
|
1053
|
+
finally {
|
|
1054
|
+
await tempHandle?.close().catch(() => undefined);
|
|
1055
|
+
await fs.rm(tempPath, { force: true }).catch(() => undefined);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
870
1058
|
/** Allowed file extensions for write/apply-edit operations. */
|
|
871
1059
|
const ALLOWED_WRITE_EXTENSIONS = new Set([
|
|
872
1060
|
".html",
|
|
@@ -929,19 +1117,6 @@ function assertNotBlockedSecretPath(relPath) {
|
|
|
929
1117
|
throw new Error(`Access rejected: "${relPath}" matches a blocked secret-file pattern.`);
|
|
930
1118
|
}
|
|
931
1119
|
}
|
|
932
|
-
/**
|
|
933
|
-
* Compute a cheap, stable version identifier for a file from its stat: mtime
|
|
934
|
-
* plus size. Not a content hash — it is only meant to detect "did this file
|
|
935
|
-
* change on disk since the caller last read it", the same tradeoff the inline
|
|
936
|
-
* (SQL-backed) workspace provider's versionHash already makes. Returns
|
|
937
|
-
* undefined when the file does not exist (new-file case).
|
|
938
|
-
*/
|
|
939
|
-
async function computeVersionHash(absolutePath) {
|
|
940
|
-
const stat = await fs.stat(absolutePath).catch(() => null);
|
|
941
|
-
if (!stat)
|
|
942
|
-
return undefined;
|
|
943
|
-
return `${stat.mtimeMs}-${stat.size}`;
|
|
944
|
-
}
|
|
945
1120
|
function countOccurrences(haystack, needle) {
|
|
946
1121
|
if (!needle)
|
|
947
1122
|
return 0;
|
|
@@ -1236,6 +1411,18 @@ export async function startDesignConnectBridge(manifest, seedOrOptions) {
|
|
|
1236
1411
|
// other and boot a frame with another frame's identity. Keep keyed scripts
|
|
1237
1412
|
// for modern clients while retaining the unkeyed slot for older clients.
|
|
1238
1413
|
const liveEditBridgeScripts = new Map();
|
|
1414
|
+
// Identifies THIS bridge process's in-memory registry, minted fresh every
|
|
1415
|
+
// time the bridge boots. `liveEditBridgeScripts` above only lives in
|
|
1416
|
+
// process memory, so a bridge restart (crash, machine sleep/wake, manual
|
|
1417
|
+
// restart) silently empties it: any screen that registered a bridgeKey
|
|
1418
|
+
// before the restart now gets a 409 "unknown bridge key" from `/live-edit`
|
|
1419
|
+
// even though nothing about that screen actually changed. Echoing this id
|
|
1420
|
+
// on both the registration response and the 409 lets a client tell the two
|
|
1421
|
+
// cases apart — "this exact process never saw my key" (stale/typo, id
|
|
1422
|
+
// matches what it already has cached) vs. "the process restarted since I
|
|
1423
|
+
// registered" (id changed, safe to transparently re-POST `/live-edit-bridge`
|
|
1424
|
+
// and retry) — instead of guessing from the error text or retrying forever.
|
|
1425
|
+
const bridgeInstanceId = crypto.randomBytes(16).toString("hex");
|
|
1239
1426
|
const server = http.createServer((req, res) => {
|
|
1240
1427
|
const corsApproved = configureBridgeCors(req, res, configuredOrigins);
|
|
1241
1428
|
if (req.method === "OPTIONS") {
|
|
@@ -1284,6 +1471,7 @@ export async function startDesignConnectBridge(manifest, seedOrOptions) {
|
|
|
1284
1471
|
ok: true,
|
|
1285
1472
|
source: manifest.source,
|
|
1286
1473
|
appFingerprint: designConnectAppFingerprint(manifest),
|
|
1474
|
+
bridgeInstanceId,
|
|
1287
1475
|
});
|
|
1288
1476
|
return;
|
|
1289
1477
|
}
|
|
@@ -1334,6 +1522,7 @@ export async function startDesignConnectBridge(manifest, seedOrOptions) {
|
|
|
1334
1522
|
}
|
|
1335
1523
|
sendJson(res, 200, {
|
|
1336
1524
|
ok: true,
|
|
1525
|
+
bridgeInstanceId,
|
|
1337
1526
|
...(bridgeKey ? { bridgeKey } : {}),
|
|
1338
1527
|
});
|
|
1339
1528
|
}
|
|
@@ -1366,8 +1555,17 @@ export async function startDesignConnectBridge(manifest, seedOrOptions) {
|
|
|
1366
1555
|
if (includeEditorBridge &&
|
|
1367
1556
|
requestedBridgeKey &&
|
|
1368
1557
|
!editorBridgeScript) {
|
|
1558
|
+
// Machine-readable `code` + echoed `bridgeKey`/`bridgeInstanceId`
|
|
1559
|
+
// let a client distinguish "this bridge process restarted since
|
|
1560
|
+
// I last registered — safe to silently re-POST
|
|
1561
|
+
// /live-edit-bridge and retry" from a genuine caller bug,
|
|
1562
|
+
// instead of string-matching `error` (see bridgeInstanceId's
|
|
1563
|
+
// doc comment above for the full rationale).
|
|
1369
1564
|
sendJson(res, 409, {
|
|
1370
1565
|
ok: false,
|
|
1566
|
+
code: "unknown-bridge-key",
|
|
1567
|
+
bridgeKey: requestedBridgeKey,
|
|
1568
|
+
bridgeInstanceId,
|
|
1371
1569
|
error: "The requested live-edit bridge script is not registered. Reload the Design frame to register it again.",
|
|
1372
1570
|
});
|
|
1373
1571
|
return;
|
|
@@ -1467,155 +1665,154 @@ export async function startDesignConnectBridge(manifest, seedOrOptions) {
|
|
|
1467
1665
|
sendJson(res, 400, { ok: false, error: "relPath is required" });
|
|
1468
1666
|
return;
|
|
1469
1667
|
}
|
|
1470
|
-
await assertPathInside(manifest.rootPath, relPath);
|
|
1471
1668
|
assertNotBlockedSecretPath(relPath);
|
|
1472
|
-
const
|
|
1669
|
+
const initialTarget = await resolveSafeBridgeFileTarget(manifest.rootPath, relPath);
|
|
1473
1670
|
if (pathname === "/read-file") {
|
|
1474
1671
|
// Read-file: no extension restriction (agents need to read any
|
|
1475
1672
|
// non-secret file), but the secret-path blocklist above still
|
|
1476
1673
|
// applies to .env*, *.pem, *.key, id_rsa*, and anything under .git/.
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1481
|
-
catch (err) {
|
|
1482
|
-
const code = err instanceof Error &&
|
|
1483
|
-
"code" in err &&
|
|
1484
|
-
err.code;
|
|
1485
|
-
if (code === "ENOENT") {
|
|
1486
|
-
sendJson(res, 404, { ok: false, error: "file not found" });
|
|
1487
|
-
}
|
|
1488
|
-
else {
|
|
1489
|
-
sendJson(res, 500, {
|
|
1490
|
-
ok: false,
|
|
1491
|
-
error: `read failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
1492
|
-
});
|
|
1493
|
-
}
|
|
1674
|
+
const snapshot = await readBridgeFileSnapshot(initialTarget.absolutePath);
|
|
1675
|
+
if (!snapshot) {
|
|
1676
|
+
sendJson(res, 404, { ok: false, error: "file not found" });
|
|
1494
1677
|
return;
|
|
1495
1678
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1679
|
+
sendJson(res, 200, {
|
|
1680
|
+
ok: true,
|
|
1681
|
+
content: snapshot.content,
|
|
1682
|
+
versionHash: snapshot.versionHash,
|
|
1683
|
+
});
|
|
1498
1684
|
return;
|
|
1499
1685
|
}
|
|
1500
1686
|
// write-file and apply-edit only allow known text/code extensions.
|
|
1501
1687
|
assertAllowedExtension(relPath);
|
|
1502
|
-
// Optional optimistic-concurrency check: when the caller supplies
|
|
1503
|
-
// expectedVersionHash, compare it against the file's CURRENT hash
|
|
1504
|
-
// before writing. A missing file is treated as no-conflict (new
|
|
1505
|
-
// file case) so first-time writes always succeed.
|
|
1506
1688
|
const expectedVersionHash = typeof body["expectedVersionHash"] === "string"
|
|
1507
1689
|
? body["expectedVersionHash"]
|
|
1508
1690
|
: undefined;
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1691
|
+
const requireExpectedVersionHash = body["requireExpectedVersionHash"] === true;
|
|
1692
|
+
await withBridgeWriteLock(initialTarget.canonicalPath, async () => {
|
|
1693
|
+
const lockedTarget = await resolveSafeBridgeFileTarget(manifest.rootPath, relPath);
|
|
1694
|
+
if (lockedTarget.canonicalPath !== initialTarget.canonicalPath) {
|
|
1695
|
+
throw new Error("Bridge target changed while waiting for the write lock");
|
|
1696
|
+
}
|
|
1697
|
+
const existing = await readBridgeFileSnapshot(lockedTarget.absolutePath);
|
|
1698
|
+
assertExpectedBridgeVersion(expectedVersionHash, existing?.versionHash, requireExpectedVersionHash);
|
|
1699
|
+
if (pathname === "/write-file") {
|
|
1700
|
+
const content = typeof body["content"] === "string"
|
|
1701
|
+
? body["content"]
|
|
1702
|
+
: undefined;
|
|
1703
|
+
if (content === undefined) {
|
|
1704
|
+
sendJson(res, 400, {
|
|
1705
|
+
ok: false,
|
|
1706
|
+
error: "content is required for write-file",
|
|
1707
|
+
});
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
const versionHash = await atomicWriteBridgeFile({
|
|
1711
|
+
rootPath: manifest.rootPath,
|
|
1712
|
+
relPath,
|
|
1713
|
+
lockedCanonicalPath: initialTarget.canonicalPath,
|
|
1714
|
+
content,
|
|
1715
|
+
expectedVersionHash,
|
|
1716
|
+
requireExpectedVersionHash,
|
|
1717
|
+
originalMode: existing?.mode,
|
|
1517
1718
|
});
|
|
1719
|
+
sendJson(res, 200, { ok: true, relPath, versionHash });
|
|
1518
1720
|
return;
|
|
1519
1721
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1722
|
+
// /apply-edit supports either full replace ({content}) or one
|
|
1723
|
+
// exact search-and-replace. Both stay within this file's lock.
|
|
1724
|
+
if (typeof body["content"] === "string") {
|
|
1725
|
+
const versionHash = await atomicWriteBridgeFile({
|
|
1726
|
+
rootPath: manifest.rootPath,
|
|
1727
|
+
relPath,
|
|
1728
|
+
lockedCanonicalPath: initialTarget.canonicalPath,
|
|
1729
|
+
content: body["content"],
|
|
1730
|
+
expectedVersionHash,
|
|
1731
|
+
requireExpectedVersionHash,
|
|
1732
|
+
originalMode: existing?.mode,
|
|
1733
|
+
});
|
|
1734
|
+
sendJson(res, 200, {
|
|
1735
|
+
ok: true,
|
|
1736
|
+
relPath,
|
|
1737
|
+
method: "replace",
|
|
1738
|
+
versionHash,
|
|
1739
|
+
});
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
const search = typeof body["search"] === "string" ? body["search"] : undefined;
|
|
1743
|
+
const replace = typeof body["replace"] === "string"
|
|
1744
|
+
? body["replace"]
|
|
1524
1745
|
: undefined;
|
|
1525
|
-
if (
|
|
1746
|
+
if (search === undefined || replace === undefined) {
|
|
1526
1747
|
sendJson(res, 400, {
|
|
1527
1748
|
ok: false,
|
|
1528
|
-
error: "content
|
|
1749
|
+
error: "apply-edit requires either {content} for a full replace, or {search, replace} for a patch",
|
|
1529
1750
|
});
|
|
1530
1751
|
return;
|
|
1531
1752
|
}
|
|
1532
|
-
|
|
1533
|
-
await fs.writeFile(absolutePath, content, "utf8");
|
|
1534
|
-
const versionHash = await computeVersionHash(absolutePath);
|
|
1535
|
-
sendJson(res, 200, { ok: true, relPath, versionHash });
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
// /apply-edit: supports either full replace ({content}) or
|
|
1539
|
-
// search-and-replace ({search, replace}).
|
|
1540
|
-
if (typeof body["content"] === "string") {
|
|
1541
|
-
// Full-file replace via apply-edit — same as write-file but keeps
|
|
1542
|
-
// the endpoint semantically separate for callers that want to
|
|
1543
|
-
// distinguish intent.
|
|
1544
|
-
await fs.mkdir(path.dirname(absolutePath), { recursive: true });
|
|
1545
|
-
await fs.writeFile(absolutePath, body["content"], "utf8");
|
|
1546
|
-
const versionHash = await computeVersionHash(absolutePath);
|
|
1547
|
-
sendJson(res, 200, {
|
|
1548
|
-
ok: true,
|
|
1549
|
-
relPath,
|
|
1550
|
-
method: "replace",
|
|
1551
|
-
versionHash,
|
|
1552
|
-
});
|
|
1553
|
-
return;
|
|
1554
|
-
}
|
|
1555
|
-
const search = typeof body["search"] === "string" ? body["search"] : undefined;
|
|
1556
|
-
const replace = typeof body["replace"] === "string" ? body["replace"] : undefined;
|
|
1557
|
-
if (search === undefined || replace === undefined) {
|
|
1558
|
-
sendJson(res, 400, {
|
|
1559
|
-
ok: false,
|
|
1560
|
-
error: "apply-edit requires either {content} for a full replace, or {search, replace} for a patch",
|
|
1561
|
-
});
|
|
1562
|
-
return;
|
|
1563
|
-
}
|
|
1564
|
-
let existing;
|
|
1565
|
-
try {
|
|
1566
|
-
existing = await fs.readFile(absolutePath, "utf8");
|
|
1567
|
-
}
|
|
1568
|
-
catch (err) {
|
|
1569
|
-
const code = err instanceof Error &&
|
|
1570
|
-
"code" in err &&
|
|
1571
|
-
err.code;
|
|
1572
|
-
if (code === "ENOENT") {
|
|
1753
|
+
if (!existing) {
|
|
1573
1754
|
sendJson(res, 404, {
|
|
1574
1755
|
ok: false,
|
|
1575
1756
|
error: "file not found — use write-file to create new files",
|
|
1576
1757
|
});
|
|
1758
|
+
return;
|
|
1577
1759
|
}
|
|
1578
|
-
|
|
1579
|
-
sendJson(res,
|
|
1760
|
+
if (search.length === 0) {
|
|
1761
|
+
sendJson(res, 400, {
|
|
1580
1762
|
ok: false,
|
|
1581
|
-
error:
|
|
1763
|
+
error: "search string must not be empty",
|
|
1582
1764
|
});
|
|
1765
|
+
return;
|
|
1583
1766
|
}
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1767
|
+
const occurrenceCount = countOccurrences(existing.content, search);
|
|
1768
|
+
if (occurrenceCount === 0) {
|
|
1769
|
+
sendJson(res, 422, {
|
|
1770
|
+
ok: false,
|
|
1771
|
+
error: "search string not found in file",
|
|
1772
|
+
});
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
if (occurrenceCount > 1) {
|
|
1776
|
+
sendJson(res, 422, {
|
|
1777
|
+
ok: false,
|
|
1778
|
+
error: "search string is ambiguous; it appears more than once in the file",
|
|
1779
|
+
});
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
const updated = existing.content.replace(search, replace);
|
|
1783
|
+
const versionHash = await atomicWriteBridgeFile({
|
|
1784
|
+
rootPath: manifest.rootPath,
|
|
1785
|
+
relPath,
|
|
1786
|
+
lockedCanonicalPath: initialTarget.canonicalPath,
|
|
1787
|
+
content: updated,
|
|
1788
|
+
expectedVersionHash,
|
|
1789
|
+
requireExpectedVersionHash,
|
|
1790
|
+
originalMode: existing.mode,
|
|
1590
1791
|
});
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1792
|
+
sendJson(res, 200, {
|
|
1793
|
+
ok: true,
|
|
1794
|
+
relPath,
|
|
1795
|
+
method: "patch",
|
|
1796
|
+
versionHash,
|
|
1797
|
+
});
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
catch (err) {
|
|
1801
|
+
if (err instanceof BridgePreconditionRequiredError) {
|
|
1802
|
+
sendJson(res, 428, {
|
|
1596
1803
|
ok: false,
|
|
1597
|
-
error: "
|
|
1804
|
+
error: "expectedVersionHash is required",
|
|
1598
1805
|
});
|
|
1599
1806
|
return;
|
|
1600
1807
|
}
|
|
1601
|
-
if (
|
|
1602
|
-
sendJson(res,
|
|
1808
|
+
if (err instanceof BridgeVersionConflictError) {
|
|
1809
|
+
sendJson(res, 409, {
|
|
1603
1810
|
ok: false,
|
|
1604
|
-
error: "
|
|
1811
|
+
error: "version conflict",
|
|
1812
|
+
currentVersionHash: err.currentVersionHash,
|
|
1605
1813
|
});
|
|
1606
1814
|
return;
|
|
1607
1815
|
}
|
|
1608
|
-
const updated = existing.replace(search, replace);
|
|
1609
|
-
await fs.writeFile(absolutePath, updated, "utf8");
|
|
1610
|
-
const versionHash = await computeVersionHash(absolutePath);
|
|
1611
|
-
sendJson(res, 200, {
|
|
1612
|
-
ok: true,
|
|
1613
|
-
relPath,
|
|
1614
|
-
method: "patch",
|
|
1615
|
-
versionHash,
|
|
1616
|
-
});
|
|
1617
|
-
}
|
|
1618
|
-
catch (err) {
|
|
1619
1816
|
sendJson(res, 500, {
|
|
1620
1817
|
ok: false,
|
|
1621
1818
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -1636,8 +1833,8 @@ export async function startDesignConnectBridge(manifest, seedOrOptions) {
|
|
|
1636
1833
|
void (async () => {
|
|
1637
1834
|
try {
|
|
1638
1835
|
const targetUrl = resolvePreviewProxyUrl(manifest.devServerUrl, req.url);
|
|
1639
|
-
const proxied = await fetchPreviewProxyResource(manifest.devServerUrl, targetUrl);
|
|
1640
|
-
sendBytes(res, proxied.status >= 400 ? proxied.status : 200, req.method === "HEAD" ? Buffer.alloc(0) : proxied.body, proxied.headers);
|
|
1836
|
+
const proxied = await fetchPreviewProxyResource(manifest.devServerUrl, targetUrl, previewProxyRequestHeaders(req));
|
|
1837
|
+
sendBytes(res, proxied.status >= 400 ? proxied.status : 200, req.method === "HEAD" ? Buffer.alloc(0) : proxied.body, proxied.headers, proxied.body.length);
|
|
1641
1838
|
}
|
|
1642
1839
|
catch (err) {
|
|
1643
1840
|
sendJson(res, 400, {
|
|
@@ -1657,7 +1854,7 @@ export async function startDesignConnectBridge(manifest, seedOrOptions) {
|
|
|
1657
1854
|
resolve();
|
|
1658
1855
|
});
|
|
1659
1856
|
});
|
|
1660
|
-
return { server, manifest, bridgeToken, previewToken };
|
|
1857
|
+
return { server, manifest, bridgeToken, previewToken, bridgeInstanceId };
|
|
1661
1858
|
}
|
|
1662
1859
|
/**
|
|
1663
1860
|
* Resolve the design app URL from an explicit value or environment variables.
|
|
@@ -1786,12 +1983,11 @@ Options:
|
|
|
1786
1983
|
|
|
1787
1984
|
Element provenance (resolveNodeToFile):
|
|
1788
1985
|
The design editor can map a selected DOM element back to its source file,
|
|
1789
|
-
line, and
|
|
1790
|
-
attributes at build time. Add one of the following to your app's build:
|
|
1986
|
+
line, and component name using one of these provenance sources:
|
|
1791
1987
|
|
|
1792
|
-
•
|
|
1793
|
-
|
|
1794
|
-
|
|
1988
|
+
• React development builds with jsxDEV enabled (the default):
|
|
1989
|
+
Design reads the selected element's development-only Fiber debug stack.
|
|
1990
|
+
React does not emit data-source-* DOM attributes automatically.
|
|
1795
1991
|
|
|
1796
1992
|
• A Babel source plugin (e.g. babel-plugin-react-source or a custom plugin):
|
|
1797
1993
|
Emits data-source-file="src/Button.tsx" data-source-line="12"
|
|
@@ -1800,7 +1996,8 @@ Element provenance (resolveNodeToFile):
|
|
|
1800
1996
|
• data-loc="src/Button.tsx:12:4" shorthand attribute (Babel source convention):
|
|
1801
1997
|
The bridge parses this as { sourceFile, line, column } automatically.
|
|
1802
1998
|
|
|
1803
|
-
|
|
1999
|
+
In production React builds and other runtimes without explicit attributes,
|
|
2000
|
+
the editor still works but exact element provenance may be absent.
|
|
1804
2001
|
Cross-origin localhost iframes cannot be read regardless of attributes (CSP).`);
|
|
1805
2002
|
}
|
|
1806
2003
|
function removeDaemonFlag(argv) {
|