@agent-native/core 0.141.7 → 0.144.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/agent-native.eject.json +9 -0
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +19 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/analytics/AGENTS.md +35 -38
- package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +1 -1
- package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +17 -22
- package/corpus/templates/analytics/actions/navigate.ts +4 -5
- package/corpus/templates/analytics/actions/provider-api-register.ts +50 -0
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +44 -11
- package/corpus/templates/analytics/actions/test-custom-api-connection.ts +106 -0
- package/corpus/templates/analytics/app/components/CustomApiCard.tsx +430 -0
- package/corpus/templates/analytics/app/i18n-data.ts +680 -0
- package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +6 -0
- package/corpus/templates/analytics/app/lib/sql-query.ts +33 -6
- package/corpus/templates/analytics/app/pages/DataSources.tsx +4 -0
- package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-load-reliably-during-background-data-sw.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-remain-responsive-while-historical-data.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-analytics-query-results-now-render-as-a-table-with-csv-downl.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-connect-public-https-custom-apis-and-hand-tested-endpoints-t.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-durable-agent-worker-memory.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-live-dashboard-panels-use-the-shared-query-timeout.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-on-demand-billing-is-available-as-a-native-dashboard-with-ta.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-06-visible-dashboard-names-are-unique.md +6 -0
- package/corpus/templates/analytics/netlify.toml +7 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +4 -0
- package/corpus/templates/analytics/server/db/index.ts +27 -0
- package/corpus/templates/analytics/server/db/schema.ts +10 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +227 -60
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +133 -44
- package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +227 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +1 -0
- package/corpus/templates/analytics/server/plugins/db.ts +52 -18
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +28 -2
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +16 -1
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/assets/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/assets/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/assets/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/brain/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/brain/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/brain/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/brain/actions/list-connection-providers.ts +24 -3
- package/corpus/templates/brain/app/lib/dispatch-links.ts +44 -0
- package/corpus/templates/brain/app/routes/sources.tsx +15 -7
- package/corpus/templates/brain/changelog/2026-08-06-brain-provider-setup-links-open-dispatch-correctly-in-standa.md +6 -0
- package/corpus/templates/brain/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +26 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/calendar/AGENTS.md +11 -32
- package/corpus/templates/calendar/actions/update-event.ts +107 -1
- package/corpus/templates/calendar/app/components/calendar/EventCalendarSelect.tsx +83 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +57 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +59 -73
- package/corpus/templates/calendar/app/hooks/use-events.ts +4 -1
- package/corpus/templates/calendar/app/i18n-data.ts +20 -0
- package/corpus/templates/calendar/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-06-existing-events-can-move-between-connected-google-account-ca.md +6 -0
- package/corpus/templates/calendar/server/lib/google-calendar.ts +101 -0
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +7 -1
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/clips/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/clips/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/clips/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +9 -24
- package/corpus/templates/clips/app/components/editor/timeline-geometry.ts +15 -0
- package/corpus/templates/clips/app/components/editor/waveform.tsx +4 -17
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +14 -3
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +28 -38
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -27
- package/corpus/templates/clips/changelog/2026-08-06-clips-recovers-from-stalled-recording-setup-without-requirin.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-06-clips-starts-faster-after-a-deploy-or-idle-period-startup-no.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-06-deleting-a-recording-returns-cleanly-to-the-library-and-fit-.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-06-hide-clip-insights-from-viewers-without-analytics-access.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +48 -20
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +275 -74
- package/corpus/templates/clips/desktop/src/lib/recording-start-guard.ts +84 -0
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +14 -4
- package/corpus/templates/clips/server/plugins/db.ts +214 -26
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/content/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/content/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/content/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/crm/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/crm/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/crm/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/crm/changelog/2026-08-06-crm-starts-faster-after-a-deploy-or-idle-period-one-time-dat.md +6 -0
- package/corpus/templates/crm/server/plugins/db.ts +11 -8
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +36 -1
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/design/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/design/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/design/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/design/actions/import-from-url.ts +1 -1
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +27 -18
- package/corpus/templates/design/app/pages/Index.tsx +6 -2
- package/corpus/templates/design/changelog/2026-08-05-deleting-a-design-from-the-home-page-no-longer-leaves-the-ap.md +6 -0
- package/corpus/templates/design/changelog/2026-08-05-renaming-a-layer-now-focuses-its-name-field-automatically.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/dispatch/changelog/2026-08-06-ready-workspace-apps-stay-one-click-away-in-a-compact-dispat.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-06-shared-vault-values-and-management-are-now-restricted-to-wor.md +6 -0
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/factory/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/factory/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/factory/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/forms/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/forms/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/forms/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/forms/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
- package/corpus/templates/forms/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/macros/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/macros/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/macros/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/mail/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/mail/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/mail/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/mail/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
- package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/plan/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/plan/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/plan/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +48 -5
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +37 -1
- package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +6 -0
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +15 -2
- package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +29 -24
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/slides/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/slides/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/slides/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/corpus/templates/slides/AGENTS.md +7 -9
- package/corpus/templates/slides/_gitignore +1 -0
- package/corpus/templates/slides/actions/export-pptx.ts +402 -36
- package/corpus/templates/slides/actions/import-file.ts +46 -85
- package/corpus/templates/slides/actions/import-google-slides-reference.ts +271 -12
- package/corpus/templates/slides/actions/import-pptx.ts +85 -60
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +10 -13
- package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +5 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +25 -12
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +112 -57
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +202 -0
- package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +26 -14
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +173 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +182 -141
- package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +18 -0
- package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +85 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -0
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +4 -0
- package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +27 -1
- package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -1
- package/corpus/templates/slides/app/lib/deck-preview-frame.ts +15 -0
- package/corpus/templates/slides/app/lib/deck-share-links.ts +9 -0
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +89 -0
- package/corpus/templates/slides/app/lib/slide-animation-elements.ts +3 -0
- package/corpus/templates/slides/app/pages/Index.tsx +91 -1
- package/corpus/templates/slides/changelog/2026-08-06-clicking-the-gray-canvas-around-a-slide-clears-its-selection.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-deck-thumbnails-keep-a-consistent-listing-frame-across-aspec.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-deep-linked-decks-open-directly-instead-of-being-interrupted.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-google-slides-exports-now-show-clear-import-steps-when-direc.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-import-pdf-google-slides-and-powerpoint-decks-directly-from-.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-imported-powerpoint-exports-keep-text-and-images-editable-an.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-presentation-prompts-continue-adding-rendered-slides-until-t.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-public-decks-now-share-read-only-presentation-links-by-defau.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-slides-offers-a-direct-connect-google-button-when-a-drive-co.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-slides-preserve-uploaded-pdf-layouts-accept-larger-reference.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-06-styled-words-stay-part-of-their-text-box-instead-of-becoming.md +6 -0
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +7 -20
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +236 -13
- package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +67 -0
- package/corpus/templates/slides/server/handlers/import/pptx-parser.ts +2 -0
- package/corpus/templates/slides/server/handlers/uploads.ts +7 -3
- package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -3
- package/corpus/templates/slides/server/lib/emails.ts +7 -2
- package/corpus/templates/slides/server/lib/google-docs-error.ts +50 -0
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +1 -0
- package/corpus/templates/slides/server/lib/import-asset-storage.ts +127 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -3
- package/corpus/templates/slides/server/routes/api/import-assets/[token].get.ts +70 -0
- package/corpus/templates/slides/shared/upload-types.ts +3 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/agent/production-agent.d.ts +17 -0
- package/dist/agent/production-agent.js +72 -1
- package/dist/cli/agent-plugin.d.ts +112 -0
- package/dist/cli/agent-plugin.js +1081 -0
- package/dist/cli/app-skill.js +39 -1
- package/dist/cli/code-agent-executor.d.ts +3 -0
- package/dist/cli/code-agent-executor.js +51 -13
- package/dist/cli/index.js +13 -0
- package/dist/cli/templates-meta.js +1 -1
- package/dist/client/AgentPanel.d.ts +3 -1
- package/dist/client/AgentPanel.js +9 -2
- package/dist/client/AssistantChat.d.ts +8 -0
- package/dist/client/AssistantChat.js +23 -8
- package/dist/client/NewWorkspaceAppFlow.js +1 -0
- package/dist/client/agent-chat-adapter.js +123 -4
- package/dist/client/assistant-ui-recovery.d.ts +2 -1
- package/dist/client/assistant-ui-recovery.js +7 -3
- package/dist/client/onboarding/FirstRunOnboarding.js +11 -4
- package/dist/client/org/TeamPage.js +1 -1
- package/dist/client/require-session.js +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
- package/dist/client/resources/McpConnectionSuggestion.js +27 -10
- package/dist/client/resources/McpIntegrationDialog.js +77 -45
- package/dist/client/resources/mcp-integration-catalog.d.ts +13 -1
- package/dist/client/resources/mcp-integration-catalog.js +255 -2
- package/dist/client/resources/mcp-integration-logos.js +18 -2
- package/dist/client/settings/AccountSettingsCard.js +2 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +9 -0
- package/dist/db/client.js +51 -16
- package/dist/db/create-get-db.js +2 -2
- package/dist/db/ddl-guard.d.ts +11 -10
- package/dist/db/ddl-guard.js +40 -12
- package/dist/deploy/build.d.ts +2 -0
- package/dist/deploy/build.js +97 -4
- package/dist/deploy/workspace-deploy.js +0 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/guards/no-env-credentials.js +9 -0
- package/dist/ingestion/index.d.ts +1 -1
- package/dist/ingestion/pptx.d.ts +68 -0
- package/dist/ingestion/pptx.js +660 -213
- package/dist/integrations/plugin.js +9 -6
- package/dist/localization/default-messages.d.ts +54 -0
- package/dist/localization/default-messages.js +57 -2
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp-client/oauth-routes.d.ts +2 -0
- package/dist/mcp-client/oauth-routes.js +73 -6
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/app-profile.js +11 -0
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/resources/store.js +15 -1
- package/dist/scripts/call-agent.js +34 -4
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat/action-filters-a2a.d.ts +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +36 -1
- package/dist/server/agent-chat-plugin.js +29 -8
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +47 -11
- package/dist/server/better-auth-instance.js +5 -3
- package/dist/server/core-routes-plugin.d.ts +8 -1
- package/dist/server/core-routes-plugin.js +28 -3
- package/dist/server/credential-provider.d.ts +3 -3
- package/dist/server/credential-provider.js +90 -4
- package/dist/server/onboarding-html.d.ts +2 -0
- package/dist/server/onboarding-html.js +115 -29
- package/dist/server/self-dispatch.d.ts +5 -4
- package/dist/server/self-dispatch.js +25 -14
- package/dist/server/ssr-handler.d.ts +1 -1
- package/dist/server/ssr-handler.js +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/cache-control.d.ts +26 -2
- package/dist/shared/cache-control.js +43 -2
- package/dist/shared/password-policy.d.ts +3 -0
- package/dist/shared/password-policy.js +3 -0
- package/dist/sharing/actions/set-resource-visibility.js +16 -4
- package/dist/sharing/registry.d.ts +14 -0
- package/dist/styles/agent-native.css +6 -0
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/dist/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/dist/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/dist/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/dist/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/dist/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/dist/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/dist/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/dist/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/dist/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
- package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/dist/templates/workspace-root/AGENTS.md +7 -0
- package/dist/user-profile/actions/change-password.js +8 -3
- package/dist/user-profile/actions/set-password.js +5 -1
- package/dist/vite/action-types-plugin.js +59 -0
- package/docs/content/skills-guide.mdx +13 -0
- package/package.json +6 -2
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/src/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/src/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/src/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/src/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/src/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/src/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/src/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/src/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/src/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
- package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
- package/src/templates/workspace-root/AGENTS.md +7 -0
|
@@ -6,11 +6,10 @@ import {
|
|
|
6
6
|
} from "@agent-native/core/server";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
9
|
+
import {
|
|
10
|
+
listDashboardSummaries,
|
|
11
|
+
normalizeDashboardName,
|
|
12
|
+
} from "../server/lib/dashboards-store";
|
|
14
13
|
|
|
15
14
|
async function resolveDashboardIdByName(name: string): Promise<string> {
|
|
16
15
|
const email = getRequestUserEmail();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getDbExec } from "@agent-native/core/db";
|
|
2
|
+
import {
|
|
3
|
+
createCustomProviderRegistrationAction,
|
|
4
|
+
CustomProviderRegistrationSchema,
|
|
5
|
+
} from "@agent-native/core/provider-api/actions/custom-provider-registration";
|
|
6
|
+
import { getCredentialContext } from "@agent-native/core/server/request-context";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
async function resolveCallerOrgRole(orgId: string, email: string) {
|
|
10
|
+
try {
|
|
11
|
+
const { rows } = await getDbExec().execute({
|
|
12
|
+
sql: `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,
|
|
13
|
+
args: [orgId, email.toLowerCase()],
|
|
14
|
+
});
|
|
15
|
+
const role = (rows[0] as { role?: unknown } | undefined)?.role;
|
|
16
|
+
return typeof role === "string" && role ? role : null;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const schema = CustomProviderRegistrationSchema.superRefine((value, ctx) => {
|
|
23
|
+
if (value.operation === "upsert" && value.baseUrl) {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(value.baseUrl);
|
|
26
|
+
if (url.protocol !== "https:") {
|
|
27
|
+
ctx.addIssue({
|
|
28
|
+
code: z.ZodIssueCode.custom,
|
|
29
|
+
path: ["baseUrl"],
|
|
30
|
+
message: "baseUrl must use public HTTPS.",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// The shared schema reports malformed URLs.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const action = createCustomProviderRegistrationAction({
|
|
40
|
+
schema,
|
|
41
|
+
getContext: getCredentialContext,
|
|
42
|
+
resolveOrgRole: resolveCallerOrgRole,
|
|
43
|
+
description:
|
|
44
|
+
"Register or update an Analytics custom API provider. Stores credential key names only, never secret values. Requires a public HTTPS base URL and owner/admin organization access.",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// The shared factory owns registration, validation, and mutation authorization;
|
|
48
|
+
// this wrapper only adds Analytics' transport contract and base URL policy.
|
|
49
|
+
// Static action registry marker: createCustomProviderRegistrationAction returns defineAction.
|
|
50
|
+
export default { ...action, http: { method: "POST" as const } };
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ACTION_CHAT_UI_DATA_TABLE_RENDERER,
|
|
3
|
+
dataTableWidgetResultSchema,
|
|
4
|
+
defineAction,
|
|
5
|
+
} from "@agent-native/core";
|
|
6
|
+
import { createDataTableWidgetResult } from "@agent-native/core/data-widgets";
|
|
2
7
|
import {
|
|
3
8
|
getRequestOrgId,
|
|
4
9
|
getRequestUserEmail,
|
|
@@ -13,6 +18,34 @@ function resolveScope() {
|
|
|
13
18
|
return { userEmail, orgId: getRequestOrgId() || null };
|
|
14
19
|
}
|
|
15
20
|
|
|
21
|
+
function toDataTableResult(result: {
|
|
22
|
+
rows: Record<string, unknown>[];
|
|
23
|
+
schema: { name: string; type: string }[];
|
|
24
|
+
}) {
|
|
25
|
+
const numericTypes = new Set([
|
|
26
|
+
"number",
|
|
27
|
+
"integer",
|
|
28
|
+
"float",
|
|
29
|
+
"double",
|
|
30
|
+
"decimal",
|
|
31
|
+
"numeric",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
return createDataTableWidgetResult({
|
|
35
|
+
widgetId: "analytics.query.v1",
|
|
36
|
+
title: "Analytics query result",
|
|
37
|
+
table: {
|
|
38
|
+
title: "Analytics query result",
|
|
39
|
+
columns: result.schema.map(({ name, type }) => ({
|
|
40
|
+
key: name,
|
|
41
|
+
label: name,
|
|
42
|
+
...(numericTypes.has(type.toLowerCase()) ? { align: "right" } : {}),
|
|
43
|
+
})),
|
|
44
|
+
rows: result.rows,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
16
49
|
export default defineAction({
|
|
17
50
|
description:
|
|
18
51
|
"Query the built-in first-party Analytics source: events recorded through this app's analytics collector endpoint (/track), compact daily rollups updated transactionally with new ingest, identifiable user-day rollups, and session replay summaries recorded through /api/analytics/replay. This source does not require an external provider connection. Use it for questions about app/site traffic, product events, template/app usage, conversions, session recordings, LLM/agent observability, model cost, token volume, latency, and other first-party data collected by this analytics app. Use source-specific actions such as BigQuery, GA4, Mixpanel, PostHog, or Amplitude when the user asks for those sources or the relevant data lives there. SQL may read analytics_events, analytics_event_daily_rollups, analytics_user_days, and session_recordings; session_replay_chunks is intentionally unavailable, and reads are automatically scoped to the current user/org. Prefer analytics_event_daily_rollups for event counts and analytics_user_days for active-user or retention questions. On the Builder.io production organization after the explicit BigQuery cutover, event and rollup reads use partitioned BigQuery tables/views while session_recordings remains in the SQL store; cross-backend joins are not supported. Before a large or historical query, call get-first-party-analytics-health and use a configured external backend when it recommends one: BigQuery supports warehouse SQL and historical analysis, while Amplitude supports product analytics, funnels, and retention. Connecting a backend does not automatically reroute /track events or copy existing Neon events; the migration action performs that explicit prepare, backfill, and cutover sequence. Aggregate, project only needed columns, use bounded recent drill-downs, and add a LIMIT for raw or high-cardinality reads; do not issue an unbounded raw-event scan or paginate a large cohort. An explicit all-time or lifetime request remains all-time, so do not invent a default lower time bound. Safe scoped results are cached for up to five minutes for this agent action. analytics_events columns include event_name, timestamp, event_date, user_id, anonymous_id, user_key, session_id, app, template, signed_in, url, path, hostname, referrer, properties, and context. analytics_event_daily_rollups columns include tenant_key, owner_email, org_id, event_date, event_name, app, template, and event_count. analytics_user_days columns include tenant_key, owner_email, org_id, event_date, and user_key. LLM observability events use event_name = '$ai_generation'; useful properties include $ai_trace_id/run_id, $ai_session_id/thread_id, $ai_model/model, $ai_provider/provider, $ai_input_tokens/input_tokens, $ai_output_tokens/output_tokens, cache_read_tokens, cache_write_tokens, cost_cents_x100, $ai_total_cost_usd/cost_usd, duration_ms/$ai_latency, status, tool_calls, successful_tools, failed_tools, tools, tools_truncated, delegated, delegation_protocol, caller_app, delegation_task_id, a2a_task_id, parent_run_id, parent_turn_id, and error_message/$ai_error. Agent Teams child runs use delegation_protocol = 'agent-team' and retain their own run_id while linking to the launching run through parent_run_id. The bounded tools array contains names, relative start times, durations, statuses, and coarse error classes only, never args or results; failed runs and interrupted tools remain queryable. session_recordings columns include id, session_id, user_id, anonymous_id, user_key, started_at, ended_at, duration_ms, chunk_count, event_count, page_count, error_count, rage_click_count, app, template, status, first_url, last_url, path, hostname, referrer, and metadata.",
|
|
@@ -23,15 +56,12 @@ export default defineAction({
|
|
|
23
56
|
"Read-only SQL over analytics_events, analytics_event_daily_rollups, analytics_user_days, and session_recordings. Use literal values, not bind placeholders. Prefer the daily rollups for counts, active-user, and retention questions. On the Builder.io production organization after the explicit BigQuery cutover, event and rollup reads use partitioned BigQuery tables/views while session_recordings remains in the SQL store; cross-backend joins are not supported. Before a large or historical query, call get-first-party-analytics-health and use a configured external backend when recommended: BigQuery for warehouse SQL and complete history, or Amplitude for product analytics, funnels, and retention. Connecting one does not automatically reroute /track events or backfill existing Neon data; the migration action performs that explicit prepare, backfill, and cutover sequence. Aggregate or project only needed columns and add a LIMIT for raw or high-cardinality reads; do not issue an unbounded raw-event scan or paginate a large cohort. An explicit all-time or lifetime request remains all-time, so do not invent a default lower time bound. Example: SELECT event_date, event_name, SUM(event_count) AS events FROM analytics_event_daily_rollups WHERE event_date >= '2026-05-01' AND event_date < '2026-06-01' GROUP BY event_date, event_name ORDER BY event_date, events DESC",
|
|
24
57
|
),
|
|
25
58
|
}),
|
|
26
|
-
outputSchema:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}),
|
|
33
|
-
),
|
|
34
|
-
}),
|
|
59
|
+
outputSchema: dataTableWidgetResultSchema,
|
|
60
|
+
chatUI: {
|
|
61
|
+
renderer: ACTION_CHAT_UI_DATA_TABLE_RENDERER,
|
|
62
|
+
title: "Analytics query result",
|
|
63
|
+
description: "Render query rows as a native table with CSV download.",
|
|
64
|
+
},
|
|
35
65
|
readOnly: true,
|
|
36
66
|
// No raw HTTP or connector-catalog route: SQL would land in query strings,
|
|
37
67
|
// logs, or a caller that lacks this app's schema and data dictionary. This
|
|
@@ -40,6 +70,9 @@ export default defineAction({
|
|
|
40
70
|
http: false,
|
|
41
71
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
42
72
|
run: async (args) => {
|
|
43
|
-
|
|
73
|
+
const result = await queryFirstPartyAnalytics(args.sql, resolveScope(), {
|
|
74
|
+
cache: true,
|
|
75
|
+
});
|
|
76
|
+
return toDataTableResult(result);
|
|
44
77
|
},
|
|
45
78
|
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { extractItemsArray } from "@agent-native/core/provider-api/staging";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { getAnalyticsProviderApiRuntime } from "../server/lib/provider-api";
|
|
6
|
+
|
|
7
|
+
const secretKey = /pass(word)?|secret|token|api[-_]?key|authorization/i;
|
|
8
|
+
const Input = z.object({
|
|
9
|
+
provider: z.string().min(1),
|
|
10
|
+
path: z.string().min(1),
|
|
11
|
+
query: z.record(z.string(), z.unknown()).optional(),
|
|
12
|
+
itemsPath: z.string().min(1).optional(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function redact(value: unknown): unknown {
|
|
16
|
+
if (Array.isArray(value)) return value.map(redact);
|
|
17
|
+
if (!value || typeof value !== "object") return value;
|
|
18
|
+
return Object.fromEntries(
|
|
19
|
+
Object.entries(value).map(([key, entry]) => [
|
|
20
|
+
key,
|
|
21
|
+
secretKey.test(key) ? "[redacted]" : redact(entry),
|
|
22
|
+
]),
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default defineAction({
|
|
27
|
+
description:
|
|
28
|
+
"Test an Analytics custom API connection with a bounded authenticated GET and return a safe row preview.",
|
|
29
|
+
schema: Input,
|
|
30
|
+
agentTool: false,
|
|
31
|
+
http: { method: "POST" },
|
|
32
|
+
readOnly: true,
|
|
33
|
+
run: async ({ provider, path, query, itemsPath }) => {
|
|
34
|
+
try {
|
|
35
|
+
const result = (await getAnalyticsProviderApiRuntime().executeRequest({
|
|
36
|
+
provider,
|
|
37
|
+
method: "GET",
|
|
38
|
+
path,
|
|
39
|
+
...(query ? { query } : {}),
|
|
40
|
+
auth: "default",
|
|
41
|
+
maxBytes: 1_000_000,
|
|
42
|
+
timeoutMs: 10_000,
|
|
43
|
+
})) as {
|
|
44
|
+
response?: {
|
|
45
|
+
ok?: boolean;
|
|
46
|
+
status?: number;
|
|
47
|
+
statusText?: string;
|
|
48
|
+
json?: unknown;
|
|
49
|
+
truncated?: boolean;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
const response = result.response;
|
|
53
|
+
const status = response?.status ?? 0;
|
|
54
|
+
const statusText = response?.statusText ?? "";
|
|
55
|
+
if (!response?.ok) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
status,
|
|
59
|
+
statusText,
|
|
60
|
+
rowCount: 0,
|
|
61
|
+
columns: [],
|
|
62
|
+
sampleRows: [],
|
|
63
|
+
itemsPath: itemsPath ?? null,
|
|
64
|
+
truncated: false,
|
|
65
|
+
error: `Custom API request failed with HTTP ${status}${statusText ? ` ${statusText}` : ""}.`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const rows = extractItemsArray(response.json, itemsPath ?? "auto");
|
|
69
|
+
const sampleRows = rows.slice(0, 5).map((row) => redact(row));
|
|
70
|
+
const columns = Array.from(
|
|
71
|
+
new Set(
|
|
72
|
+
rows.flatMap((row) =>
|
|
73
|
+
row && typeof row === "object" && !Array.isArray(row)
|
|
74
|
+
? Object.keys(row)
|
|
75
|
+
: [],
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
return {
|
|
80
|
+
ok: true,
|
|
81
|
+
status,
|
|
82
|
+
statusText,
|
|
83
|
+
rowCount: rows.length,
|
|
84
|
+
columns,
|
|
85
|
+
sampleRows,
|
|
86
|
+
itemsPath: itemsPath ?? "auto",
|
|
87
|
+
truncated: Boolean(response.truncated),
|
|
88
|
+
};
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
status: 0,
|
|
93
|
+
statusText: "",
|
|
94
|
+
rowCount: 0,
|
|
95
|
+
columns: [],
|
|
96
|
+
sampleRows: [],
|
|
97
|
+
itemsPath: itemsPath ?? null,
|
|
98
|
+
truncated: false,
|
|
99
|
+
error:
|
|
100
|
+
error instanceof Error
|
|
101
|
+
? `Custom API connection failed: ${error.message}`
|
|
102
|
+
: "Custom API connection failed.",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
});
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { useSendToAgentChat } from "@agent-native/core/client/agent-chat";
|
|
2
|
+
import { useActionMutation } from "@agent-native/core/client/hooks";
|
|
3
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
4
|
+
import { useOrgRole } from "@agent-native/core/client/org";
|
|
5
|
+
import {
|
|
6
|
+
IconChevronDown,
|
|
7
|
+
IconChevronUp,
|
|
8
|
+
IconExternalLink,
|
|
9
|
+
} from "@tabler/icons-react";
|
|
10
|
+
import { useMemo, useState } from "react";
|
|
11
|
+
|
|
12
|
+
import { Badge } from "@/components/ui/badge";
|
|
13
|
+
import { Button } from "@/components/ui/button";
|
|
14
|
+
import {
|
|
15
|
+
Card,
|
|
16
|
+
CardContent,
|
|
17
|
+
CardDescription,
|
|
18
|
+
CardHeader,
|
|
19
|
+
CardTitle,
|
|
20
|
+
} from "@/components/ui/card";
|
|
21
|
+
import { Input } from "@/components/ui/input";
|
|
22
|
+
import { Label } from "@/components/ui/label";
|
|
23
|
+
import {
|
|
24
|
+
Select,
|
|
25
|
+
SelectContent,
|
|
26
|
+
SelectItem,
|
|
27
|
+
SelectTrigger,
|
|
28
|
+
SelectValue,
|
|
29
|
+
} from "@/components/ui/select";
|
|
30
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
31
|
+
|
|
32
|
+
type AuthType = "none" | "bearer" | "basic" | "api-key-header";
|
|
33
|
+
type TestResult = {
|
|
34
|
+
ok?: boolean;
|
|
35
|
+
status?: number;
|
|
36
|
+
error?: string;
|
|
37
|
+
rowCount?: number;
|
|
38
|
+
columns?: string[];
|
|
39
|
+
sampleRows?: unknown[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type ActionMutationName = Parameters<typeof useActionMutation>[0];
|
|
43
|
+
|
|
44
|
+
const mutationName = (name: string): ActionMutationName => name;
|
|
45
|
+
|
|
46
|
+
export function CustomApiCard() {
|
|
47
|
+
const t = useT();
|
|
48
|
+
const { canManageOrg, org } = useOrgRole();
|
|
49
|
+
const scope = org?.orgId && canManageOrg ? "org" : "user";
|
|
50
|
+
const { send, isGenerating, codeRequiredDialog } = useSendToAgentChat();
|
|
51
|
+
const register = useActionMutation(mutationName("provider-api-register"));
|
|
52
|
+
const test = useActionMutation(mutationName("test-custom-api-connection"));
|
|
53
|
+
const [open, setOpen] = useState(false);
|
|
54
|
+
const [registered, setRegistered] = useState(false);
|
|
55
|
+
const [registeredProviderId, setRegisteredProviderId] = useState<
|
|
56
|
+
string | null
|
|
57
|
+
>(null);
|
|
58
|
+
const [result, setResult] = useState<TestResult | null>(null);
|
|
59
|
+
const [error, setError] = useState<string | null>(null);
|
|
60
|
+
const [authType, setAuthType] = useState<AuthType>("none");
|
|
61
|
+
const [values, setValues] = useState({
|
|
62
|
+
label: "",
|
|
63
|
+
baseUrl: "",
|
|
64
|
+
path: "",
|
|
65
|
+
query: "",
|
|
66
|
+
itemsPath: "",
|
|
67
|
+
credentialKey: "",
|
|
68
|
+
usernameKey: "",
|
|
69
|
+
passwordKey: "",
|
|
70
|
+
headerName: "",
|
|
71
|
+
docsUrl: "",
|
|
72
|
+
});
|
|
73
|
+
function resetConnectionState() {
|
|
74
|
+
setRegistered(false);
|
|
75
|
+
setRegisteredProviderId(null);
|
|
76
|
+
setResult(null);
|
|
77
|
+
}
|
|
78
|
+
const set =
|
|
79
|
+
(key: keyof typeof values) =>
|
|
80
|
+
(event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
81
|
+
setValues((current) => ({ ...current, [key]: event.target.value }));
|
|
82
|
+
setError(null);
|
|
83
|
+
if (key === "path" || key === "query" || key === "itemsPath") {
|
|
84
|
+
setResult(null);
|
|
85
|
+
} else {
|
|
86
|
+
resetConnectionState();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const parsedQuery = useMemo(() => {
|
|
90
|
+
if (!values.query.trim()) return undefined;
|
|
91
|
+
try {
|
|
92
|
+
const value: unknown = JSON.parse(values.query);
|
|
93
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return value as Record<string, unknown>;
|
|
97
|
+
} catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}, [values.query]);
|
|
101
|
+
|
|
102
|
+
function providerId(label: string): string {
|
|
103
|
+
return (
|
|
104
|
+
label
|
|
105
|
+
.trim()
|
|
106
|
+
.toLowerCase()
|
|
107
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
108
|
+
.replace(/^-+|-+$/g, "")
|
|
109
|
+
.slice(0, 64) || "custom-api"
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function auth() {
|
|
114
|
+
if (authType === "none") return { type: "none" as const };
|
|
115
|
+
if (authType === "basic")
|
|
116
|
+
return {
|
|
117
|
+
type: "basic" as const,
|
|
118
|
+
usernameKey: values.usernameKey.trim(),
|
|
119
|
+
passwordKey: values.passwordKey.trim(),
|
|
120
|
+
};
|
|
121
|
+
if (authType === "api-key-header")
|
|
122
|
+
return {
|
|
123
|
+
type: "api-key-header" as const,
|
|
124
|
+
credentialKey: values.credentialKey.trim(),
|
|
125
|
+
headerName: values.headerName.trim(),
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
type: "bearer" as const,
|
|
129
|
+
credentialKey: values.credentialKey.trim(),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function handleRegister() {
|
|
134
|
+
setError(null);
|
|
135
|
+
const id = providerId(values.label);
|
|
136
|
+
try {
|
|
137
|
+
await register.mutateAsync({
|
|
138
|
+
operation: "upsert",
|
|
139
|
+
scope,
|
|
140
|
+
id,
|
|
141
|
+
label: values.label.trim(),
|
|
142
|
+
baseUrl: values.baseUrl.trim(),
|
|
143
|
+
auth: auth(),
|
|
144
|
+
docsUrls: values.docsUrl.trim() ? [values.docsUrl.trim()] : undefined,
|
|
145
|
+
});
|
|
146
|
+
setRegistered(true);
|
|
147
|
+
setRegisteredProviderId(id);
|
|
148
|
+
setResult(null);
|
|
149
|
+
} catch (cause) {
|
|
150
|
+
setError(
|
|
151
|
+
cause instanceof Error
|
|
152
|
+
? cause.message
|
|
153
|
+
: t("dataSources.customApi.registerError"),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function handleTest() {
|
|
159
|
+
setError(null);
|
|
160
|
+
if (parsedQuery === null) {
|
|
161
|
+
setError(t("dataSources.customApi.invalidQuery"));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const provider = registeredProviderId ?? providerId(values.label);
|
|
165
|
+
try {
|
|
166
|
+
const response = await test.mutateAsync({
|
|
167
|
+
provider,
|
|
168
|
+
path: values.path.trim(),
|
|
169
|
+
query: parsedQuery,
|
|
170
|
+
itemsPath: values.itemsPath.trim() || undefined,
|
|
171
|
+
});
|
|
172
|
+
const nextResult = response as TestResult;
|
|
173
|
+
setResult(nextResult);
|
|
174
|
+
if (!nextResult.ok) {
|
|
175
|
+
setError(nextResult.error || t("dataSources.customApi.testError"));
|
|
176
|
+
}
|
|
177
|
+
} catch (cause) {
|
|
178
|
+
setError(
|
|
179
|
+
cause instanceof Error
|
|
180
|
+
? cause.message
|
|
181
|
+
: t("dataSources.customApi.testError"),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function saveAsProgram() {
|
|
187
|
+
const details = {
|
|
188
|
+
provider: registeredProviderId ?? providerId(values.label),
|
|
189
|
+
path: values.path.trim(),
|
|
190
|
+
itemsPath: values.itemsPath.trim() || undefined,
|
|
191
|
+
query: parsedQuery,
|
|
192
|
+
};
|
|
193
|
+
send({
|
|
194
|
+
submit: true,
|
|
195
|
+
openSidebar: true,
|
|
196
|
+
message: `${t("dataSources.customApi.agentPrompt")}\n\n${JSON.stringify(details)}`,
|
|
197
|
+
context:
|
|
198
|
+
"The endpoint above passed a bounded read-only GET test. Save it as a manual-refresh Analytics Data Program using providerFetch and emit(rows, schema). Do not ask for or include raw credential values; the provider registration already references the user's scoped secret key.",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const needsKey = authType !== "none";
|
|
203
|
+
const credentialLink =
|
|
204
|
+
values.credentialKey.trim() ||
|
|
205
|
+
(authType === "basic" ? values.usernameKey.trim() : "");
|
|
206
|
+
return (
|
|
207
|
+
<Card>
|
|
208
|
+
<CardHeader className="p-0">
|
|
209
|
+
<button
|
|
210
|
+
type="button"
|
|
211
|
+
className="flex w-full items-center justify-between gap-3 p-6 text-left"
|
|
212
|
+
aria-expanded={open}
|
|
213
|
+
onClick={() => setOpen((current) => !current)}
|
|
214
|
+
>
|
|
215
|
+
<div>
|
|
216
|
+
<CardTitle>{t("dataSources.customApi.title")}</CardTitle>
|
|
217
|
+
<CardDescription>
|
|
218
|
+
{t("dataSources.customApi.description")}
|
|
219
|
+
</CardDescription>
|
|
220
|
+
</div>
|
|
221
|
+
{open ? (
|
|
222
|
+
<IconChevronUp aria-hidden="true" />
|
|
223
|
+
) : (
|
|
224
|
+
<IconChevronDown aria-hidden="true" />
|
|
225
|
+
)}
|
|
226
|
+
</button>
|
|
227
|
+
</CardHeader>
|
|
228
|
+
{open && (
|
|
229
|
+
<CardContent className="space-y-4">
|
|
230
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
231
|
+
{(["label", "baseUrl", "path"] as const).map((key) => (
|
|
232
|
+
<div className="space-y-2" key={key}>
|
|
233
|
+
<Label htmlFor={`custom-api-${key}`}>
|
|
234
|
+
{t(`dataSources.customApi.fields.${key}`)}
|
|
235
|
+
</Label>
|
|
236
|
+
<Input
|
|
237
|
+
id={`custom-api-${key}`}
|
|
238
|
+
value={values[key]}
|
|
239
|
+
onChange={set(key)}
|
|
240
|
+
placeholder={t(`dataSources.customApi.placeholders.${key}`)}
|
|
241
|
+
type={key === "baseUrl" ? "url" : "text"}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
))}
|
|
245
|
+
</div>
|
|
246
|
+
<div className="space-y-2">
|
|
247
|
+
<Label htmlFor="custom-api-query">
|
|
248
|
+
{t("dataSources.customApi.fields.query")}
|
|
249
|
+
</Label>
|
|
250
|
+
<Textarea
|
|
251
|
+
id="custom-api-query"
|
|
252
|
+
value={values.query}
|
|
253
|
+
onChange={set("query")}
|
|
254
|
+
placeholder={t("dataSources.customApi.placeholders.query")}
|
|
255
|
+
rows={3}
|
|
256
|
+
/>
|
|
257
|
+
</div>
|
|
258
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
259
|
+
<div className="space-y-2">
|
|
260
|
+
<Label htmlFor="custom-api-items-path">
|
|
261
|
+
{t("dataSources.customApi.fields.itemsPath")}
|
|
262
|
+
</Label>
|
|
263
|
+
<Input
|
|
264
|
+
id="custom-api-items-path"
|
|
265
|
+
value={values.itemsPath}
|
|
266
|
+
onChange={set("itemsPath")}
|
|
267
|
+
placeholder={t("dataSources.customApi.placeholders.itemsPath")}
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
<div className="space-y-2">
|
|
271
|
+
<Label htmlFor="custom-api-docs">
|
|
272
|
+
{t("dataSources.customApi.fields.docsUrl")}
|
|
273
|
+
</Label>
|
|
274
|
+
<Input
|
|
275
|
+
id="custom-api-docs"
|
|
276
|
+
type="url"
|
|
277
|
+
value={values.docsUrl}
|
|
278
|
+
onChange={set("docsUrl")}
|
|
279
|
+
placeholder={t("dataSources.customApi.placeholders.docsUrl")}
|
|
280
|
+
/>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
<div className="space-y-2">
|
|
284
|
+
<Label htmlFor="custom-api-auth">
|
|
285
|
+
{t("dataSources.customApi.fields.authType")}
|
|
286
|
+
</Label>
|
|
287
|
+
<Select
|
|
288
|
+
value={authType}
|
|
289
|
+
onValueChange={(value) => {
|
|
290
|
+
setAuthType(value as AuthType);
|
|
291
|
+
setError(null);
|
|
292
|
+
resetConnectionState();
|
|
293
|
+
}}
|
|
294
|
+
>
|
|
295
|
+
<SelectTrigger id="custom-api-auth">
|
|
296
|
+
<SelectValue />
|
|
297
|
+
</SelectTrigger>
|
|
298
|
+
<SelectContent>
|
|
299
|
+
<SelectItem value="none">
|
|
300
|
+
{t("dataSources.customApi.authNone")}
|
|
301
|
+
</SelectItem>
|
|
302
|
+
<SelectItem value="bearer">
|
|
303
|
+
{t("dataSources.customApi.authBearer")}
|
|
304
|
+
</SelectItem>
|
|
305
|
+
<SelectItem value="basic">
|
|
306
|
+
{t("dataSources.customApi.authBasic")}
|
|
307
|
+
</SelectItem>
|
|
308
|
+
<SelectItem value="api-key-header">
|
|
309
|
+
{t("dataSources.customApi.authApiKey")}
|
|
310
|
+
</SelectItem>
|
|
311
|
+
</SelectContent>
|
|
312
|
+
</Select>
|
|
313
|
+
</div>
|
|
314
|
+
{needsKey && (
|
|
315
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
316
|
+
{authType === "basic" ? (
|
|
317
|
+
<>
|
|
318
|
+
<div className="space-y-2">
|
|
319
|
+
<Label htmlFor="custom-api-username-key">
|
|
320
|
+
{t("dataSources.customApi.fields.usernameKey")}
|
|
321
|
+
</Label>
|
|
322
|
+
<Input
|
|
323
|
+
id="custom-api-username-key"
|
|
324
|
+
value={values.usernameKey}
|
|
325
|
+
onChange={set("usernameKey")}
|
|
326
|
+
/>
|
|
327
|
+
</div>
|
|
328
|
+
<div className="space-y-2">
|
|
329
|
+
<Label htmlFor="custom-api-password-key">
|
|
330
|
+
{t("dataSources.customApi.fields.passwordKey")}
|
|
331
|
+
</Label>
|
|
332
|
+
<Input
|
|
333
|
+
id="custom-api-password-key"
|
|
334
|
+
value={values.passwordKey}
|
|
335
|
+
onChange={set("passwordKey")}
|
|
336
|
+
/>
|
|
337
|
+
</div>
|
|
338
|
+
</>
|
|
339
|
+
) : (
|
|
340
|
+
<div className="space-y-2">
|
|
341
|
+
<Label htmlFor="custom-api-credential-key">
|
|
342
|
+
{t("dataSources.customApi.fields.credentialKey")}
|
|
343
|
+
</Label>
|
|
344
|
+
<Input
|
|
345
|
+
id="custom-api-credential-key"
|
|
346
|
+
value={values.credentialKey}
|
|
347
|
+
onChange={set("credentialKey")}
|
|
348
|
+
/>
|
|
349
|
+
</div>
|
|
350
|
+
)}
|
|
351
|
+
{authType === "api-key-header" && (
|
|
352
|
+
<div className="space-y-2">
|
|
353
|
+
<Label htmlFor="custom-api-header-name">
|
|
354
|
+
{t("dataSources.customApi.fields.headerName")}
|
|
355
|
+
</Label>
|
|
356
|
+
<Input
|
|
357
|
+
id="custom-api-header-name"
|
|
358
|
+
value={values.headerName}
|
|
359
|
+
onChange={set("headerName")}
|
|
360
|
+
/>
|
|
361
|
+
</div>
|
|
362
|
+
)}
|
|
363
|
+
</div>
|
|
364
|
+
)}
|
|
365
|
+
{needsKey && credentialLink && (
|
|
366
|
+
<p className="text-sm text-muted-foreground">
|
|
367
|
+
{t("dataSources.customApi.keyHint")}{" "}
|
|
368
|
+
<a
|
|
369
|
+
className="inline-flex items-center gap-1 underline"
|
|
370
|
+
href={`/settings/integrations/secrets/${encodeURIComponent(credentialLink)}`}
|
|
371
|
+
>
|
|
372
|
+
{credentialLink}
|
|
373
|
+
<IconExternalLink className="h-3.5 w-3.5" aria-hidden="true" />
|
|
374
|
+
</a>
|
|
375
|
+
</p>
|
|
376
|
+
)}
|
|
377
|
+
{error && (
|
|
378
|
+
<p role="alert" className="text-sm text-destructive">
|
|
379
|
+
{error}
|
|
380
|
+
</p>
|
|
381
|
+
)}
|
|
382
|
+
<div className="flex flex-wrap gap-2">
|
|
383
|
+
<Button
|
|
384
|
+
onClick={handleRegister}
|
|
385
|
+
disabled={register.isPending || !values.label || !values.baseUrl}
|
|
386
|
+
>
|
|
387
|
+
{t("dataSources.customApi.register")}
|
|
388
|
+
</Button>
|
|
389
|
+
<Button
|
|
390
|
+
variant="outline"
|
|
391
|
+
onClick={handleTest}
|
|
392
|
+
disabled={!registered || test.isPending || !values.path}
|
|
393
|
+
>
|
|
394
|
+
{t("dataSources.customApi.test")}
|
|
395
|
+
</Button>
|
|
396
|
+
</div>
|
|
397
|
+
{result && (
|
|
398
|
+
<div className="space-y-2">
|
|
399
|
+
<div className="flex flex-wrap gap-2">
|
|
400
|
+
<Badge>
|
|
401
|
+
{t("dataSources.customApi.connectionResult")}:{" "}
|
|
402
|
+
{result.status ?? t("dataSources.customApi.unknown")}
|
|
403
|
+
</Badge>
|
|
404
|
+
<Badge>
|
|
405
|
+
{t("dataSources.customApi.rowCount")}:{" "}
|
|
406
|
+
{result.rowCount ?? result.sampleRows?.length ?? 0}
|
|
407
|
+
</Badge>
|
|
408
|
+
</div>
|
|
409
|
+
{result.columns?.length ? (
|
|
410
|
+
<p className="text-sm text-muted-foreground">
|
|
411
|
+
{t("dataSources.customApi.columns")}:{" "}
|
|
412
|
+
{result.columns.join(", ")}
|
|
413
|
+
</p>
|
|
414
|
+
) : null}
|
|
415
|
+
<pre className="max-h-56 overflow-auto rounded-md bg-muted p-3 text-xs">
|
|
416
|
+
{JSON.stringify((result.sampleRows ?? []).slice(0, 5), null, 2)}
|
|
417
|
+
</pre>
|
|
418
|
+
{result.ok && (
|
|
419
|
+
<Button onClick={saveAsProgram} disabled={isGenerating}>
|
|
420
|
+
{t("dataSources.customApi.handoffButton")}
|
|
421
|
+
</Button>
|
|
422
|
+
)}
|
|
423
|
+
</div>
|
|
424
|
+
)}
|
|
425
|
+
{codeRequiredDialog}
|
|
426
|
+
</CardContent>
|
|
427
|
+
)}
|
|
428
|
+
</Card>
|
|
429
|
+
);
|
|
430
|
+
}
|