@agent-native/core 0.129.1 → 0.130.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 +78 -0
- package/corpus/core/docs/content/cloneable-saas.mdx +114 -0
- package/corpus/core/docs/content/creating-templates.mdx +28 -2
- package/corpus/core/docs/content/syncing-template-changes.mdx +7 -0
- package/corpus/core/docs/content/template-clips-developers.mdx +12 -5
- package/corpus/core/docs/content/template-dispatch-developers.mdx +1 -1
- package/corpus/core/docs/content/template-dispatch.mdx +1 -1
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action-ui.ts +1 -0
- package/corpus/core/src/action.ts +5 -1
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +15 -5
- package/corpus/core/src/agent/harness/runner.ts +5 -0
- package/corpus/core/src/agent/production-agent.ts +33 -0
- package/corpus/core/src/agent/run-loop-with-resume.ts +5 -0
- package/corpus/core/src/agent/run-manager.ts +126 -1
- package/corpus/core/src/agent/thread-data-builder.ts +47 -2
- package/corpus/core/src/agent/types.ts +2 -0
- package/corpus/core/src/chat-threads/store.ts +4 -5
- package/corpus/core/src/cli/create.ts +1139 -65
- package/corpus/core/src/cli/design-connect.ts +3 -7
- package/corpus/core/src/cli/index.ts +5 -3
- package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +83 -47
- package/corpus/core/src/client/AssistantChat.tsx +27 -16
- package/corpus/core/src/client/agent-chat-adapter.ts +24 -0
- package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +17 -2
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/message-components.tsx +104 -1
- package/corpus/core/src/client/chat/run-recovery.tsx +15 -3
- package/corpus/core/src/client/chat/widgets/DataTableWidget.tsx +10 -29
- package/corpus/core/src/client/chat/widgets/WorkspaceFileWidget.tsx +71 -0
- package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +22 -0
- package/corpus/core/src/client/chat/widgets/workspace-file-result.ts +49 -0
- package/corpus/core/src/client/embed-auth.ts +2 -0
- package/corpus/core/src/client/org/InvitationBanner.tsx +14 -1
- package/corpus/core/src/client/org/OrgSwitcher.tsx +23 -0
- package/corpus/core/src/client/org/TeamPage.tsx +130 -0
- package/corpus/core/src/client/org/WorkspaceNotice.tsx +85 -0
- package/corpus/core/src/client/org/hooks.ts +14 -0
- package/corpus/core/src/client/org/index.ts +2 -0
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +27 -40
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +134 -51
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +7 -0
- package/corpus/core/src/client/resources/use-resources.ts +6 -0
- package/corpus/core/src/client/use-action.ts +3 -1
- package/corpus/core/src/client/widgets/index.ts +1 -0
- package/corpus/core/src/coding-tools/run-code.ts +2 -1
- package/corpus/core/src/db/client.ts +28 -4
- package/corpus/core/src/deploy/build.ts +31 -3
- package/corpus/core/src/eject/remote-mcp-presets.ts +1 -1
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/index.ts +1 -0
- package/corpus/core/src/integrations/google-docs-poller.ts +3 -0
- package/corpus/core/src/integrations/webhook-handler.ts +28 -11
- package/corpus/core/src/jobs/scheduler.ts +4 -0
- package/corpus/core/src/localization/default-messages.ts +3 -2
- package/corpus/core/src/mcp/build-server.ts +20 -2
- package/corpus/core/src/observability/traces.ts +48 -12
- package/corpus/core/src/org/handlers.ts +52 -1
- package/corpus/core/src/org/migrations.ts +4 -0
- package/corpus/core/src/org/plugin.ts +14 -0
- package/corpus/core/src/org/schema.ts +1 -0
- package/corpus/core/src/org/types.ts +8 -0
- package/corpus/core/src/org/workspace-url.ts +74 -0
- package/corpus/core/src/resources/handlers.ts +35 -5
- package/corpus/core/src/scripts/runner.ts +183 -5
- package/corpus/core/src/server/action-routes.ts +32 -2
- package/corpus/core/src/server/agent-chat-plugin.ts +29 -0
- package/corpus/core/src/server/agent-teams.ts +10 -1
- package/corpus/core/src/server/auth.ts +33 -7
- package/corpus/core/src/server/embed-route.ts +9 -0
- package/corpus/core/src/server/embed-session.ts +62 -3
- package/corpus/core/src/server/open-route.ts +20 -6
- package/corpus/core/src/server/prompts/framework-core-compact.ts +1 -1
- package/corpus/core/src/server/prompts/framework-core.ts +1 -0
- package/corpus/core/src/server/request-context.ts +36 -1
- package/corpus/core/src/shared/embed-auth.ts +1 -0
- package/corpus/core/src/shared/html-document.ts +115 -0
- package/corpus/core/src/shared/index.ts +1 -0
- package/corpus/core/src/sharing/access.ts +8 -1
- package/corpus/core/src/sharing/registry.ts +11 -2
- package/corpus/core/src/workspace-files/actions.ts +100 -0
- package/corpus/core/src/workspace-files/tool.ts +4 -0
- package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +4 -0
- package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +9 -1
- package/corpus/templates/analytics/AGENTS.md +17 -24
- package/corpus/templates/analytics/actions/data-source-status.ts +11 -1
- package/corpus/templates/analytics/app/i18n-data.ts +34 -0
- package/corpus/templates/analytics/app/lib/data-source-status.ts +86 -0
- package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +9 -0
- package/corpus/templates/analytics/app/pages/DataSources.tsx +184 -20
- package/corpus/templates/analytics/changelog/2026-07-28-native-analytics-data-sources-no-longer-show-duplicate-mc.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-exports-now-appear-in-chat-with-a-direct-download-instead-of.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-queued-chat-prompts-no-longer-resend-after-analytics-navigat.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-scheduled-dashboard-emails-load-every-panel-reliably-without.md +6 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +6 -3
- package/corpus/templates/analytics/server/lib/credential-keys.ts +1 -1
- package/corpus/templates/analytics/server/lib/hubspot.ts +1 -1
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +55 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +123 -27
- package/corpus/templates/analytics/server/routes/[...page].get.ts +2 -7
- package/corpus/templates/analytics/vite.config.ts +7 -0
- package/corpus/templates/brain/actions/list-connection-providers.ts +100 -2
- package/corpus/templates/brain/app/lib/brain.ts +10 -0
- package/corpus/templates/brain/app/routes/sources.tsx +15 -4
- package/corpus/templates/brain/changelog/2026-07-29-brain-automatically-retries-failed-source-syncs-and-guides-y.md +6 -0
- package/corpus/templates/brain/jobs/process-ingest-queue.ts +13 -0
- package/corpus/templates/brain/server/jobs/sync-sources.ts +27 -22
- package/corpus/templates/brain/server/plugins/agent-chat.ts +2 -1
- package/corpus/templates/brain/server/routes/api/brain/queue/run.post.ts +7 -1
- package/corpus/templates/clips/AGENTS.md +1 -1
- package/corpus/templates/clips/actions/create-recording-agent-link.ts +11 -1
- package/corpus/templates/clips/actions/update-comment.ts +68 -0
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +0 -1
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +221 -65
- package/corpus/templates/clips/app/components/player/video-player.tsx +101 -27
- package/corpus/templates/clips/app/i18n/en-US.ts +2 -1
- package/corpus/templates/clips/app/routes/bug-report.done.tsx +48 -27
- package/corpus/templates/clips/app/routes/bug-report.tsx +43 -4
- package/corpus/templates/clips/app/routes/record.tsx +2 -0
- package/corpus/templates/clips/changelog/2026-07-29-clip-playback-now-recovers-instead-of-getting-stuck-when-a-b.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-29-comments-can-now-be-edited-after-posting.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-29-download-actions-now-use-a-shorter-clearer-label.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-29-embedded-bug-reports-now-return-a-temporary-agent-readable.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +21 -1
- package/corpus/templates/clips/shared/bug-report.ts +99 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +7 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +5 -2
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +20 -8
- package/corpus/templates/content/changelog/2026-07-29-missing-databases-no-longer-trigger-repeated-personal-view-e.md +6 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +83 -47
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +474 -55
- package/corpus/templates/design/.generated/bridge/embedded-wheel.generated.ts +3 -0
- package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +18 -1
- package/corpus/templates/design/.generated/bridge/source-location.generated.ts +348 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +2 -7
- package/corpus/templates/design/actions/apply-visual-edit.ts +7 -1
- package/corpus/templates/design/actions/connect-localhost.ts +5 -9
- package/corpus/templates/design/actions/create-component.ts +16 -13
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +2 -7
- package/corpus/templates/design/actions/list-design-source-capabilities.ts +4 -7
- package/corpus/templates/design/actions/list-local-files.ts +26 -50
- package/corpus/templates/design/actions/list-localhost-connections.ts +2 -7
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +2 -2
- package/corpus/templates/design/actions/open-visual-edit.ts +234 -135
- package/corpus/templates/design/actions/read-local-file.ts +26 -50
- package/corpus/templates/design/actions/request-localhost-write-consent.ts +2 -7
- package/corpus/templates/design/actions/write-local-file.ts +31 -73
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +307 -133
- package/corpus/templates/design/app/components/design/EditPanel.tsx +94 -22
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +137 -60
- package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +75 -14
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +755 -97
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +8 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +24 -1
- package/corpus/templates/design/app/components/design/bridge/source-location.bridge.ts +530 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbenchLoader.tsx +278 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +32 -12
- package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-setup.ts +14 -3
- package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +87 -24
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +36 -0
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +2 -1
- package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +48 -8
- package/corpus/templates/design/app/components/design/design-canvas/content-size-report.ts +3 -13
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +4 -2
- package/corpus/templates/design/app/components/design/design-canvas/hit-test.ts +3 -13
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +3 -1
- package/corpus/templates/design/app/components/design/edit-panel/inspect-code-source.ts +93 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +4 -2
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +35 -0
- package/corpus/templates/design/app/components/design/multi-screen/iframe-targeting.ts +24 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -0
- package/corpus/templates/design/app/components/design/types.ts +29 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +7 -4
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +153 -46
- package/corpus/templates/design/app/i18n-responsive-interact.ts +12 -12
- package/corpus/templates/design/app/lib/design-editor-route.ts +23 -0
- package/corpus/templates/design/app/pages/VisualEdit.tsx +19 -6
- package/corpus/templates/design/app/pages/design-editor/action-error.ts +19 -0
- package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +29 -0
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +6 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +277 -15
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +75 -9
- package/corpus/templates/design/app/pages/design-editor/layer-state-scope.ts +28 -0
- package/corpus/templates/design/app/pages/design-editor/live-screen-layer-drop.ts +41 -0
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +496 -41
- package/corpus/templates/design/app/pages/design-editor/pending-live-edit-unload-guard.ts +27 -0
- package/corpus/templates/design/app/pages/design-editor/pending-structure-verification.ts +10 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +168 -9
- package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +41 -3
- package/corpus/templates/design/app/pages/design-editor/screen-command-utils.ts +5 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +42 -13
- package/corpus/templates/design/app/pages/design-editor/source-location.ts +180 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +17 -0
- package/corpus/templates/design/app/public-routes.ts +1 -0
- package/corpus/templates/design/app/routes/visual-edit.$id.tsx +15 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +1 -1
- package/corpus/templates/design/changelog/2026-07-28-deleting-a-selected-element-now-removes-it-from-the-live-pre.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-deleting-an-element-on-a-connected-localhost-screen-now-queu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-shows-a-responsive-device-preview-with-pre.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-layer-hide-and-lock-now-apply-to-the-live-preview-instead-of.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-layers-panel-selections-on-a-connected-localhost-screen-can-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-locked-layers-now-show-a-subtle-dashed-outline-in-the-live-p.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-the-apply-design-updates-button-now-counts-pending-live-text.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-the-design-canvas-no-longer-stalls-on-a-render-loop-while-a-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-visual-edit-on-a-local-dev-server-no-longer-requires-signing.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-visual-edits-on-a-react-19-app-no-longer-report-a-source-lin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-an-edit-that-cannot-be-written-to-source-now-names-the-reaso.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-applying-a-primitive-dropped-onto-a-connected-localhost-scre.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-changing-a-style-in-the-inspector-now-updates-a-connected-lo.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-choosing-edit-or-annotate-from-a-focused-screen-now-returns-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-concurrent-source-edits-now-return-a-clear-conflict-response.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-creating-a-component-from-an-element-that-repeats-in-the-sou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-deleting-an-element-you-just-dropped-onto-a-live-screen-no-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-dragging-a-layer-across-screens-no-longer-dies-when-the-poin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-dragging-an-element-from-a-stored-screen-into-a-live-localho.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-drawing-a-box-on-the-canvas-and-dragging-it-into-a-connected.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-fixed-a-loading-screen-flicker-when-reopening-designs-with-a.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-interact-mode-has-edit-and-annotate-buttons-again-so-you-can.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-layer-groups-can-be-dropped-onto-live-localhost-screens.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-localhost-source-errors-now-say-what-actually-went-wrong-whi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-opening-or-refreshing-a-live-localhost-screen-in-single-scre.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-redo-after-undoing-a-drop-onto-a-live-screen-puts-the-elemen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-shapes-can-be-drawn-directly-onto-live-localhost-screens.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-shapes-drawn-on-the-canvas-board-now-appear-immediately-inst.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-style-edits-on-a-running-localhost-screen-now-preview-live-a.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-the-apply-design-updates-control-now-appears-as-a-clean-conn.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-visual-edits-on-a-mapped-component-instance-now-tell-the-age.md +6 -0
- package/corpus/templates/design/server/lib/design-data-access.ts +21 -7
- package/corpus/templates/design/server/lib/localhost-connection.ts +240 -0
- package/corpus/templates/design/server/routes/[...page].get.ts +2 -5
- package/corpus/templates/design/server/source-workspace.ts +2 -0
- package/corpus/templates/design/shared/code-layer.ts +43 -0
- package/corpus/templates/design/shared/html-content.ts +26 -0
- package/corpus/templates/design/shared/html-integrity.ts +21 -1
- package/corpus/templates/design/shared/local-jsx-visual-edit.ts +15 -0
- package/corpus/templates/design/shared/source-mode.ts +76 -0
- package/corpus/templates/dispatch/changelog/2026-07-29-workspace-members-without-admin-access-now-see-a-clear-permi.md +6 -0
- package/corpus/templates/dispatch/server/lib/usage-metrics.ts +2 -1
- package/corpus/templates/plan/app/lib/plan-annotation-runtime.ts +3 -4
- package/corpus/templates/plan/server/routes/[...page].get.ts +2 -5
- package/corpus/templates/slides/actions/patch-deck.ts +29 -17
- package/corpus/toolkit/CHANGELOG.md +6 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +3 -2
- package/dist/action-ui.d.ts +1 -0
- package/dist/action-ui.d.ts.map +1 -1
- package/dist/action-ui.js +1 -0
- package/dist/action-ui.js.map +1 -1
- package/dist/action.d.ts +5 -1
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +13 -5
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/harness/runner.d.ts.map +1 -1
- package/dist/agent/harness/runner.js +5 -0
- package/dist/agent/harness/runner.js.map +1 -1
- package/dist/agent/production-agent.d.ts +16 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +18 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +6 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-manager.d.ts +14 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +91 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +3 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +35 -3
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/agent/types.d.ts +2 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +4 -6
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/create.d.ts +66 -6
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +765 -66
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +2 -7
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/index.js +5 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.js +83 -47
- package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +16 -9
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +21 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.js +8 -2
- package/dist/client/blocks/library/wireframe-kit.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/chat/message-components.d.ts +17 -0
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +24 -2
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts +6 -0
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +13 -3
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat/widgets/DataTableWidget.d.ts.map +1 -1
- package/dist/client/chat/widgets/DataTableWidget.js +2 -3
- package/dist/client/chat/widgets/DataTableWidget.js.map +1 -1
- package/dist/client/chat/widgets/WorkspaceFileWidget.d.ts +5 -0
- package/dist/client/chat/widgets/WorkspaceFileWidget.d.ts.map +1 -0
- package/dist/client/chat/widgets/WorkspaceFileWidget.js +34 -0
- package/dist/client/chat/widgets/WorkspaceFileWidget.js.map +1 -0
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js +13 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/client/chat/widgets/workspace-file-result.d.ts +12 -0
- package/dist/client/chat/widgets/workspace-file-result.d.ts.map +1 -0
- package/dist/client/chat/widgets/workspace-file-result.js +34 -0
- package/dist/client/chat/widgets/workspace-file-result.js.map +1 -0
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +2 -1
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/org/InvitationBanner.d.ts +5 -2
- package/dist/client/org/InvitationBanner.d.ts.map +1 -1
- package/dist/client/org/InvitationBanner.js +9 -2
- package/dist/client/org/InvitationBanner.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +3 -2
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +27 -3
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/org/WorkspaceNotice.d.ts +18 -0
- package/dist/client/org/WorkspaceNotice.d.ts.map +1 -0
- package/dist/client/org/WorkspaceNotice.js +56 -0
- package/dist/client/org/WorkspaceNotice.js.map +1 -0
- package/dist/client/org/hooks.d.ts +1 -0
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/org/hooks.js +12 -0
- package/dist/client/org/hooks.js.map +1 -1
- package/dist/client/org/index.d.ts +2 -1
- package/dist/client/org/index.d.ts.map +1 -1
- package/dist/client/org/index.js +2 -1
- package/dist/client/org/index.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +8 -12
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts +3 -0
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +49 -20
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +3 -0
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/resources/use-resources.d.ts +1 -0
- package/dist/client/resources/use-resources.d.ts.map +1 -1
- package/dist/client/resources/use-resources.js +3 -0
- package/dist/client/resources/use-resources.js.map +1 -1
- package/dist/client/use-action.d.ts +3 -1
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +3 -1
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/widgets/index.d.ts +1 -1
- package/dist/client/widgets/index.d.ts.map +1 -1
- package/dist/client/widgets/index.js +1 -1
- package/dist/client/widgets/index.js.map +1 -1
- package/dist/coding-tools/run-code.js +2 -1
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +24 -3
- package/dist/db/client.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +25 -3
- package/dist/deploy/build.js.map +1 -1
- package/dist/eject/remote-mcp-presets.js +1 -1
- package/dist/eject/remote-mcp-presets.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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/google-docs-poller.d.ts.map +1 -1
- package/dist/integrations/google-docs-poller.js +4 -1
- package/dist/integrations/google-docs-poller.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +23 -11
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/jobs/scheduler.js +4 -0
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/localization/default-messages.d.ts +3 -2
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +3 -2
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +10 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +27 -7
- package/dist/observability/traces.js.map +1 -1
- package/dist/org/handlers.d.ts +15 -0
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +45 -1
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +4 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/org/plugin.d.ts +1 -0
- package/dist/org/plugin.d.ts.map +1 -1
- package/dist/org/plugin.js +10 -1
- package/dist/org/plugin.js.map +1 -1
- package/dist/org/schema.d.ts +19 -0
- package/dist/org/schema.d.ts.map +1 -1
- package/dist/org/schema.js +1 -0
- package/dist/org/schema.js.map +1 -1
- package/dist/org/types.d.ts +8 -0
- package/dist/org/types.d.ts.map +1 -1
- package/dist/org/types.js.map +1 -1
- package/dist/org/workspace-url.d.ts +32 -0
- package/dist/org/workspace-url.d.ts.map +1 -0
- package/dist/org/workspace-url.js +63 -0
- package/dist/org/workspace-url.js.map +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +7 -7
- package/dist/resources/handlers.d.ts +2 -3
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +25 -5
- package/dist/resources/handlers.js.map +1 -1
- package/dist/scripts/runner.d.ts +17 -0
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +130 -5
- package/dist/scripts/runner.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +24 -2
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +26 -1
- 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/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +10 -1
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/auth.d.ts +22 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +33 -7
- package/dist/server/auth.js.map +1 -1
- package/dist/server/embed-route.d.ts.map +1 -1
- package/dist/server/embed-route.js +6 -1
- package/dist/server/embed-route.js.map +1 -1
- package/dist/server/embed-session.d.ts +7 -0
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +46 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/server/open-route.d.ts.map +1 -1
- package/dist/server/open-route.js +17 -7
- package/dist/server/open-route.js.map +1 -1
- package/dist/server/prompts/framework-core-compact.js +1 -1
- package/dist/server/prompts/framework-core-compact.js.map +1 -1
- package/dist/server/prompts/framework-core.d.ts.map +1 -1
- package/dist/server/prompts/framework-core.js +1 -0
- package/dist/server/prompts/framework-core.js.map +1 -1
- package/dist/server/request-context.d.ts +26 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +18 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/shared/embed-auth.d.ts +1 -0
- package/dist/shared/embed-auth.d.ts.map +1 -1
- package/dist/shared/embed-auth.js +1 -0
- package/dist/shared/embed-auth.js.map +1 -1
- package/dist/shared/html-document.d.ts +12 -0
- package/dist/shared/html-document.d.ts.map +1 -0
- package/dist/shared/html-document.js +92 -0
- package/dist/shared/html-document.js.map +1 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/sharing/access.d.ts +1 -0
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +8 -2
- package/dist/sharing/access.js.map +1 -1
- package/dist/sharing/registry.d.ts +3 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js.map +1 -1
- package/dist/workspace-files/actions.d.ts +28 -0
- package/dist/workspace-files/actions.d.ts.map +1 -0
- package/dist/workspace-files/actions.js +75 -0
- package/dist/workspace-files/actions.js.map +1 -0
- package/dist/workspace-files/tool.d.ts.map +1 -1
- package/dist/workspace-files/tool.js +4 -0
- package/dist/workspace-files/tool.js.map +1 -1
- package/docs/content/cloneable-saas.mdx +114 -0
- package/docs/content/creating-templates.mdx +28 -2
- package/docs/content/syncing-template-changes.mdx +7 -0
- package/docs/content/template-clips-developers.mdx +12 -5
- package/docs/content/template-dispatch-developers.mdx +1 -1
- package/docs/content/template-dispatch.mdx +1 -1
- package/package.json +2 -2
- package/src/action-ui.ts +1 -0
- package/src/action.ts +5 -1
- package/src/agent/engine/ai-sdk-engine.ts +15 -5
- package/src/agent/harness/runner.ts +5 -0
- package/src/agent/production-agent.ts +33 -0
- package/src/agent/run-loop-with-resume.ts +5 -0
- package/src/agent/run-manager.ts +126 -1
- package/src/agent/thread-data-builder.ts +47 -2
- package/src/agent/types.ts +2 -0
- package/src/chat-threads/store.ts +4 -5
- package/src/cli/create.ts +1139 -65
- package/src/cli/design-connect.ts +3 -7
- package/src/cli/index.ts +5 -3
- package/src/cli/skills-content/design-visual-edit-skill.ts +83 -47
- package/src/client/AssistantChat.tsx +27 -16
- package/src/client/agent-chat-adapter.ts +24 -0
- package/src/client/blocks/library/wireframe-kit.tsx +17 -2
- package/src/client/chat/index.ts +1 -0
- package/src/client/chat/message-components.tsx +104 -1
- package/src/client/chat/run-recovery.tsx +15 -3
- package/src/client/chat/widgets/DataTableWidget.tsx +10 -29
- package/src/client/chat/widgets/WorkspaceFileWidget.tsx +71 -0
- package/src/client/chat/widgets/builtin-tool-renderers.tsx +22 -0
- package/src/client/chat/widgets/workspace-file-result.ts +49 -0
- package/src/client/embed-auth.ts +2 -0
- package/src/client/org/InvitationBanner.tsx +14 -1
- package/src/client/org/OrgSwitcher.tsx +23 -0
- package/src/client/org/TeamPage.tsx +130 -0
- package/src/client/org/WorkspaceNotice.tsx +85 -0
- package/src/client/org/hooks.ts +14 -0
- package/src/client/org/index.ts +2 -0
- package/src/client/resources/McpIntegrationDialog.tsx +27 -40
- package/src/client/resources/ResourcesPanel.tsx +134 -51
- package/src/client/resources/mcp-integration-catalog.ts +7 -0
- package/src/client/resources/use-resources.ts +6 -0
- package/src/client/use-action.ts +3 -1
- package/src/client/widgets/index.ts +1 -0
- package/src/coding-tools/run-code.ts +2 -1
- package/src/db/client.ts +28 -4
- package/src/deploy/build.ts +31 -3
- package/src/eject/remote-mcp-presets.ts +1 -1
- package/src/index.browser.ts +1 -0
- package/src/index.ts +1 -0
- package/src/integrations/google-docs-poller.ts +3 -0
- package/src/integrations/webhook-handler.ts +28 -11
- package/src/jobs/scheduler.ts +4 -0
- package/src/localization/default-messages.ts +3 -2
- package/src/mcp/build-server.ts +20 -2
- package/src/observability/traces.ts +48 -12
- package/src/org/handlers.ts +52 -1
- package/src/org/migrations.ts +4 -0
- package/src/org/plugin.ts +14 -0
- package/src/org/schema.ts +1 -0
- package/src/org/types.ts +8 -0
- package/src/org/workspace-url.ts +74 -0
- package/src/resources/handlers.ts +35 -5
- package/src/scripts/runner.ts +183 -5
- package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +2 -1
- package/src/server/action-routes.ts +32 -2
- package/src/server/agent-chat-plugin.ts +29 -0
- package/src/server/agent-teams.ts +10 -1
- package/src/server/auth.ts +33 -7
- package/src/server/embed-route.ts +9 -0
- package/src/server/embed-session.ts +62 -3
- package/src/server/open-route.ts +20 -6
- package/src/server/prompts/framework-core-compact.ts +1 -1
- package/src/server/prompts/framework-core.ts +1 -0
- package/src/server/request-context.ts +36 -1
- package/src/shared/embed-auth.ts +1 -0
- package/src/shared/html-document.ts +115 -0
- package/src/shared/index.ts +1 -0
- package/src/sharing/access.ts +8 -1
- package/src/sharing/registry.ts +11 -2
- package/src/workspace-files/actions.ts +100 -0
- package/src/workspace-files/tool.ts +4 -0
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +0 -6
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +0 -6
package/dist/cli/create.js
CHANGED
|
@@ -73,6 +73,11 @@ const HEADLESS_OPTION = {
|
|
|
73
73
|
label: "Headless",
|
|
74
74
|
hint: "Action-first app with one hello primitive and no UI shell",
|
|
75
75
|
};
|
|
76
|
+
const COMMUNITY_OPTION = {
|
|
77
|
+
name: "community",
|
|
78
|
+
label: "Community template",
|
|
79
|
+
hint: "Install a third-party Agent Native app from a public GitHub repository",
|
|
80
|
+
};
|
|
76
81
|
/**
|
|
77
82
|
* Main entry for `agent-native create [name]`.
|
|
78
83
|
*
|
|
@@ -138,6 +143,11 @@ export async function createApp(name, opts) {
|
|
|
138
143
|
await createStandaloneApp(name, { ...opts, template: shape }, clack);
|
|
139
144
|
return;
|
|
140
145
|
}
|
|
146
|
+
if (shape === "community") {
|
|
147
|
+
const template = await promptCommunityTemplate(clack);
|
|
148
|
+
await createStandaloneApp(name, { ...opts, template }, clack);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
141
151
|
// shape === "template" → full app(s) in a workspace.
|
|
142
152
|
await createWorkspaceInteractive(name, opts, clack);
|
|
143
153
|
return;
|
|
@@ -150,6 +160,7 @@ export async function createApp(name, opts) {
|
|
|
150
160
|
* choice made here implies the project structure, so we deliberately avoid a
|
|
151
161
|
* separate "workspace or standalone?" question:
|
|
152
162
|
* - "template" → full app(s) in a workspace (the multi-select picker)
|
|
163
|
+
* - "community" → a single standalone app from a public GitHub repository
|
|
153
164
|
* - "chat" → a single standalone chat UI app
|
|
154
165
|
* - "headless" → a single standalone action-first app with no UI shell
|
|
155
166
|
* Chat and headless are standalone on purpose: a monorepo is unnecessary
|
|
@@ -176,8 +187,13 @@ function startShapePromptOptions() {
|
|
|
176
187
|
},
|
|
177
188
|
{
|
|
178
189
|
value: "template",
|
|
179
|
-
label: "
|
|
180
|
-
hint: "Clone
|
|
190
|
+
label: "First-party template(s)",
|
|
191
|
+
hint: "Clone official apps (Mail, Calendar, Slides, ...) into a workspace",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
value: COMMUNITY_OPTION.name,
|
|
195
|
+
label: COMMUNITY_OPTION.label,
|
|
196
|
+
hint: COMMUNITY_OPTION.hint,
|
|
181
197
|
},
|
|
182
198
|
{
|
|
183
199
|
value: "headless",
|
|
@@ -187,6 +203,52 @@ function startShapePromptOptions() {
|
|
|
187
203
|
],
|
|
188
204
|
};
|
|
189
205
|
}
|
|
206
|
+
async function promptCommunityTemplate(clack) {
|
|
207
|
+
const selection = await clack.text({
|
|
208
|
+
message: "Which community template do you want to install?",
|
|
209
|
+
placeholder: "https://github.com/owner/repo[?app=id][#ref]",
|
|
210
|
+
validate(value) {
|
|
211
|
+
try {
|
|
212
|
+
parseCommunityPromptValue(String(value ?? ""));
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return error instanceof Error ? error.message : String(error);
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
if (clack.isCancel(selection)) {
|
|
220
|
+
clack.cancel("Cancelled.");
|
|
221
|
+
process.exit(0);
|
|
222
|
+
}
|
|
223
|
+
return parseCommunityPromptValue(String(selection)).canonical;
|
|
224
|
+
}
|
|
225
|
+
async function promptCommunityWorkspaceApp(apps, clack) {
|
|
226
|
+
const selection = await clack.select({
|
|
227
|
+
message: "Which app from this community workspace do you want to install?",
|
|
228
|
+
options: apps.map((app) => ({
|
|
229
|
+
value: app.name,
|
|
230
|
+
label: app.label,
|
|
231
|
+
hint: app.label === app.name ? undefined : app.name,
|
|
232
|
+
})),
|
|
233
|
+
});
|
|
234
|
+
if (clack.isCancel(selection)) {
|
|
235
|
+
clack.cancel("Cancelled.");
|
|
236
|
+
process.exit(0);
|
|
237
|
+
}
|
|
238
|
+
return String(selection);
|
|
239
|
+
}
|
|
240
|
+
function communityScaffoldOptions(clack, shape, destinationAppName, targetWorkspaceCoreName) {
|
|
241
|
+
return {
|
|
242
|
+
shape,
|
|
243
|
+
destinationAppName,
|
|
244
|
+
targetWorkspaceCoreName,
|
|
245
|
+
...(process.stdin.isTTY && process.stdout.isTTY
|
|
246
|
+
? {
|
|
247
|
+
selectCommunityWorkspaceApp: (apps) => promptCommunityWorkspaceApp(apps, clack),
|
|
248
|
+
}
|
|
249
|
+
: {}),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
190
252
|
/**
|
|
191
253
|
* Validate a project name supplied on the command line. Mirrors the rule in
|
|
192
254
|
* `promptNameIfMissing` so an invalid name is rejected before any interactive
|
|
@@ -233,6 +295,9 @@ async function createWorkspaceInteractive(name, opts, clack) {
|
|
|
233
295
|
process.exit(1);
|
|
234
296
|
}
|
|
235
297
|
const s = clack.spinner();
|
|
298
|
+
for (const template of templates) {
|
|
299
|
+
showCommunityTemplateTrustNote(template, clack);
|
|
300
|
+
}
|
|
236
301
|
s.start(`Scaffolding workspace "${name}"...`);
|
|
237
302
|
const appNames = new Set();
|
|
238
303
|
const scaffoldedApps = [];
|
|
@@ -254,17 +319,22 @@ async function createWorkspaceInteractive(name, opts, clack) {
|
|
|
254
319
|
// Distinguish download vs local copy in the spinner so a multi-second
|
|
255
320
|
// GitHub fetch doesn't look like a frozen "Scaffolding..." message.
|
|
256
321
|
// Mirrors the local-vs-remote decision inside scaffoldAppTemplate.
|
|
257
|
-
const willDownload = templateName !== "headless" &&
|
|
258
|
-
|
|
259
|
-
|
|
322
|
+
const willDownload = templateName !== "headless" &&
|
|
323
|
+
(isCommunityTemplateSelection(templateName) ||
|
|
324
|
+
!findLocalTemplate(normalizeTemplateName(templateName)));
|
|
260
325
|
s.message(willDownload
|
|
261
326
|
? `Downloading ${titleCase(appName)} template (${i + 1}/${templates.length})...`
|
|
262
327
|
: `Scaffolding ${titleCase(appName)} (${i + 1}/${templates.length})...`);
|
|
263
328
|
const appDir = path.join(targetDir, "apps", appName);
|
|
264
|
-
await scaffoldAppTemplate(appDir, templateName);
|
|
329
|
+
const resolution = await scaffoldAppTemplate(appDir, templateName, communityScaffoldOptions(clack, "workspace", appName, workspaceCoreName));
|
|
265
330
|
s.message(`Configuring ${titleCase(appName)} (${i + 1}/${templates.length})...`);
|
|
266
331
|
replacePlaceholders(appDir, appName, appTitleForScaffold(appName), name);
|
|
267
|
-
|
|
332
|
+
if (resolution.sourceIdentity) {
|
|
333
|
+
applyScaffoldIdentity(appDir, appName, templateName, resolution.sourceIdentity);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
rewriteTrackingAppId(appDir, appName, templateName);
|
|
337
|
+
}
|
|
268
338
|
workspacifyApp({
|
|
269
339
|
appDir,
|
|
270
340
|
appName,
|
|
@@ -275,8 +345,11 @@ async function createWorkspaceInteractive(name, opts, clack) {
|
|
|
275
345
|
dispatchDependencyVersion: getDispatchDependencyVersion(),
|
|
276
346
|
toolkitDependencyVersion: getToolkitDependencyVersion(),
|
|
277
347
|
});
|
|
278
|
-
fixPackageJsonName(appDir, appName, templateName
|
|
279
|
-
|
|
348
|
+
fixPackageJsonName(appDir, appName, templateName, {
|
|
349
|
+
...resolution,
|
|
350
|
+
shape: "workspace",
|
|
351
|
+
});
|
|
352
|
+
fixWebManifestName(appDir, appName, templateName, resolution.sourceIdentity);
|
|
280
353
|
rewriteNetlifyToml(appDir, appName, "workspace");
|
|
281
354
|
renameGitignore(appDir);
|
|
282
355
|
// Each app owns its own .claude / .agents symlinks.
|
|
@@ -337,10 +410,10 @@ async function createWorkspaceInteractive(name, opts, clack) {
|
|
|
337
410
|
}
|
|
338
411
|
function workspaceAppNameForTemplateSelection(templateName) {
|
|
339
412
|
const normalized = normalizeTemplateName(templateName);
|
|
340
|
-
|
|
413
|
+
const community = parseCommunityTemplateSelection(normalized, false);
|
|
414
|
+
if (!community)
|
|
341
415
|
return templateName;
|
|
342
|
-
const
|
|
343
|
-
const repoName = repo.split("/").filter(Boolean).pop() ?? "app";
|
|
416
|
+
const repoName = community.app ?? community.repo.split("/").pop() ?? "app";
|
|
344
417
|
let appName = repoName
|
|
345
418
|
.toLowerCase()
|
|
346
419
|
.replace(/[^a-z0-9-]+/g, "-")
|
|
@@ -499,11 +572,17 @@ async function scaffoldOneAppIntoWorkspace(workspace, appName, templateName, cla
|
|
|
499
572
|
process.exit(1);
|
|
500
573
|
}
|
|
501
574
|
const s = clack.spinner();
|
|
575
|
+
showCommunityTemplateTrustNote(templateName, clack);
|
|
502
576
|
s.start(`Working... no action needed. Scaffolding apps/${appName} from ${templateName}.`);
|
|
503
577
|
try {
|
|
504
|
-
const resolution = await scaffoldAppTemplate(appDir, templateName);
|
|
578
|
+
const resolution = await scaffoldAppTemplate(appDir, templateName, communityScaffoldOptions(clack, "workspace", appName, workspace.workspaceCoreName));
|
|
505
579
|
replacePlaceholders(appDir, appName, appTitleForScaffold(appName), path.basename(workspace.workspaceRoot));
|
|
506
|
-
|
|
580
|
+
if (resolution.sourceIdentity) {
|
|
581
|
+
applyScaffoldIdentity(appDir, appName, templateName, resolution.sourceIdentity);
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
rewriteTrackingAppId(appDir, appName, templateName);
|
|
585
|
+
}
|
|
507
586
|
workspacifyApp({
|
|
508
587
|
appDir,
|
|
509
588
|
appName,
|
|
@@ -518,7 +597,7 @@ async function scaffoldOneAppIntoWorkspace(workspace, appName, templateName, cla
|
|
|
518
597
|
...resolution,
|
|
519
598
|
shape: "workspace",
|
|
520
599
|
});
|
|
521
|
-
fixWebManifestName(appDir, appName, templateName);
|
|
600
|
+
fixWebManifestName(appDir, appName, templateName, resolution.sourceIdentity);
|
|
522
601
|
rewriteNetlifyToml(appDir, appName, "workspace");
|
|
523
602
|
renameGitignore(appDir);
|
|
524
603
|
setupAgentSymlinks(appDir);
|
|
@@ -556,20 +635,7 @@ async function createStandaloneApp(name, opts, clack) {
|
|
|
556
635
|
if (!template) {
|
|
557
636
|
const picked = await clack.select({
|
|
558
637
|
message: "Which template would you like to use?",
|
|
559
|
-
options:
|
|
560
|
-
{
|
|
561
|
-
value: HEADLESS_OPTION.name,
|
|
562
|
-
label: HEADLESS_OPTION.label,
|
|
563
|
-
hint: HEADLESS_OPTION.hint,
|
|
564
|
-
},
|
|
565
|
-
...onRampFirst(coreTemplates())
|
|
566
|
-
.filter((t) => t.name !== HEADLESS_OPTION.name)
|
|
567
|
-
.map((t) => ({
|
|
568
|
-
value: t.name,
|
|
569
|
-
label: t.label,
|
|
570
|
-
hint: t.hint,
|
|
571
|
-
})),
|
|
572
|
-
],
|
|
638
|
+
options: standaloneTemplatePromptOptions(),
|
|
573
639
|
});
|
|
574
640
|
if (clack.isCancel(picked)) {
|
|
575
641
|
clack.cancel("Cancelled.");
|
|
@@ -578,12 +644,17 @@ async function createStandaloneApp(name, opts, clack) {
|
|
|
578
644
|
template = picked;
|
|
579
645
|
}
|
|
580
646
|
template = normalizeTemplateName(template);
|
|
647
|
+
if (template === COMMUNITY_OPTION.name) {
|
|
648
|
+
template = await promptCommunityTemplate(clack);
|
|
649
|
+
}
|
|
581
650
|
const s = clack.spinner();
|
|
651
|
+
showCommunityTemplateTrustNote(template, clack);
|
|
582
652
|
s.start(template === "headless"
|
|
583
653
|
? "Scaffolding the headless agent app..."
|
|
584
|
-
:
|
|
654
|
+
: (communityTemplateDownloadMessage(template) ??
|
|
655
|
+
`Downloading the ${template} template from GitHub...`));
|
|
585
656
|
try {
|
|
586
|
-
const resolution = await scaffoldAppTemplate(targetDir, template);
|
|
657
|
+
const resolution = await scaffoldAppTemplate(targetDir, template, communityScaffoldOptions(clack, "standalone", name));
|
|
587
658
|
s.message(`Setting up ${name}…`);
|
|
588
659
|
postProcessStandalone(name, targetDir, template, resolution);
|
|
589
660
|
s.stop("App created!");
|
|
@@ -611,6 +682,27 @@ async function createStandaloneApp(name, opts, clack) {
|
|
|
611
682
|
clack.outro(`Done! Next steps:\n\n cd ${name}\n pnpm install\n pnpm dev`);
|
|
612
683
|
}
|
|
613
684
|
}
|
|
685
|
+
function standaloneTemplatePromptOptions() {
|
|
686
|
+
return [
|
|
687
|
+
{
|
|
688
|
+
value: HEADLESS_OPTION.name,
|
|
689
|
+
label: HEADLESS_OPTION.label,
|
|
690
|
+
hint: HEADLESS_OPTION.hint,
|
|
691
|
+
},
|
|
692
|
+
...onRampFirst(coreTemplates())
|
|
693
|
+
.filter((t) => t.name !== HEADLESS_OPTION.name)
|
|
694
|
+
.map((t) => ({
|
|
695
|
+
value: t.name,
|
|
696
|
+
label: t.label,
|
|
697
|
+
hint: t.hint,
|
|
698
|
+
})),
|
|
699
|
+
{
|
|
700
|
+
value: COMMUNITY_OPTION.name,
|
|
701
|
+
label: COMMUNITY_OPTION.label,
|
|
702
|
+
hint: COMMUNITY_OPTION.hint,
|
|
703
|
+
},
|
|
704
|
+
];
|
|
705
|
+
}
|
|
614
706
|
/**
|
|
615
707
|
* Remove a partially-scaffolded target directory after a scaffold failure so a
|
|
616
708
|
* retry doesn't hit the "Directory already exists" guard. Best-effort — the
|
|
@@ -630,11 +722,12 @@ function cleanupOnFailure(targetDir) {
|
|
|
630
722
|
/**
|
|
631
723
|
* Scaffold a single app template into `targetDir`. Resolves:
|
|
632
724
|
* - "headless" / legacy "blank" → bundled action-first template
|
|
633
|
-
* - "
|
|
725
|
+
* - "community:user/repo[#ref]" → download and validate the whole repo
|
|
726
|
+
* - legacy "github:user/repo[#ref]" and clean GitHub HTTPS URLs → community
|
|
634
727
|
* - first-party template name → use a bundled copy or download its subdir
|
|
635
728
|
* from BuilderIO/agent-native
|
|
636
729
|
*/
|
|
637
|
-
async function scaffoldAppTemplate(targetDir, template) {
|
|
730
|
+
async function scaffoldAppTemplate(targetDir, template, options = {}) {
|
|
638
731
|
fs.mkdirSync(path.dirname(targetDir), { recursive: true });
|
|
639
732
|
// Normalize legacy / renamed aliases.
|
|
640
733
|
let resolved = normalizeTemplateName(template);
|
|
@@ -650,13 +743,38 @@ async function scaffoldAppTemplate(targetDir, template) {
|
|
|
650
743
|
templateRef: getGitHubTemplateRefCandidates()[0],
|
|
651
744
|
};
|
|
652
745
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
746
|
+
const community = parseCommunityTemplateSelection(resolved, false);
|
|
747
|
+
if (community) {
|
|
748
|
+
const stagingDir = path.join(path.dirname(targetDir), `.agent-native-community-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
|
|
749
|
+
try {
|
|
750
|
+
const ref = await downloadGitHubRepo(community.repo, stagingDir, community.ref);
|
|
751
|
+
const source = await resolveCommunityTemplateSource(stagingDir, community, options);
|
|
752
|
+
copyDir(source.sourceDir, targetDir);
|
|
753
|
+
if (source.workspaceRoot) {
|
|
754
|
+
normalizeCommunityWorkspaceAppDependencies(targetDir, source.workspaceRoot, {
|
|
755
|
+
shape: options.shape,
|
|
756
|
+
destinationAppName: options.destinationAppName ?? path.basename(targetDir),
|
|
757
|
+
targetWorkspaceCoreName: options.targetWorkspaceCoreName,
|
|
758
|
+
sourceIdentity: source.sourceIdentity,
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
templateSource: "github",
|
|
763
|
+
templateRef: ref,
|
|
764
|
+
sourceIdentity: source.sourceIdentity,
|
|
765
|
+
communityTemplate: {
|
|
766
|
+
source: `https://github.com/${community.repo}`,
|
|
767
|
+
ref,
|
|
768
|
+
...(source.app ? { app: source.app } : {}),
|
|
769
|
+
},
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
finally {
|
|
773
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
774
|
+
}
|
|
657
775
|
}
|
|
658
776
|
if (!getTemplate(resolved)) {
|
|
659
|
-
throw new Error(`Unknown template "${template}". Known: ${allTemplateNames().join(", ")}
|
|
777
|
+
throw new Error(`Unknown template "${template}". Known first-party templates: ${allTemplateNames().join(", ")}. For a community template, use community:owner/repo.`);
|
|
660
778
|
}
|
|
661
779
|
// If running from the framework monorepo with a local templates/ dir, use
|
|
662
780
|
// that. Otherwise download from GitHub. This keeps `agent-native create`
|
|
@@ -718,8 +836,149 @@ function normalizeTemplateName(template) {
|
|
|
718
836
|
if (template === "image" || template === "images" || template === "asset") {
|
|
719
837
|
return "assets";
|
|
720
838
|
}
|
|
839
|
+
const community = parseCommunityTemplateSelection(template, false);
|
|
840
|
+
if (community)
|
|
841
|
+
return community.canonical;
|
|
721
842
|
return template;
|
|
722
843
|
}
|
|
844
|
+
function parseCommunityTemplateSelection(selection, required = true) {
|
|
845
|
+
const value = selection.trim();
|
|
846
|
+
let raw;
|
|
847
|
+
if (value.startsWith("community:")) {
|
|
848
|
+
raw = value.slice("community:".length).trim();
|
|
849
|
+
}
|
|
850
|
+
else if (value.startsWith("github:")) {
|
|
851
|
+
raw = value.slice("github:".length).trim();
|
|
852
|
+
}
|
|
853
|
+
else if (value.startsWith("https://github.com/")) {
|
|
854
|
+
raw = value;
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
if (!required)
|
|
858
|
+
return undefined;
|
|
859
|
+
throw new ValidationError(`Invalid community template "${selection}". Use community:owner/repo, optionally with #branch, #tag, or #commit.`);
|
|
860
|
+
}
|
|
861
|
+
if (!raw) {
|
|
862
|
+
throw new ValidationError("A community template repository is required. Use owner/repo or a GitHub URL.");
|
|
863
|
+
}
|
|
864
|
+
let repo;
|
|
865
|
+
let app;
|
|
866
|
+
let ref;
|
|
867
|
+
if (raw.includes("://")) {
|
|
868
|
+
let url;
|
|
869
|
+
try {
|
|
870
|
+
url = new URL(raw);
|
|
871
|
+
}
|
|
872
|
+
catch {
|
|
873
|
+
throw new ValidationError(`Invalid GitHub repository URL "${raw}". Expected https://github.com/owner/repo.`);
|
|
874
|
+
}
|
|
875
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
876
|
+
if (url.protocol !== "https:" ||
|
|
877
|
+
url.hostname !== "github.com" ||
|
|
878
|
+
url.port ||
|
|
879
|
+
url.username ||
|
|
880
|
+
url.password ||
|
|
881
|
+
parts.length !== 2) {
|
|
882
|
+
throw new ValidationError(`Invalid GitHub repository URL "${raw}". Expected https://github.com/owner/repo with an optional #ref.`);
|
|
883
|
+
}
|
|
884
|
+
repo = `${parts[0]}/${parts[1].replace(/\.git$/, "")}`;
|
|
885
|
+
app = parseCommunityAppSearchParams(url.searchParams, raw);
|
|
886
|
+
if (url.hash.length > 1) {
|
|
887
|
+
try {
|
|
888
|
+
ref = decodeURIComponent(url.hash.slice(1));
|
|
889
|
+
}
|
|
890
|
+
catch {
|
|
891
|
+
throw new ValidationError(`Invalid encoded Git ref in "${raw}".`);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
else {
|
|
896
|
+
const hash = raw.indexOf("#");
|
|
897
|
+
const beforeHash = hash === -1 ? raw : raw.slice(0, hash);
|
|
898
|
+
const query = beforeHash.indexOf("?");
|
|
899
|
+
repo = (query === -1 ? beforeHash : beforeHash.slice(0, query)).replace(/\.git$/, "");
|
|
900
|
+
if (query !== -1) {
|
|
901
|
+
app = parseCommunityAppSearchParams(new URLSearchParams(beforeHash.slice(query + 1)), raw);
|
|
902
|
+
}
|
|
903
|
+
ref = hash === -1 ? undefined : raw.slice(hash + 1);
|
|
904
|
+
}
|
|
905
|
+
validateRepoName(repo);
|
|
906
|
+
if (app !== undefined)
|
|
907
|
+
validateCommunityAppSelector(app);
|
|
908
|
+
if (ref !== undefined)
|
|
909
|
+
validateGitRef(ref);
|
|
910
|
+
return {
|
|
911
|
+
repo,
|
|
912
|
+
...(app ? { app } : {}),
|
|
913
|
+
...(ref ? { ref } : {}),
|
|
914
|
+
canonical: `community:${repo}${app ? `?app=${app}` : ""}${ref ? `#${ref}` : ""}`,
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
function parseCommunityAppSearchParams(params, source) {
|
|
918
|
+
const entries = [...params.entries()];
|
|
919
|
+
if (entries.length === 0)
|
|
920
|
+
return undefined;
|
|
921
|
+
if (entries.length !== 1 || entries[0]?.[0] !== "app" || !entries[0][1]) {
|
|
922
|
+
throw new ValidationError(`Invalid community template selector in "${source}". Only ?app=<app-name> is supported.`);
|
|
923
|
+
}
|
|
924
|
+
return entries[0][1];
|
|
925
|
+
}
|
|
926
|
+
function validateCommunityAppSelector(app) {
|
|
927
|
+
if (app === "dispatch") {
|
|
928
|
+
throw new ValidationError("Dispatch cannot be installed from a community workspace.");
|
|
929
|
+
}
|
|
930
|
+
const error = getWorkspaceAppIdValidationError(app);
|
|
931
|
+
if (error)
|
|
932
|
+
throw new ValidationError(error);
|
|
933
|
+
}
|
|
934
|
+
function isCommunityTemplateSelection(selection) {
|
|
935
|
+
return parseCommunityTemplateSelection(selection, false) !== undefined;
|
|
936
|
+
}
|
|
937
|
+
function parseCommunityPromptValue(value) {
|
|
938
|
+
const trimmed = value.trim();
|
|
939
|
+
const selection = trimmed.startsWith("community:") ||
|
|
940
|
+
trimmed.startsWith("github:") ||
|
|
941
|
+
trimmed.startsWith("https://github.com/")
|
|
942
|
+
? trimmed
|
|
943
|
+
: `community:${trimmed}`;
|
|
944
|
+
return parseCommunityTemplateSelection(selection);
|
|
945
|
+
}
|
|
946
|
+
function validateGitRef(ref) {
|
|
947
|
+
if (!ref ||
|
|
948
|
+
!/^[a-zA-Z0-9][a-zA-Z0-9._/+-]*$/.test(ref) ||
|
|
949
|
+
ref.includes("..") ||
|
|
950
|
+
ref.includes("//") ||
|
|
951
|
+
ref.includes("@{") ||
|
|
952
|
+
ref.endsWith("/") ||
|
|
953
|
+
ref.endsWith(".") ||
|
|
954
|
+
ref.split("/").some((part) => !part || part.endsWith(".lock"))) {
|
|
955
|
+
throw new ValidationError(`Invalid Git ref "${ref}". Use a branch, tag, or commit SHA without spaces or Git ref control characters.`);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
function communityTemplateTrustMessage(selection) {
|
|
959
|
+
const community = parseCommunityTemplateSelection(selection, false);
|
|
960
|
+
if (!community)
|
|
961
|
+
return undefined;
|
|
962
|
+
return [
|
|
963
|
+
`${community.repo} is third-party code and is not reviewed or maintained by Agent Native.`,
|
|
964
|
+
"The CLI downloads source only; it does not install dependencies or run template scripts.",
|
|
965
|
+
"Review the generated files before running pnpm install.",
|
|
966
|
+
community.ref
|
|
967
|
+
? `Requested ref: ${community.ref}`
|
|
968
|
+
: "No ref supplied; the CLI will try main, then master.",
|
|
969
|
+
].join("\n");
|
|
970
|
+
}
|
|
971
|
+
function communityTemplateDownloadMessage(selection) {
|
|
972
|
+
const community = parseCommunityTemplateSelection(selection, false);
|
|
973
|
+
return community
|
|
974
|
+
? `Downloading community template ${community.repo} from GitHub...`
|
|
975
|
+
: undefined;
|
|
976
|
+
}
|
|
977
|
+
function showCommunityTemplateTrustNote(selection, clack) {
|
|
978
|
+
const message = communityTemplateTrustMessage(selection);
|
|
979
|
+
if (message)
|
|
980
|
+
clack.note(message, "Community template — review before use");
|
|
981
|
+
}
|
|
723
982
|
/**
|
|
724
983
|
* Find a local packages/<name> directory (for framework development).
|
|
725
984
|
* Returns undefined when running as a published npm package.
|
|
@@ -867,13 +1126,12 @@ async function scaffoldRequiredPackages(templateNames, workspaceRoot) {
|
|
|
867
1126
|
function postProcessStandalone(name, targetDir, templateName, resolution) {
|
|
868
1127
|
const appTitle = appTitleForScaffold(name);
|
|
869
1128
|
replacePlaceholders(targetDir, name, appTitle);
|
|
870
|
-
|
|
871
|
-
rewriteAgentChatAppId(targetDir, name, templateName);
|
|
1129
|
+
applyScaffoldIdentity(targetDir, name, templateName, resolution?.sourceIdentity);
|
|
872
1130
|
fixPackageJsonName(targetDir, name, templateName, {
|
|
873
1131
|
...resolution,
|
|
874
1132
|
shape: "standalone",
|
|
875
1133
|
});
|
|
876
|
-
fixWebManifestName(targetDir, name, templateName);
|
|
1134
|
+
fixWebManifestName(targetDir, name, templateName, resolution?.sourceIdentity);
|
|
877
1135
|
rewriteNetlifyToml(targetDir, name, "standalone");
|
|
878
1136
|
for (const base of ["learnings"]) {
|
|
879
1137
|
const defaultsFile = path.join(targetDir, `${base}.defaults.md`);
|
|
@@ -1157,12 +1415,14 @@ export function detectWorkspace(startDir) {
|
|
|
1157
1415
|
}
|
|
1158
1416
|
export { parseWorkspaceScope };
|
|
1159
1417
|
/** @internal — exported for E2E tests */
|
|
1160
|
-
export { scaffoldWorkspaceRoot as _scaffoldWorkspaceRoot, scaffoldAppTemplate as _scaffoldAppTemplate, scaffoldRequiredPackages as _scaffoldRequiredPackages, postProcessStandalone as _postProcessStandalone, loadCatalog as _loadCatalog, fixPackageJsonName as _fixPackageJsonName, renameGitignore as _renameGitignore, rewriteNetlifyToml as _rewriteNetlifyToml, getCoreDependencyVersion as _getCoreDependencyVersion, getDispatchDependencyVersion as _getDispatchDependencyVersion, getToolkitDependencyVersion as _getToolkitDependencyVersion, getCorePackageVersion as _getCorePackageVersion, getGitHubTemplateRef as _getGitHubTemplateRef, getGitHubTemplateRefCandidates as _getGitHubTemplateRefCandidates, githubTarballUrl as _githubTarballUrl, findLocalTemplateFrom as _findLocalTemplateFrom, workspaceAppNameForTemplateSelection as _workspaceAppNameForTemplateSelection, startShapePromptOptions as _startShapePromptOptions, shouldSkipScaffoldEntry as _shouldSkipScaffoldEntry, tarExtractArgs as _tarExtractArgs, downloadGitHubSubdir as _downloadGitHubSubdir, findLocalTemplate as _findLocalTemplate, templateSourceName as _templateSourceName, normalizeTemplateName as _normalizeTemplateName, appTitleForScaffold as _appTitleForScaffold, replacePlaceholders as _replacePlaceholders, rewriteTrackingAppId as _rewriteTrackingAppId, rewriteAgentChatAppId as _rewriteAgentChatAppId, fixWebManifestName as _fixWebManifestName, copyDir as _copyDir, localTemplateSourceKind as _localTemplateSourceKind, REPO as _REPO, TEMPLATES_DIR as _TEMPLATES_DIR, };
|
|
1418
|
+
export { scaffoldWorkspaceRoot as _scaffoldWorkspaceRoot, scaffoldAppTemplate as _scaffoldAppTemplate, scaffoldRequiredPackages as _scaffoldRequiredPackages, postProcessStandalone as _postProcessStandalone, loadCatalog as _loadCatalog, fixPackageJsonName as _fixPackageJsonName, renameGitignore as _renameGitignore, rewriteNetlifyToml as _rewriteNetlifyToml, getCoreDependencyVersion as _getCoreDependencyVersion, getDispatchDependencyVersion as _getDispatchDependencyVersion, getToolkitDependencyVersion as _getToolkitDependencyVersion, getCorePackageVersion as _getCorePackageVersion, getGitHubTemplateRef as _getGitHubTemplateRef, getGitHubTemplateRefCandidates as _getGitHubTemplateRefCandidates, githubTarballUrl as _githubTarballUrl, findLocalTemplateFrom as _findLocalTemplateFrom, workspaceAppNameForTemplateSelection as _workspaceAppNameForTemplateSelection, startShapePromptOptions as _startShapePromptOptions, standaloneTemplatePromptOptions as _standaloneTemplatePromptOptions, parseCommunityTemplateSelection as _parseCommunityTemplateSelection, communityTemplateTrustMessage as _communityTemplateTrustMessage, communityTemplateTarballUrl as _communityTemplateTarballUrl, assertCommunityTemplateRoot as _assertCommunityTemplateRoot, assertSafeCommunityArchiveListing as _assertSafeCommunityArchiveListing, validateCommunityArchive as _validateCommunityArchive, resolveCommunityTemplateSource as _resolveCommunityTemplateSource, discoverCommunityWorkspaceApps as _discoverCommunityWorkspaceApps, normalizeCommunityWorkspaceAppDependencies as _normalizeCommunityWorkspaceAppDependencies, shouldSkipScaffoldEntry as _shouldSkipScaffoldEntry, tarExtractArgs as _tarExtractArgs, downloadGitHubSubdir as _downloadGitHubSubdir, findLocalTemplate as _findLocalTemplate, templateSourceName as _templateSourceName, normalizeTemplateName as _normalizeTemplateName, appTitleForScaffold as _appTitleForScaffold, replacePlaceholders as _replacePlaceholders, rewriteTrackingAppId as _rewriteTrackingAppId, rewriteAgentChatAppId as _rewriteAgentChatAppId, applyScaffoldIdentity as _applyScaffoldIdentity, fixWebManifestName as _fixWebManifestName, copyDir as _copyDir, localTemplateSourceKind as _localTemplateSourceKind, REPO as _REPO, TEMPLATES_DIR as _TEMPLATES_DIR, };
|
|
1161
1419
|
/* ─────────────────────────────────────────────────────────────────────────
|
|
1162
1420
|
* Download / copy helpers
|
|
1163
1421
|
* ───────────────────────────────────────────────────────────────────────── */
|
|
1164
1422
|
function validateRepoName(repo) {
|
|
1165
|
-
|
|
1423
|
+
const parts = repo.split("/");
|
|
1424
|
+
if (!/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo) ||
|
|
1425
|
+
parts.some((part) => part === "." || part === "..")) {
|
|
1166
1426
|
throw new ValidationError(`Invalid repository name "${repo}". Expected format: user/repo`);
|
|
1167
1427
|
}
|
|
1168
1428
|
}
|
|
@@ -1173,7 +1433,18 @@ function tarExtractArgs(tarPath, destDir, options = {}) {
|
|
|
1173
1433
|
pattern,
|
|
1174
1434
|
])
|
|
1175
1435
|
: [];
|
|
1176
|
-
|
|
1436
|
+
const safeOwnership = options.untrustedCommunityArchive
|
|
1437
|
+
? ["--no-same-owner", "--no-same-permissions"]
|
|
1438
|
+
: [];
|
|
1439
|
+
return [
|
|
1440
|
+
"xzf",
|
|
1441
|
+
tarPath,
|
|
1442
|
+
"--strip-components=1",
|
|
1443
|
+
...safeOwnership,
|
|
1444
|
+
...excludes,
|
|
1445
|
+
"-C",
|
|
1446
|
+
destDir,
|
|
1447
|
+
];
|
|
1177
1448
|
}
|
|
1178
1449
|
function execFileBuffer(command, args, options) {
|
|
1179
1450
|
return new Promise((resolve, reject) => {
|
|
@@ -1208,6 +1479,9 @@ async function downloadAndExtract(url, destDir, options = {}) {
|
|
|
1208
1479
|
const tarPath = path.join(destDir, ".download.tar.gz");
|
|
1209
1480
|
fs.writeFileSync(tarPath, tarball);
|
|
1210
1481
|
try {
|
|
1482
|
+
if (options.untrustedCommunityArchive) {
|
|
1483
|
+
validateCommunityArchive(tarPath);
|
|
1484
|
+
}
|
|
1211
1485
|
execFileSync("tar", tarExtractArgs(tarPath, destDir, options), {
|
|
1212
1486
|
stdio: "pipe",
|
|
1213
1487
|
});
|
|
@@ -1216,6 +1490,40 @@ async function downloadAndExtract(url, destDir, options = {}) {
|
|
|
1216
1490
|
fs.unlinkSync(tarPath);
|
|
1217
1491
|
}
|
|
1218
1492
|
}
|
|
1493
|
+
function validateCommunityArchive(tarPath) {
|
|
1494
|
+
const listing = execFileSync("tar", ["tvzf", tarPath], {
|
|
1495
|
+
encoding: "utf-8",
|
|
1496
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1497
|
+
});
|
|
1498
|
+
assertSafeCommunityArchiveListing(listing);
|
|
1499
|
+
}
|
|
1500
|
+
function assertSafeCommunityArchiveListing(listing) {
|
|
1501
|
+
const unsafeEntry = listing.split(/\r?\n/).find((line) => {
|
|
1502
|
+
if (line.startsWith("h"))
|
|
1503
|
+
return true;
|
|
1504
|
+
if (!line.startsWith("l"))
|
|
1505
|
+
return false;
|
|
1506
|
+
return !isCanonicalAgentSymlinkListing(line);
|
|
1507
|
+
});
|
|
1508
|
+
if (unsafeEntry) {
|
|
1509
|
+
throw new ValidationError("Community template archives may only contain Agent Native's canonical internal symlinks (CLAUDE.md and .claude/skills). Remove other symbolic or hard links and try again.");
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
function isCanonicalAgentSymlinkListing(line) {
|
|
1513
|
+
const match = line.match(/\s(\S+)\s+->\s+(\S+)\s*$/);
|
|
1514
|
+
if (!match)
|
|
1515
|
+
return false;
|
|
1516
|
+
const archivePath = match[1];
|
|
1517
|
+
const target = match[2];
|
|
1518
|
+
const parts = archivePath.split("/");
|
|
1519
|
+
if (parts.some((part) => !part || part === "." || part === ".." || part.includes("\\"))) {
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
return ((parts.at(-1) === "CLAUDE.md" && target === "AGENTS.md") ||
|
|
1523
|
+
(parts.at(-2) === ".claude" &&
|
|
1524
|
+
parts.at(-1) === "skills" &&
|
|
1525
|
+
target === "../.agents/skills"));
|
|
1526
|
+
}
|
|
1219
1527
|
/** Resolves to the ref that actually succeeded so callers can record it. */
|
|
1220
1528
|
async function downloadGitHubSubdir(repo, subdir, targetDir, refOverride) {
|
|
1221
1529
|
validateRepoName(repo);
|
|
@@ -1249,10 +1557,332 @@ async function downloadGitHubSubdir(repo, subdir, targetDir, refOverride) {
|
|
|
1249
1557
|
}
|
|
1250
1558
|
throw new Error(`Failed to download templates from ${repo}. Tried refs:\n${errors.join("\n")}`);
|
|
1251
1559
|
}
|
|
1252
|
-
async function downloadGitHubRepo(repo, targetDir) {
|
|
1560
|
+
async function downloadGitHubRepo(repo, targetDir, requestedRef) {
|
|
1253
1561
|
validateRepoName(repo);
|
|
1254
|
-
const
|
|
1255
|
-
|
|
1562
|
+
const refs = requestedRef ? [requestedRef] : ["main", "master"];
|
|
1563
|
+
const errors = [];
|
|
1564
|
+
for (const ref of refs) {
|
|
1565
|
+
try {
|
|
1566
|
+
await downloadAndExtract(communityTemplateTarballUrl(repo, ref), targetDir, { untrustedCommunityArchive: true });
|
|
1567
|
+
return ref;
|
|
1568
|
+
}
|
|
1569
|
+
catch (error) {
|
|
1570
|
+
errors.push(` ${ref}: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}`);
|
|
1571
|
+
cleanupOnFailure(targetDir);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
throw new Error(`Failed to download community template ${repo}. Tried refs:\n${errors.join("\n")}\nThe repository must be public, and the requested branch, tag, or commit must exist.`);
|
|
1575
|
+
}
|
|
1576
|
+
function communityTemplateTarballUrl(repo, ref) {
|
|
1577
|
+
return `https://codeload.github.com/${repo}/tar.gz/${encodeURIComponent(ref)}`;
|
|
1578
|
+
}
|
|
1579
|
+
function assertCommunityTemplateRoot(targetDir, repo) {
|
|
1580
|
+
const packagePath = path.join(targetDir, "package.json");
|
|
1581
|
+
if (!fs.existsSync(packagePath)) {
|
|
1582
|
+
throw new ValidationError(`Community template ${repo} is not an Agent Native app at the repository root: package.json was not found. Point to a repository whose root is the app.`);
|
|
1583
|
+
}
|
|
1584
|
+
let pkg;
|
|
1585
|
+
try {
|
|
1586
|
+
pkg = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
1587
|
+
}
|
|
1588
|
+
catch {
|
|
1589
|
+
throw new ValidationError(`Community template ${repo} has an unreadable package.json at the repository root.`);
|
|
1590
|
+
}
|
|
1591
|
+
const dependencyGroups = [
|
|
1592
|
+
pkg.dependencies,
|
|
1593
|
+
pkg.devDependencies,
|
|
1594
|
+
pkg.peerDependencies,
|
|
1595
|
+
];
|
|
1596
|
+
const usesAgentNativeCore = dependencyGroups.some((group) => group !== null &&
|
|
1597
|
+
typeof group === "object" &&
|
|
1598
|
+
!Array.isArray(group) &&
|
|
1599
|
+
typeof group["@agent-native/core"] ===
|
|
1600
|
+
"string");
|
|
1601
|
+
if (!usesAgentNativeCore) {
|
|
1602
|
+
throw new ValidationError(`Community template ${repo} is not an Agent Native app at the repository root. Its package.json must directly depend on @agent-native/core in dependencies, devDependencies, or peerDependencies.`);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
async function resolveCommunityTemplateSource(stagingDir, selection, options = {}) {
|
|
1606
|
+
const rootPkg = readPackageJsonObject(path.join(stagingDir, "package.json"));
|
|
1607
|
+
const agentNative = rootPkg?.["agent-native"] &&
|
|
1608
|
+
typeof rootPkg["agent-native"] === "object" &&
|
|
1609
|
+
!Array.isArray(rootPkg["agent-native"])
|
|
1610
|
+
? rootPkg["agent-native"]
|
|
1611
|
+
: undefined;
|
|
1612
|
+
const hasWorkspaceCoreMarker = agentNative && Object.hasOwn(agentNative, "workspaceCore");
|
|
1613
|
+
const workspaceCore = agentNative?.workspaceCore;
|
|
1614
|
+
if (hasWorkspaceCoreMarker &&
|
|
1615
|
+
(typeof workspaceCore !== "string" || !workspaceCore.trim())) {
|
|
1616
|
+
throw new ValidationError(`Community repository ${selection.repo} has invalid agent-native.workspaceCore metadata. Expected a non-empty workspace package name.`);
|
|
1617
|
+
}
|
|
1618
|
+
const isWorkspace = typeof workspaceCore === "string";
|
|
1619
|
+
if (!isWorkspace) {
|
|
1620
|
+
assertCommunityTemplateRoot(stagingDir, selection.repo);
|
|
1621
|
+
const sourceIdentity = readCommunitySourceIdentity(stagingDir, selection.repo);
|
|
1622
|
+
if (selection.app) {
|
|
1623
|
+
throw new ValidationError(`Community repository ${selection.repo} is a single app at its root. Remove ?app=${selection.app}; app selectors are only for workspace repositories.`);
|
|
1624
|
+
}
|
|
1625
|
+
return { sourceDir: stagingDir, sourceIdentity };
|
|
1626
|
+
}
|
|
1627
|
+
const workspaceApps = discoverCommunityWorkspaceApps(stagingDir);
|
|
1628
|
+
if (workspaceApps.length === 0) {
|
|
1629
|
+
throw new ValidationError(`Community workspace ${selection.repo} has no installable apps. Dispatch cannot be installed from a community workspace, and apps must directly depend on @agent-native/core.`);
|
|
1630
|
+
}
|
|
1631
|
+
let selectedApp = selection.app;
|
|
1632
|
+
if (!selectedApp) {
|
|
1633
|
+
if (!options.selectCommunityWorkspaceApp) {
|
|
1634
|
+
throw new ValidationError(`Community workspace ${selection.repo} requires an app selection, but no interactive terminal is available. Select one with ?app=<app-name>. Available apps: ${workspaceApps.map((app) => app.name).join(", ")}.`);
|
|
1635
|
+
}
|
|
1636
|
+
else {
|
|
1637
|
+
selectedApp = await options.selectCommunityWorkspaceApp(workspaceApps.map(({ name, label }) => ({ name, label })));
|
|
1638
|
+
validateCommunityAppSelector(selectedApp);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
const selected = workspaceApps.find((app) => app.name === selectedApp);
|
|
1642
|
+
if (!selected) {
|
|
1643
|
+
throw new ValidationError(`App "${selectedApp}" was not found in community workspace ${selection.repo}. Available apps: ${workspaceApps.map((app) => app.name).join(", ")}.`);
|
|
1644
|
+
}
|
|
1645
|
+
return {
|
|
1646
|
+
sourceDir: selected.dir,
|
|
1647
|
+
sourceIdentity: selected.sourceIdentity,
|
|
1648
|
+
app: selected.name,
|
|
1649
|
+
workspaceRoot: stagingDir,
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
function discoverCommunityWorkspaceApps(workspaceRoot) {
|
|
1653
|
+
const appsDir = path.join(workspaceRoot, "apps");
|
|
1654
|
+
if (!fs.existsSync(appsDir))
|
|
1655
|
+
return [];
|
|
1656
|
+
return fs
|
|
1657
|
+
.readdirSync(appsDir, { withFileTypes: true })
|
|
1658
|
+
.filter((entry) => entry.isDirectory())
|
|
1659
|
+
.flatMap((entry) => {
|
|
1660
|
+
const appDir = path.join(appsDir, entry.name);
|
|
1661
|
+
const packagePath = path.join(appDir, "package.json");
|
|
1662
|
+
if (!fs.existsSync(packagePath))
|
|
1663
|
+
return [];
|
|
1664
|
+
const pkg = readPackageJsonObject(packagePath);
|
|
1665
|
+
if (!pkg) {
|
|
1666
|
+
throw new ValidationError(`Community workspace app "${entry.name}" has an unreadable package.json.`);
|
|
1667
|
+
}
|
|
1668
|
+
if (getWorkspaceAppIdValidationError(entry.name))
|
|
1669
|
+
return [];
|
|
1670
|
+
if (!packageDirectlyDependsOnAgentNativeCore(pkg))
|
|
1671
|
+
return [];
|
|
1672
|
+
const sourceIdentity = readCommunitySourceIdentity(appDir, `${path.basename(workspaceRoot)}/${entry.name}`);
|
|
1673
|
+
const packageName = sourceIdentity.appName.split("/").pop() ?? sourceIdentity.appName;
|
|
1674
|
+
if (packageName === "dispatch")
|
|
1675
|
+
return [];
|
|
1676
|
+
return [
|
|
1677
|
+
{
|
|
1678
|
+
name: entry.name,
|
|
1679
|
+
label: sourceIdentity.appTitle,
|
|
1680
|
+
dir: appDir,
|
|
1681
|
+
sourceIdentity,
|
|
1682
|
+
},
|
|
1683
|
+
];
|
|
1684
|
+
})
|
|
1685
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
1686
|
+
}
|
|
1687
|
+
function readPackageJsonObject(packagePath) {
|
|
1688
|
+
try {
|
|
1689
|
+
const value = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
1690
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
1691
|
+
? value
|
|
1692
|
+
: undefined;
|
|
1693
|
+
}
|
|
1694
|
+
catch {
|
|
1695
|
+
return undefined;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
function packageDirectlyDependsOnAgentNativeCore(pkg) {
|
|
1699
|
+
return [pkg.dependencies, pkg.devDependencies, pkg.peerDependencies].some((group) => group !== null &&
|
|
1700
|
+
typeof group === "object" &&
|
|
1701
|
+
!Array.isArray(group) &&
|
|
1702
|
+
typeof group["@agent-native/core"] ===
|
|
1703
|
+
"string");
|
|
1704
|
+
}
|
|
1705
|
+
function normalizeCommunityWorkspaceAppDependencies(appDir, sourceWorkspaceRoot, options) {
|
|
1706
|
+
const appPackagePath = path.join(appDir, "package.json");
|
|
1707
|
+
const pkg = readPackageJsonObject(appPackagePath);
|
|
1708
|
+
if (!pkg) {
|
|
1709
|
+
throw new ValidationError("Selected community workspace app has an unreadable package.json.");
|
|
1710
|
+
}
|
|
1711
|
+
const rootPkg = readPackageJsonObject(path.join(sourceWorkspaceRoot, "package.json"));
|
|
1712
|
+
const agentNative = rootPkg?.["agent-native"] &&
|
|
1713
|
+
typeof rootPkg["agent-native"] === "object" &&
|
|
1714
|
+
!Array.isArray(rootPkg["agent-native"])
|
|
1715
|
+
? rootPkg["agent-native"]
|
|
1716
|
+
: undefined;
|
|
1717
|
+
const sourceWorkspaceCore = typeof agentNative?.workspaceCore === "string"
|
|
1718
|
+
? agentNative.workspaceCore
|
|
1719
|
+
: undefined;
|
|
1720
|
+
const catalog = loadCommunityWorkspaceCatalog(sourceWorkspaceRoot);
|
|
1721
|
+
const supportedWorkspacePackages = new Set([
|
|
1722
|
+
"@agent-native/core",
|
|
1723
|
+
"@agent-native/dispatch",
|
|
1724
|
+
"@agent-native/toolkit",
|
|
1725
|
+
]);
|
|
1726
|
+
for (const depType of [
|
|
1727
|
+
"dependencies",
|
|
1728
|
+
"devDependencies",
|
|
1729
|
+
"peerDependencies",
|
|
1730
|
+
]) {
|
|
1731
|
+
const deps = pkg[depType];
|
|
1732
|
+
if (!deps || typeof deps !== "object" || Array.isArray(deps))
|
|
1733
|
+
continue;
|
|
1734
|
+
for (const [key, value] of Object.entries(deps)) {
|
|
1735
|
+
if (key === sourceWorkspaceCore) {
|
|
1736
|
+
delete deps[key];
|
|
1737
|
+
continue;
|
|
1738
|
+
}
|
|
1739
|
+
if (value === "catalog:") {
|
|
1740
|
+
const resolved = catalog[key];
|
|
1741
|
+
if (!resolved) {
|
|
1742
|
+
throw new ValidationError(`Community workspace app dependency "${key}" uses catalog:, but no value was found in the source pnpm-workspace.yaml catalog.`);
|
|
1743
|
+
}
|
|
1744
|
+
deps[key] = resolved;
|
|
1745
|
+
continue;
|
|
1746
|
+
}
|
|
1747
|
+
if (typeof value === "string" && value.startsWith("catalog:")) {
|
|
1748
|
+
throw new ValidationError(`Community workspace app dependency "${key}" uses unsupported named catalog reference "${value}". Use a concrete version or the default catalog.`);
|
|
1749
|
+
}
|
|
1750
|
+
if (typeof value === "string" &&
|
|
1751
|
+
value.startsWith("workspace:") &&
|
|
1752
|
+
supportedWorkspacePackages.has(key)) {
|
|
1753
|
+
deps[key] =
|
|
1754
|
+
key === "@agent-native/core"
|
|
1755
|
+
? getCoreDependencyVersion()
|
|
1756
|
+
: key === "@agent-native/dispatch"
|
|
1757
|
+
? getDispatchDependencyVersion()
|
|
1758
|
+
: getToolkitDependencyVersion();
|
|
1759
|
+
continue;
|
|
1760
|
+
}
|
|
1761
|
+
if (typeof value === "string" && value.startsWith("workspace:")) {
|
|
1762
|
+
throw new ValidationError(`Community workspace app dependency "${key}" uses "${value}", but that source workspace package is not included when installing one app. Publish it with a concrete version or remove the dependency.`);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
fs.writeFileSync(appPackagePath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1767
|
+
if (sourceWorkspaceCore) {
|
|
1768
|
+
rewriteCanonicalCommunityWorkspacePlugins(appDir, sourceWorkspaceCore, options);
|
|
1769
|
+
}
|
|
1770
|
+
if (sourceWorkspaceCore &&
|
|
1771
|
+
findSourceImportSpecifier(appDir, sourceWorkspaceCore)) {
|
|
1772
|
+
throw new ValidationError(`Community workspace app imports its source shared package "${sourceWorkspaceCore}", which is not included when installing one app. Move that code into the app or publish it as a concrete dependency.`);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
function rewriteCanonicalCommunityWorkspacePlugins(appDir, sourceWorkspaceCore, options) {
|
|
1776
|
+
const authPath = path.join(appDir, "server", "plugins", "auth.ts");
|
|
1777
|
+
const agentChatPath = path.join(appDir, "server", "plugins", "agent-chat.ts");
|
|
1778
|
+
if (options.shape === "standalone") {
|
|
1779
|
+
if (fs.existsSync(authPath)) {
|
|
1780
|
+
const auth = fs.readFileSync(authPath, "utf-8");
|
|
1781
|
+
if (containsModuleSpecifier(auth, sourceWorkspaceCore) &&
|
|
1782
|
+
auth.includes("defaultAuthPlugin") &&
|
|
1783
|
+
auth.includes("workspaceServer")) {
|
|
1784
|
+
fs.writeFileSync(authPath, 'export { defaultAuthPlugin as default } from "@agent-native/core/server";\n');
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
if (fs.existsSync(agentChatPath)) {
|
|
1788
|
+
const agentChat = fs.readFileSync(agentChatPath, "utf-8");
|
|
1789
|
+
if (containsModuleSpecifier(agentChat, sourceWorkspaceCore) &&
|
|
1790
|
+
agentChat.includes("createWorkspaceAgentChatPlugin") &&
|
|
1791
|
+
agentChat.includes("actionsRegistry")) {
|
|
1792
|
+
fs.writeFileSync(agentChatPath, [
|
|
1793
|
+
'import { createAgentChatPlugin, loadActionsFromStaticRegistry } from "@agent-native/core/server";',
|
|
1794
|
+
'import actionsRegistry from "../../.generated/actions-registry.js";',
|
|
1795
|
+
"",
|
|
1796
|
+
"export default createAgentChatPlugin({",
|
|
1797
|
+
` appId: ${JSON.stringify(options.destinationAppName)},`,
|
|
1798
|
+
" actions: loadActionsFromStaticRegistry(actionsRegistry),",
|
|
1799
|
+
"});",
|
|
1800
|
+
"",
|
|
1801
|
+
].join("\n"));
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
if (options.shape === "workspace" && options.targetWorkspaceCoreName) {
|
|
1807
|
+
for (const pluginPath of [authPath, agentChatPath]) {
|
|
1808
|
+
if (!fs.existsSync(pluginPath))
|
|
1809
|
+
continue;
|
|
1810
|
+
const content = fs.readFileSync(pluginPath, "utf-8");
|
|
1811
|
+
const next = replaceModuleSpecifierPrefix(content, sourceWorkspaceCore, options.targetWorkspaceCoreName);
|
|
1812
|
+
if (next !== content)
|
|
1813
|
+
fs.writeFileSync(pluginPath, next);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
function containsModuleSpecifier(content, moduleName) {
|
|
1818
|
+
return new RegExp(`(["'])${escapeRegExp(moduleName)}(?:/[^"']*)?\\1`).test(content);
|
|
1819
|
+
}
|
|
1820
|
+
function replaceModuleSpecifierPrefix(content, sourceModule, targetModule) {
|
|
1821
|
+
return content.replace(new RegExp(`(["'])${escapeRegExp(sourceModule)}((?:/[^"']*)?)\\1`, "g"), (_match, quote, suffix) => `${quote}${targetModule}${suffix}${quote}`);
|
|
1822
|
+
}
|
|
1823
|
+
function findSourceImportSpecifier(dir, moduleName) {
|
|
1824
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
1825
|
+
if (entry.isSymbolicLink())
|
|
1826
|
+
continue;
|
|
1827
|
+
const filePath = path.join(dir, entry.name);
|
|
1828
|
+
if (entry.isDirectory()) {
|
|
1829
|
+
const nested = findSourceImportSpecifier(filePath, moduleName);
|
|
1830
|
+
if (nested)
|
|
1831
|
+
return nested;
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
if (!/\.(?:[cm]?[jt]sx?)$/.test(entry.name))
|
|
1835
|
+
continue;
|
|
1836
|
+
try {
|
|
1837
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
1838
|
+
const escaped = escapeRegExp(moduleName);
|
|
1839
|
+
if (new RegExp(`(?:from\\s*|import\\s*\\(\\s*|import\\s*|require\\s*\\(\\s*)(["'])${escaped}(?:/[^"']*)?\\1`).test(content)) {
|
|
1840
|
+
return filePath;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
catch {
|
|
1844
|
+
// Ignore unreadable source files; package validation reports main issues.
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
return undefined;
|
|
1848
|
+
}
|
|
1849
|
+
function loadCommunityWorkspaceCatalog(workspaceRoot) {
|
|
1850
|
+
const workspacePath = path.join(workspaceRoot, "pnpm-workspace.yaml");
|
|
1851
|
+
if (!fs.existsSync(workspacePath))
|
|
1852
|
+
return {};
|
|
1853
|
+
const result = {};
|
|
1854
|
+
let inCatalog = false;
|
|
1855
|
+
for (const line of fs.readFileSync(workspacePath, "utf-8").split("\n")) {
|
|
1856
|
+
if (/^catalog:\s*$/.test(line)) {
|
|
1857
|
+
inCatalog = true;
|
|
1858
|
+
continue;
|
|
1859
|
+
}
|
|
1860
|
+
if (!inCatalog)
|
|
1861
|
+
continue;
|
|
1862
|
+
if (/^\S/.test(line))
|
|
1863
|
+
break;
|
|
1864
|
+
const match = line.match(/^\s+"?([^":]+)"?\s*:\s*"?([^"]+)"?\s*$/);
|
|
1865
|
+
if (match)
|
|
1866
|
+
result[match[1]] = match[2];
|
|
1867
|
+
}
|
|
1868
|
+
return result;
|
|
1869
|
+
}
|
|
1870
|
+
function readCommunitySourceIdentity(targetDir, repo) {
|
|
1871
|
+
const fallbackName = repo.split("/").pop() ?? "app";
|
|
1872
|
+
try {
|
|
1873
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(targetDir, "package.json"), "utf-8"));
|
|
1874
|
+
const appName = typeof pkg.name === "string" && pkg.name.trim()
|
|
1875
|
+
? pkg.name.trim()
|
|
1876
|
+
: fallbackName;
|
|
1877
|
+
const unscopedName = appName.split("/").pop() ?? appName;
|
|
1878
|
+
const appTitle = typeof pkg.displayName === "string" && pkg.displayName.trim()
|
|
1879
|
+
? pkg.displayName.trim()
|
|
1880
|
+
: titleCase(unscopedName);
|
|
1881
|
+
return { appName, appTitle };
|
|
1882
|
+
}
|
|
1883
|
+
catch {
|
|
1884
|
+
return { appName: fallbackName, appTitle: titleCase(fallbackName) };
|
|
1885
|
+
}
|
|
1256
1886
|
}
|
|
1257
1887
|
function githubTarballUrl(repo, ref, kind) {
|
|
1258
1888
|
return `https://codeload.github.com/${repo}/tar.gz/refs/${kind === "tag" ? "tags" : "heads"}/${encodeURIComponent(ref)}`;
|
|
@@ -1368,6 +1998,9 @@ function isChatOnRampTemplate(templateName) {
|
|
|
1368
1998
|
return templateName === "chat" || templateName === "starter";
|
|
1369
1999
|
}
|
|
1370
2000
|
function trackingTemplateName(templateName) {
|
|
2001
|
+
if (templateName && isCommunityTemplateSelection(templateName)) {
|
|
2002
|
+
return undefined;
|
|
2003
|
+
}
|
|
1371
2004
|
return templateName === "starter" ? "chat" : templateName;
|
|
1372
2005
|
}
|
|
1373
2006
|
function defaultPackageDescriptionForScaffold(appName) {
|
|
@@ -1398,26 +2031,36 @@ function fixPackageJsonName(appDir, name, templateName, provenance) {
|
|
|
1398
2031
|
(isChatOnRampTemplate(templateName) && name !== templateName)) {
|
|
1399
2032
|
pkg.description = defaultPackageDescriptionForScaffold(name);
|
|
1400
2033
|
}
|
|
2034
|
+
else if (provenance?.sourceIdentity &&
|
|
2035
|
+
typeof pkg.description === "string") {
|
|
2036
|
+
pkg.description = replaceSourceIdentityText(pkg.description, provenance.sourceIdentity, name, appTitle);
|
|
2037
|
+
}
|
|
1401
2038
|
const scaffoldGuidance = scaffoldGuidanceForTemplate(templateName);
|
|
1402
|
-
if (scaffoldGuidance) {
|
|
2039
|
+
if (scaffoldGuidance || provenance?.communityTemplate) {
|
|
1403
2040
|
const agentNative = pkg["agent-native"] &&
|
|
1404
2041
|
typeof pkg["agent-native"] === "object" &&
|
|
1405
2042
|
!Array.isArray(pkg["agent-native"])
|
|
1406
2043
|
? pkg["agent-native"]
|
|
1407
2044
|
: {};
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
2045
|
+
if (scaffoldGuidance) {
|
|
2046
|
+
const coreVersion = getCorePackageVersion();
|
|
2047
|
+
agentNative.scaffold = {
|
|
2048
|
+
template: trackingTemplateName(templateName),
|
|
2049
|
+
frameworkSkills: scaffoldGuidance,
|
|
2050
|
+
...(provenance?.templateRef
|
|
2051
|
+
? { templateRef: provenance.templateRef }
|
|
2052
|
+
: {}),
|
|
2053
|
+
...(provenance?.templateSource
|
|
2054
|
+
? { templateSource: provenance.templateSource }
|
|
2055
|
+
: {}),
|
|
2056
|
+
...(coreVersion ? { coreVersion } : {}),
|
|
2057
|
+
...(provenance?.shape ? { shape: provenance.shape } : {}),
|
|
2058
|
+
};
|
|
2059
|
+
}
|
|
2060
|
+
if (provenance?.communityTemplate) {
|
|
2061
|
+
delete agentNative.scaffold;
|
|
2062
|
+
agentNative.communityTemplate = provenance.communityTemplate;
|
|
2063
|
+
}
|
|
1421
2064
|
pkg["agent-native"] = agentNative;
|
|
1422
2065
|
}
|
|
1423
2066
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
@@ -1432,9 +2075,11 @@ function scaffoldGuidanceForTemplate(templateName) {
|
|
|
1432
2075
|
return "headless";
|
|
1433
2076
|
return getTemplate(normalized) ? "default" : undefined;
|
|
1434
2077
|
}
|
|
1435
|
-
function fixWebManifestName(appDir, name, templateName) {
|
|
1436
|
-
if (!isChatOnRampTemplate(templateName) || name === templateName)
|
|
2078
|
+
function fixWebManifestName(appDir, name, templateName, sourceIdentity) {
|
|
2079
|
+
if ((!isChatOnRampTemplate(templateName) || name === templateName) &&
|
|
2080
|
+
!sourceIdentity) {
|
|
1437
2081
|
return;
|
|
2082
|
+
}
|
|
1438
2083
|
const manifestPath = path.join(appDir, "public", "manifest.json");
|
|
1439
2084
|
if (!fs.existsSync(manifestPath))
|
|
1440
2085
|
return;
|
|
@@ -1443,7 +2088,10 @@ function fixWebManifestName(appDir, name, templateName) {
|
|
|
1443
2088
|
const appTitle = titleCase(name);
|
|
1444
2089
|
manifest.name = appTitle;
|
|
1445
2090
|
manifest.short_name = appTitle;
|
|
1446
|
-
if (typeof manifest.description
|
|
2091
|
+
if (sourceIdentity && typeof manifest.description === "string") {
|
|
2092
|
+
manifest.description = replaceSourceIdentityText(manifest.description, sourceIdentity, name, appTitle);
|
|
2093
|
+
}
|
|
2094
|
+
else if (typeof manifest.description !== "string" ||
|
|
1447
2095
|
/\b(blank app|starter|chat-first)\b/i.test(manifest.description)) {
|
|
1448
2096
|
manifest.description = defaultPackageDescriptionForScaffold(name);
|
|
1449
2097
|
}
|
|
@@ -1451,6 +2099,21 @@ function fixWebManifestName(appDir, name, templateName) {
|
|
|
1451
2099
|
}
|
|
1452
2100
|
catch { }
|
|
1453
2101
|
}
|
|
2102
|
+
function replaceSourceIdentityText(value, sourceIdentity, appName, appTitle) {
|
|
2103
|
+
const unscopedSourceName = sourceIdentity.appName.split("/").pop() ?? sourceIdentity.appName;
|
|
2104
|
+
const replacements = new Map([
|
|
2105
|
+
[sourceIdentity.appTitle, appTitle],
|
|
2106
|
+
[sourceIdentity.appName, appName],
|
|
2107
|
+
[unscopedSourceName, appName],
|
|
2108
|
+
]);
|
|
2109
|
+
const sourceValues = [...replacements.keys()]
|
|
2110
|
+
.filter(Boolean)
|
|
2111
|
+
.sort((a, b) => b.length - a.length);
|
|
2112
|
+
if (sourceValues.length === 0)
|
|
2113
|
+
return value;
|
|
2114
|
+
const pattern = new RegExp(sourceValues.map(escapeRegExp).join("|"), "g");
|
|
2115
|
+
return value.replace(pattern, (match) => replacements.get(match) ?? match);
|
|
2116
|
+
}
|
|
1454
2117
|
function getCoreDependencyVersion() {
|
|
1455
2118
|
if (process.env.AGENT_NATIVE_CREATE_USE_LOCAL_CORE === "1") {
|
|
1456
2119
|
const localCore = findLocalPackage("core");
|
|
@@ -1719,12 +2382,25 @@ function rewriteNetlifyToml(appDir, appName, mode) {
|
|
|
1719
2382
|
}
|
|
1720
2383
|
catch { }
|
|
1721
2384
|
}
|
|
1722
|
-
function
|
|
2385
|
+
function applyScaffoldIdentity(appDir, appName, templateName, sourceIdentity) {
|
|
2386
|
+
rewriteTrackingAppId(appDir, appName, templateName, sourceIdentity);
|
|
2387
|
+
rewriteAgentChatAppId(appDir, appName, templateName, sourceIdentity);
|
|
2388
|
+
rewriteAppConfigIdentity(appDir, appName, sourceIdentity);
|
|
2389
|
+
}
|
|
2390
|
+
function rewriteAgentChatAppId(appDir, appName, templateName, sourceIdentity) {
|
|
1723
2391
|
const pluginPath = path.join(appDir, "server", "plugins", "agent-chat.ts");
|
|
1724
2392
|
if (!fs.existsSync(pluginPath))
|
|
1725
2393
|
return;
|
|
1726
2394
|
try {
|
|
1727
2395
|
const content = fs.readFileSync(pluginPath, "utf-8");
|
|
2396
|
+
if (sourceIdentity) {
|
|
2397
|
+
const next = content
|
|
2398
|
+
.replace(/(createAgentChatPlugin\(\{[\s\S]*?\bappId:\s*)(["'])[^"']+\2/, (_match, prefix, quote) => `${prefix}${quote}${appName}${quote}`)
|
|
2399
|
+
.replace(/(const\s+options\s*=\s*\{[\s\S]*?\bappId:\s*)(["'])[^"']+\2/, (_match, prefix, quote) => `${prefix}${quote}${appName}${quote}`);
|
|
2400
|
+
if (next !== content)
|
|
2401
|
+
fs.writeFileSync(pluginPath, next);
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
1728
2404
|
const sourceAppIds = ["chat", "starter"];
|
|
1729
2405
|
if (templateName && templateName !== appName) {
|
|
1730
2406
|
sourceAppIds.push(templateName);
|
|
@@ -1739,13 +2415,19 @@ function rewriteAgentChatAppId(appDir, appName, templateName) {
|
|
|
1739
2415
|
}
|
|
1740
2416
|
catch { }
|
|
1741
2417
|
}
|
|
1742
|
-
function rewriteTrackingAppId(appDir, appName, templateName) {
|
|
2418
|
+
function rewriteTrackingAppId(appDir, appName, templateName, sourceIdentity) {
|
|
1743
2419
|
const rootPath = path.join(appDir, "app", "root.tsx");
|
|
1744
2420
|
if (!fs.existsSync(rootPath))
|
|
1745
2421
|
return;
|
|
1746
2422
|
try {
|
|
1747
2423
|
const content = fs.readFileSync(rootPath, "utf-8");
|
|
1748
2424
|
const trackedTemplateName = trackingTemplateName(templateName);
|
|
2425
|
+
if (sourceIdentity) {
|
|
2426
|
+
const next = content.replace(/(configureTracking\(\{[\s\S]*?\bapp:\s*)(["'])[^"']+\2/, (_match, prefix, quote) => `${prefix}${quote}${appName}${quote}`);
|
|
2427
|
+
if (next !== content)
|
|
2428
|
+
fs.writeFileSync(rootPath, next);
|
|
2429
|
+
return;
|
|
2430
|
+
}
|
|
1749
2431
|
const sourceAppIds = ["agent-native-[^\"']+", "\\{\\{APP_NAME\\}\\}"];
|
|
1750
2432
|
if (templateName && templateName !== appName) {
|
|
1751
2433
|
sourceAppIds.push(escapeRegExp(templateName));
|
|
@@ -1768,6 +2450,23 @@ function rewriteTrackingAppId(appDir, appName, templateName) {
|
|
|
1768
2450
|
}
|
|
1769
2451
|
catch { }
|
|
1770
2452
|
}
|
|
2453
|
+
function rewriteAppConfigIdentity(appDir, appName, sourceIdentity) {
|
|
2454
|
+
if (!sourceIdentity)
|
|
2455
|
+
return;
|
|
2456
|
+
const configPath = path.join(appDir, "app", "lib", "app-config.ts");
|
|
2457
|
+
if (!fs.existsSync(configPath))
|
|
2458
|
+
return;
|
|
2459
|
+
try {
|
|
2460
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
2461
|
+
const appTitle = appTitleForScaffold(appName);
|
|
2462
|
+
const next = content
|
|
2463
|
+
.replace(/(const\s+rawAppName\s*=\s*)(["'])[^"']*\2/, (_match, prefix, quote) => `${prefix}${quote}${appName}${quote}`)
|
|
2464
|
+
.replace(/(const\s+rawAppTitle\s*=\s*)(["'])[^"']*\2/, (_match, prefix, quote) => `${prefix}${quote}${appTitle}${quote}`);
|
|
2465
|
+
if (next !== content)
|
|
2466
|
+
fs.writeFileSync(configPath, next);
|
|
2467
|
+
}
|
|
2468
|
+
catch { }
|
|
2469
|
+
}
|
|
1771
2470
|
function escapeRegExp(value) {
|
|
1772
2471
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1773
2472
|
}
|