@agent-native/core 0.141.5 → 0.141.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +3 -14
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +20 -8
- package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +5 -5
- package/corpus/templates/analytics/AGENTS.md +2 -2
- package/corpus/templates/analytics/actions/compose-dashboard.ts +1 -1
- package/corpus/templates/analytics/actions/get-first-party-analytics-health.ts +15 -1
- package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +135 -0
- package/corpus/templates/analytics/actions/navigate.ts +1 -1
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -2
- package/corpus/templates/analytics/actions/update-dashboard.ts +0 -1
- package/corpus/templates/analytics/actions/view-screen.ts +0 -20
- package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +5 -5
- package/corpus/templates/analytics/app/i18n-data.ts +330 -90
- package/corpus/templates/analytics/app/pages/Agents.tsx +0 -3
- package/corpus/templates/analytics/app/pages/DataSources.tsx +66 -30
- package/corpus/templates/analytics/app/pages/Settings.tsx +22 -43
- package/corpus/templates/analytics/app/pages/settings/settings-search.ts +23 -20
- package/corpus/templates/analytics/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/analytics/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/analytics/app/routes/team.tsx +1 -1
- package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-now-repair-automatically-after-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-05-settings-no-longer-includes-an-unnecessary-about-section.md +6 -0
- package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +1 -1
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +109 -0
- package/corpus/templates/analytics/scripts/first-party-analytics-bigquery.sql +104 -0
- package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +342 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -2
- package/corpus/templates/analytics/server/lib/analytics-rollup-lock.ts +8 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +11 -29
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +55 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +1058 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +151 -46
- package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +15 -9
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +59 -17
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -3
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -4
- package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +61 -0
- package/corpus/templates/analytics/server/plugins/db.ts +58 -187
- package/corpus/templates/analytics/server/routes/api/analytics-rollups/backfill.post.ts +53 -0
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +8 -6
- package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +16 -1
- package/corpus/templates/assets/AGENTS.md +1 -0
- package/corpus/templates/assets/actions/_helpers.ts +12 -0
- package/corpus/templates/assets/actions/generate-image.ts +9 -3
- package/corpus/templates/assets/actions/import-style-from-url.ts +115 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
- package/corpus/templates/assets/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/assets/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/assets/app/routes/settings.tsx +5 -0
- package/corpus/templates/assets/app/routes/team.tsx +1 -1
- package/corpus/templates/assets/changelog/2026-08-05-brand-kits-can-import-a-website-s-hydrated-visual-language-a.md +6 -0
- package/corpus/templates/assets/changelog/2026-08-05-generated-images-now-hand-off-embeddable-previews-to-other-w.md +6 -0
- package/corpus/templates/assets/server/lib/generation.ts +58 -0
- package/corpus/templates/assets/shared/api.ts +12 -0
- package/corpus/templates/brain/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/brain/app/routes/team.tsx +1 -1
- package/corpus/templates/brain/app/routes.ts +1 -1
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +3 -1
- package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +1 -1
- package/corpus/templates/calendar/app/routes/_app.settings.$.tsx +1 -0
- package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
- package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
- package/corpus/templates/chat/app/root.tsx +1 -1
- package/corpus/templates/chat/app/routes/agent.tsx +7 -7
- package/corpus/templates/chat/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/chat/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/chat/app/routes/team.tsx +1 -1
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +1 -1
- package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +1 -1
- package/corpus/templates/clips/app/routes/_app.settings.$.tsx +1 -0
- package/corpus/templates/clips/changelog/2026-08-05-clips-overlays-now-use-clean-surfaces-without-clipped-shadow.md +6 -0
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +2 -2
- package/corpus/templates/clips/desktop/src/styles.css +3 -54
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -3
- package/corpus/templates/content/app/routes/_app.extensions._index.tsx +1 -1
- package/corpus/templates/content/app/routes/_app.settings.$.tsx +1 -0
- package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
- package/corpus/templates/content/e2e/global-setup.ts +1 -1
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -1
- package/corpus/templates/design/actions/analyze-brand-assets.ts +7 -7
- package/corpus/templates/design/actions/get-design-system.ts +3 -1
- package/corpus/templates/design/actions/import-from-url.ts +6 -5
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +2 -2
- package/corpus/templates/design/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/design/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/design/app/routes/team.tsx +1 -1
- package/corpus/templates/design/changelog/2026-08-05-missing-design-systems-now-return-a-clear-not-found-response.md +6 -0
- package/corpus/templates/design/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-de.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +1 -1
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
- package/corpus/templates/dispatch/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +1 -1
- package/corpus/templates/factory/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/factory/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/factory/app/routes/team.tsx +1 -1
- package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +1 -1
- package/corpus/templates/forms/app/routes/_app.settings.$.tsx +1 -0
- package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
- package/corpus/templates/forms/shared/navigation.ts +1 -1
- package/corpus/templates/macros/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/macros/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/macros/app/routes/team.tsx +1 -1
- package/corpus/templates/mail/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/mail/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
- package/corpus/templates/plan/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/plan/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/plan/app/routes/team.tsx +1 -1
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +3 -1
- package/corpus/templates/slides/actions/analyze-brand-assets.ts +7 -7
- package/corpus/templates/slides/actions/import-from-url.ts +6 -6
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -2
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +3 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +9 -3
- package/corpus/templates/slides/app/context/DeckContext.tsx +71 -20
- package/corpus/templates/slides/app/global.css +6 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +5 -5
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +9 -5
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +17 -0
- package/corpus/templates/slides/app/pages/Index.tsx +16 -5
- package/corpus/templates/slides/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/slides/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/slides/app/routes/team.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-08-05-failed-deck-creation-and-reference-uploads-now-recover-clean.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-05-new-presentation-prompts-now-clearly-describe-presentation-g.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-05-slides-recover-completed-generation-results-after-a-sync-hic.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-st.md +6 -0
- package/corpus/templates/tasks/app/components/layout/Header.tsx +3 -1
- package/corpus/templates/tasks/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/tasks/app/routes/settings.$.tsx +1 -0
- package/dist/agent/engine/ai-sdk-engine.js +49 -3
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.js +8 -0
- package/dist/agent/types.d.ts +7 -0
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/agent-chat-adapter.d.ts +1 -1
- package/dist/client/agent-chat-adapter.js +30 -3
- package/dist/client/agent-page/AgentWorkspaceContent.d.ts +1 -1
- package/dist/client/agent-page/AgentWorkspaceContent.js +13 -13
- package/dist/client/chat/run-recovery.d.ts +2 -1
- package/dist/client/chat/run-recovery.js +14 -3
- package/dist/client/embed-auth.js +6 -2
- package/dist/client/error-format.d.ts +1 -0
- package/dist/client/error-format.js +16 -2
- package/dist/client/integrations/IntegrationsPanel.js +40 -6
- package/dist/client/org/OrgSwitcher.js +2 -2
- package/dist/client/org/TeamPage.js +92 -72
- package/dist/client/require-session.d.ts +1 -1
- package/dist/client/require-session.js +3 -3
- package/dist/client/resources/use-mcp-servers.d.ts +5 -0
- package/dist/client/resources/use-mcp-servers.js +36 -0
- package/dist/client/settings/AccountSettingsCard.js +40 -35
- package/dist/client/settings/AutomationsSection.js +1 -1
- package/dist/client/settings/DemoModeSection.d.ts +3 -1
- package/dist/client/settings/DemoModeSection.js +4 -1
- package/dist/client/settings/ExtensionsSettingsContent.js +1 -1
- package/dist/client/settings/SchedulingTimezoneField.d.ts +3 -1
- package/dist/client/settings/SchedulingTimezoneField.js +9 -5
- package/dist/client/settings/SettingsPanel.d.ts +4 -1
- package/dist/client/settings/SettingsPanel.js +21 -21
- package/dist/client/settings/SettingsTabsPage.d.ts +2 -2
- package/dist/client/settings/SettingsTabsPage.js +98 -45
- package/dist/client/settings/VoiceTranscriptionSection.js +6 -1
- package/dist/client/settings/useSettingsPanelController.js +47 -8
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/ingestion/website.d.ts +30 -0
- package/dist/localization/default-messages.d.ts +5 -0
- package/dist/localization/default-messages.js +5 -0
- package/dist/mcp-client/oauth-routes.js +1 -1
- package/dist/mcp-client/routes.d.ts +1 -0
- package/dist/mcp-client/routes.js +49 -0
- package/dist/navigation/index.js +16 -5
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/scripts/call-agent.js +11 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +9 -7
- package/dist/server/identity-sso.js +2 -1
- package/dist/server/onboarding-html.js +57 -34
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/workspace-provider-oauth.js +1 -1
- package/dist/setup-connections/onboarding.js +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/sign-in-journey.d.ts +5 -2
- package/dist/shared/sign-in-journey.js +10 -4
- package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
- package/dist/templates/chat/app/hooks/use-navigation-state.ts +1 -1
- package/dist/templates/chat/app/root.tsx +1 -1
- package/dist/templates/chat/app/routes/agent.tsx +7 -7
- package/dist/templates/chat/app/routes/extensions._index.tsx +1 -1
- package/dist/templates/chat/app/routes/settings.$.tsx +1 -0
- package/dist/templates/chat/app/routes/team.tsx +1 -1
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
- package/docs/content/authentication.mdx +4 -5
- package/docs/content/template-analytics-developers.mdx +1 -1
- package/docs/content/template-calendar-developers.mdx +1 -1
- package/docs/content/template-chat-developers.mdx +2 -2
- package/docs/content/template-content-developers.mdx +1 -1
- package/docs/content/template-mail-developers.mdx +1 -1
- package/docs/content/template-plan-developers.mdx +1 -1
- package/docs/content/toolkit-command-navigation.mdx +6 -6
- package/docs/content/toolkit-org-team.mdx +1 -1
- package/docs/content/toolkit-settings.mdx +8 -8
- package/docs/content/toolkit-setup-connections.mdx +1 -1
- package/package.json +1 -1
- package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
- package/src/templates/chat/app/hooks/use-navigation-state.ts +1 -1
- package/src/templates/chat/app/root.tsx +1 -1
- package/src/templates/chat/app/routes/agent.tsx +7 -7
- package/src/templates/chat/app/routes/extensions._index.tsx +1 -1
- package/src/templates/chat/app/routes/settings.$.tsx +1 -0
- package/src/templates/chat/app/routes/team.tsx +1 -1
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
- package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +0 -62
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +0 -369
- package/corpus/templates/analytics/actions/list-dashboard-templates.ts +0 -47
- package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +0 -34
- package/corpus/templates/analytics/app/routes/catalog.tsx +0 -240
- package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-stay-complete-and-atomic.md +0 -6
- package/corpus/templates/analytics/changelog/2026-08-05-historical-rollups-stay-complete-while-live-events-arrive.md +0 -6
- package/src/templates/default/actions/navigate.spec.ts +0 -53
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { IconPlus, IconBrandSlack, IconBrandTelegram, IconBrandWhatsapp, IconBrandGoogleDrive, IconTerminal2, IconBuildingSkyscraper, IconCopy, IconCheck, IconChevronLeft, IconChevronRight, IconExternalLink, IconCircleCheck, IconInfoCircle, IconSearch, IconServer, } from "@tabler/icons-react";
|
|
2
|
+
import { IconPlus, IconBrandSlack, IconBrandTelegram, IconBrandWhatsapp, IconBrandGoogleDrive, IconTerminal2, IconBuildingSkyscraper, IconCopy, IconCheck, IconChevronLeft, IconChevronRight, IconExternalLink, IconCircleCheck, IconInfoCircle, IconSearch, IconServer, IconLoader2, IconRefresh, } from "@tabler/icons-react";
|
|
3
3
|
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
4
4
|
import { agentNativePath } from "../api-path.js";
|
|
5
5
|
import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
6
6
|
import { useT } from "../i18n.js";
|
|
7
7
|
import { getDefaultMcpIntegrations, } from "../resources/mcp-integration-catalog.js";
|
|
8
8
|
import { McpIntegrationDialog } from "../resources/McpIntegrationDialog.js";
|
|
9
|
-
import { useCreateMcpServer, useDeleteMcpServer, useMcpServers, } from "../resources/use-mcp-servers.js";
|
|
9
|
+
import { useCreateMcpServer, useDeleteMcpServer, useMcpServers, useReconnectMcpServer, } from "../resources/use-mcp-servers.js";
|
|
10
10
|
import { useIntegrationStatus, } from "./useIntegrationStatus.js";
|
|
11
11
|
import { isNonPublicWebhookUrl } from "./webhook-url.js";
|
|
12
12
|
const PLATFORMS = [
|
|
@@ -257,12 +257,19 @@ function McpLogo({ integration }) {
|
|
|
257
257
|
const [failed, setFailed] = useState(false);
|
|
258
258
|
return (_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border/70 bg-background text-xs font-semibold text-muted-foreground", children: failed ? (integration.name.slice(0, 1)) : (_jsx("img", { src: integration.logoUrl, alt: "", loading: "lazy", decoding: "async", className: "size-7 object-contain", onError: () => setFailed(true) })) }));
|
|
259
259
|
}
|
|
260
|
-
function McpServerStatus({ server }) {
|
|
260
|
+
function McpServerStatus({ server, onReconnect, reconnecting = false, reconnectError, }) {
|
|
261
|
+
const t = useT();
|
|
261
262
|
if (server.status.state === "connected") {
|
|
262
263
|
return (_jsxs("span", { className: "inline-flex items-center gap-1.5 text-xs text-emerald-600 dark:text-emerald-400", children: [_jsx("span", { className: "size-1.5 rounded-full bg-emerald-500" }), "Connected \u00B7 ", server.status.toolCount, " tool", server.status.toolCount === 1 ? "" : "s"] }));
|
|
263
264
|
}
|
|
264
265
|
if (server.status.state === "error") {
|
|
265
|
-
return (_jsxs("
|
|
266
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-start sm:justify-between", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "inline-flex items-center gap-1.5 text-xs text-destructive", children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), t("mcpIntegrations.connectionError")] }), _jsx("p", { className: "mt-1 max-w-3xl break-words text-xs leading-5 text-destructive/85", children: t("mcpIntegrations.connectionErrorReason", {
|
|
267
|
+
reason: server.status.error,
|
|
268
|
+
}) }), reconnectError && (_jsx("p", { className: "mt-1 break-words text-xs leading-5 text-destructive", children: t("mcpIntegrations.reconnectFailed", {
|
|
269
|
+
error: reconnectError,
|
|
270
|
+
}) }))] }), onReconnect && (_jsxs("button", { type: "button", onClick: onReconnect, disabled: reconnecting, className: "inline-flex shrink-0 items-center gap-1.5 rounded-md border border-border bg-background px-2.5 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent disabled:cursor-wait disabled:opacity-60", children: [reconnecting ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : (_jsx(IconRefresh, { className: "size-3.5" })), reconnecting
|
|
271
|
+
? t("mcpIntegrations.reconnecting")
|
|
272
|
+
: t("mcpIntegrations.reconnect")] }))] }));
|
|
266
273
|
}
|
|
267
274
|
return (_jsxs("span", { className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground", children: [_jsx("span", { className: "size-1.5 rounded-full bg-muted-foreground/50" }), "Status unknown"] }));
|
|
268
275
|
}
|
|
@@ -273,10 +280,13 @@ function McpIntegrationsSection({ query }) {
|
|
|
273
280
|
const serversQuery = useMcpServers();
|
|
274
281
|
const createServer = useCreateMcpServer();
|
|
275
282
|
const deleteServer = useDeleteMcpServer();
|
|
283
|
+
const reconnectServer = useReconnectMcpServer();
|
|
276
284
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
277
285
|
const [initialIntegrationId, setInitialIntegrationId] = useState(null);
|
|
278
286
|
const [deleteTarget, setDeleteTarget] = useState(null);
|
|
279
287
|
const [deleteError, setDeleteError] = useState(null);
|
|
288
|
+
const [reconnectingKey, setReconnectingKey] = useState(null);
|
|
289
|
+
const [reconnectError, setReconnectError] = useState(null);
|
|
280
290
|
const catalog = useMemo(() => getDefaultMcpIntegrations(), []);
|
|
281
291
|
const normalizedQuery = query.trim().toLowerCase();
|
|
282
292
|
const filteredCatalog = useMemo(() => {
|
|
@@ -318,14 +328,38 @@ function McpIntegrationsSection({ query }) {
|
|
|
318
328
|
: "Could not remove agent integration.");
|
|
319
329
|
}
|
|
320
330
|
}, [deleteServer, deleteTarget]);
|
|
331
|
+
const reconnect = useCallback(async (server) => {
|
|
332
|
+
const key = `${server.scope}:${server.id}`;
|
|
333
|
+
setReconnectingKey(key);
|
|
334
|
+
setReconnectError(null);
|
|
335
|
+
try {
|
|
336
|
+
await reconnectServer.mutateAsync({
|
|
337
|
+
id: server.id,
|
|
338
|
+
scope: server.scope,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
setReconnectError({
|
|
343
|
+
key,
|
|
344
|
+
message: error instanceof Error ? error.message : String(error),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
setReconnectingKey(null);
|
|
349
|
+
}
|
|
350
|
+
}, [reconnectServer]);
|
|
321
351
|
return (_jsxs("section", { "data-testid": "mcp-integrations", className: "space-y-5", children: [_jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(IconServer, { className: "size-4 text-muted-foreground" }), _jsx("h2", { className: "text-sm font-semibold text-foreground", children: "Agent integrations" })] }), _jsx("p", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: "Give your agent tools and context from the services you use." })] }), _jsxs("button", { type: "button", onClick: () => openCatalog(), className: "inline-flex w-fit items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90", children: [_jsx(IconPlus, { className: "size-3.5" }), "Add integration"] })] }), deleteError && (_jsx("p", { className: "border-y border-destructive/20 bg-destructive/5 py-3 text-xs text-destructive", children: deleteError })), serversQuery.isError ? (_jsx("p", { className: "border-y border-destructive/20 bg-destructive/5 py-3 text-xs text-destructive", children: "Could not load connected agent integrations. The catalog is still available." })) : serversQuery.isLoading ? (_jsxs("div", { className: "space-y-3 rounded-xl border border-border/70 bg-card px-4 py-4", children: [_jsx("div", { className: "h-5 w-1/2 animate-pulse rounded bg-muted" }), _jsx("div", { className: "h-5 w-3/4 animate-pulse rounded bg-muted" })] })) : servers.length > 0 && !normalizedQuery ? (_jsxs("section", { className: "space-y-2", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Installed" }), _jsx("div", { className: "overflow-hidden rounded-xl border border-border/70 bg-card text-card-foreground", children: _jsx("div", { className: "divide-y divide-border/60 px-4", children: servers.map((server) => {
|
|
322
352
|
const key = `${server.scope}:${server.id}`;
|
|
323
353
|
const canRemove = server.scope === "user" ||
|
|
324
354
|
serversQuery.data?.role === "owner" ||
|
|
325
355
|
serversQuery.data?.role === "admin";
|
|
326
|
-
return (_jsxs("div", { className: "flex items-
|
|
356
|
+
return (_jsxs("div", { className: "flex items-start gap-3 py-3.5", children: [_jsx("span", { className: "mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-lg border border-border/70 bg-background text-muted-foreground", children: _jsx(IconServer, { className: "size-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "truncate text-sm font-medium text-foreground", children: server.name }), _jsx("span", { className: "text-[11px] text-muted-foreground", children: server.scope === "user"
|
|
327
357
|
? "Personal"
|
|
328
|
-
: "Organization" })] }), _jsx(McpServerStatus, { server: server
|
|
358
|
+
: "Organization" })] }), _jsx(McpServerStatus, { server: server, onReconnect: server.status.state === "error"
|
|
359
|
+
? () => void reconnect(server)
|
|
360
|
+
: undefined, reconnecting: reconnectingKey === key, reconnectError: reconnectError?.key === key
|
|
361
|
+
? reconnectError.message
|
|
362
|
+
: undefined })] }), canRemove && (_jsx("button", { type: "button", onClick: () => void removeServer(server), disabled: deleteServer.isPending, className: "shrink-0 rounded-md border border-border px-2.5 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent disabled:opacity-50", children: deleteTarget === key ? "Confirm" : "Remove" }))] }, key));
|
|
329
363
|
}) }) })] })) : null, filteredCatalog.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Available integrations" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [filteredCatalog.length, " integrations"] })] }), _jsx("div", { className: "grid gap-x-8 overflow-hidden rounded-xl border border-border/70 bg-card px-4 sm:grid-cols-2", children: filteredCatalog.map((integration) => (_jsx(McpCatalogItem, { integration: integration, connected: connectedUrls.has(compareMcpUrl(integration.url)), onConnect: openCatalog }, integration.id))) })] })), filteredCatalog.length === 0 && normalizedQuery && (_jsxs("p", { className: "border-y border-border/60 py-4 text-xs text-muted-foreground", children: ["No agent integrations match \u201C", query, "\u201D."] })), _jsx(McpIntegrationDialog, { open: dialogOpen, onOpenChange: setDialogOpen, initialIntegrationId: initialIntegrationId, defaultScope: "user", canCreateOrgMcp: canCreateOrgMcp, hasOrg: hasOrg, onCreateMcpServer: (args) => createServer.mutateAsync(args) })] }));
|
|
330
364
|
}
|
|
331
365
|
export function IntegrationsPanel() {
|
|
@@ -30,8 +30,8 @@ const SECTION_LABEL_CLASS = "px-2.5 pt-1 pb-0.5 text-[10px] uppercase tracking-w
|
|
|
30
30
|
const APP_SUBMENU_CONTENT_CLASS = "z-50 w-72 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2";
|
|
31
31
|
const SWITCHER_BUTTON_CLASS = "flex w-full items-center gap-2 rounded-md border-0 bg-accent/50 px-2.5 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/70 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-60 cursor-pointer";
|
|
32
32
|
const COMPACT_SWITCHER_BUTTON_CLASS = "flex items-center justify-center rounded-md border-0 bg-accent/50 p-1.5 text-muted-foreground hover:bg-accent/70 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-60 cursor-pointer";
|
|
33
|
-
const DEFAULT_ORGANIZATION_SETTINGS_PATH = "/settings
|
|
34
|
-
const DEFAULT_PROFILE_PATH = "/settings
|
|
33
|
+
const DEFAULT_ORGANIZATION_SETTINGS_PATH = "/settings/organization";
|
|
34
|
+
const DEFAULT_PROFILE_PATH = "/settings/account";
|
|
35
35
|
const DEFAULT_AGENT_PATH = "/agent";
|
|
36
36
|
const APP_ICON_MAP = {
|
|
37
37
|
Mail: IconMail,
|
|
@@ -3,11 +3,11 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
|
|
|
3
3
|
import { Button as ToolkitButton } from "@agent-native/toolkit/ui/button";
|
|
4
4
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@agent-native/toolkit/ui/select";
|
|
5
5
|
import { Switch } from "@agent-native/toolkit/ui/switch";
|
|
6
|
-
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@agent-native/toolkit/ui/table";
|
|
7
6
|
import { IconUserPlus, IconTrash, IconCrown, IconShieldCheck, IconLoader2, IconCheck, IconPencil, IconAt, IconX, IconKey, IconCopy, IconRefresh, IconEye, IconEyeOff, IconCloudUpload, IconFileImport, IconPlus, IconAlertTriangle, IconUsersGroup, IconHelpCircle, IconExternalLink, } from "@tabler/icons-react";
|
|
8
7
|
import { forwardRef, useMemo, useRef, useState, } from "react";
|
|
9
8
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
10
9
|
import { useT } from "../i18n.js";
|
|
10
|
+
import { SettingsGroup, SettingsRow } from "../settings/SettingsRow.js";
|
|
11
11
|
import { cn } from "../utils.js";
|
|
12
12
|
import { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useBulkInviteMembers, useChangeMemberRole, useAcceptInvitation, useRemoveMember, useDeleteOrg, useSwitchOrg, useSetOrgDomain, useSetOrgWorkspaceUrl, useSetOrgAuthProvider, useRevealA2ASecret, useSetA2ASecret, useSyncA2ASecret, useJoinByDomain, useAppRoles, useSetAppMemberRole, } from "./hooks.js";
|
|
13
13
|
const Button = forwardRef(({ className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, variant: "ghost", className: cn("h-auto p-0 hover:bg-transparent hover:text-inherit active:scale-100 [&_svg]:!size-auto", className), ...props })));
|
|
@@ -24,6 +24,12 @@ function ErrorText({ error }) {
|
|
|
24
24
|
return null;
|
|
25
25
|
return (_jsx("p", { className: "text-xs text-destructive", children: error instanceof Error ? error.message : String(error) }));
|
|
26
26
|
}
|
|
27
|
+
function OrganizationHelpIcon({ content, docsUrl, }) {
|
|
28
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", "aria-label": "More information", className: "inline-flex size-3.5 shrink-0 items-center justify-center rounded-full text-muted-foreground/70 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", children: _jsx(IconHelpCircle, { className: "size-3" }) }) }), _jsxs(TooltipContent, { className: "max-w-xs leading-5", children: [_jsx("p", { children: content }), docsUrl ? (_jsx("a", { href: docsUrl, target: "_blank", rel: "noreferrer", className: "mt-1 inline-block underline underline-offset-2", children: "Learn more" })) : null] })] }));
|
|
29
|
+
}
|
|
30
|
+
function OrganizationDescription({ children, help, docsUrl, }) {
|
|
31
|
+
return (_jsxs("span", { className: "inline-flex max-w-full items-center gap-1.5", children: [_jsx("span", { children: children }), help ? _jsx(OrganizationHelpIcon, { content: help, docsUrl: docsUrl }) : null] }));
|
|
32
|
+
}
|
|
27
33
|
function PendingInvitationsCard() {
|
|
28
34
|
const t = useT();
|
|
29
35
|
const { data: org } = useOrg();
|
|
@@ -100,7 +106,7 @@ function MembersCard({ appRoles }) {
|
|
|
100
106
|
const members = membersData?.members ?? [];
|
|
101
107
|
const pendingInvites = invitationsData?.invitations ?? [];
|
|
102
108
|
const hasMultipleOrgs = (org.orgs?.length ?? 0) > 1;
|
|
103
|
-
return (_jsxs("div", { className: "space-y-
|
|
109
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs(SettingsGroup, { title: "Organization", children: [_jsx(SettingsRow, { id: "organization", label: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(IconUsersGroup, { className: "size-4 text-muted-foreground" }), _jsx(OrgNameDisplay, { name: org.orgName ?? "", canEdit: isOwnerOrAdmin })] }), description: `${t("org.memberCount", { count: members.length })} · ${t("org.youAreRole", { role: org.role })}`, control: hasMultipleOrgs ? (_jsxs(Select, { value: org.orgId ?? "", onValueChange: (value) => switchOrg.mutate(value || null), disabled: switchOrg.isPending, children: [_jsx(SelectTrigger, { className: "h-auto w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-xs sm:w-auto", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: org.orgs.map((o) => (_jsx(SelectItem, { value: o.orgId, children: o.orgName }, o.orgId))) })] })) : undefined }), isOwnerOrAdmin && (_jsxs(_Fragment, { children: [_jsx(DomainSettingsSection, { domain: org.allowedDomain, ownerEmail: org.email }), _jsx(WorkspaceUrlSettingsSection, { workspaceUrl: org.workspaceUrl }), _jsx(AuthProviderSettingsSection, { requiredAuthProvider: org.requiredAuthProvider }), isOwner && _jsx(A2ASecretSection, { isSet: Boolean(org.a2aSecretSet) })] })), switchOrg.error && (_jsx("div", { className: "px-5 pb-4", children: _jsx(ErrorText, { error: switchOrg.error }) }))] }), _jsx(MembersTableCard, { members: members, pendingInvites: pendingInvites, isLoadingMembers: isLoadingMembers, currentUserEmail: org.email, currentUserRole: org.role ?? null, appRoles: appRoles }), isOwner && _jsx(DangerZoneCard, { orgName: org.orgName ?? "" })] }));
|
|
104
110
|
}
|
|
105
111
|
function MembersTableCard({ members, pendingInvites, isLoadingMembers, currentUserEmail, currentUserRole, appRoles, }) {
|
|
106
112
|
const t = useT();
|
|
@@ -111,8 +117,7 @@ function MembersTableCard({ members, pendingInvites, isLoadingMembers, currentUs
|
|
|
111
117
|
a.email.toLowerCase(),
|
|
112
118
|
a.role,
|
|
113
119
|
]));
|
|
114
|
-
|
|
115
|
-
return (_jsxs("section", { className: "rounded-lg border border-border bg-card", children: [_jsxs("div", { className: "flex flex-col gap-3 border-b border-border px-4 py-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium", children: t("org.members") }), _jsx("p", { className: "text-xs text-muted-foreground", children: t("org.memberCount", { count: members.length }) })] }), canInvite && !showInviteForm && (_jsxs(Button, { type: "button", intent: "primary", emphasis: "solid", onClick: () => setShowInviteForm(true), className: "inline-flex items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90", children: [_jsx(IconUserPlus, { size: 14 }), t("org.inviteMembers")] }))] }), canInvite && showInviteForm && (_jsx("div", { className: "border-b border-border p-4", children: _jsx(BulkInviteForm, { currentUserRole: currentUserRole, onClose: () => setShowInviteForm(false) }) })), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t("org.member") }), _jsx(TableHead, { children: t("org.role") }), appRoles && (_jsx(TableHead, { children: appRoles.label ?? appRoles.appId })), _jsx(TableHead, { children: t("org.status") }), _jsx(TableHead, { className: "text-end", children: t("org.actions") })] }) }), _jsx(TableBody, { children: isLoadingMembers && members.length === 0 ? ([0, 1, 2].map((i) => (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columnCount, children: _jsx("div", { className: "h-3.5 rounded bg-muted animate-pulse", style: { width: `${180 + i * 48}px` } }) }) }, i)))) : members.length === 0 && pendingInvites.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columnCount, className: "py-8 text-center text-sm text-muted-foreground", children: t("org.noMembers") }) })) : (_jsxs(_Fragment, { children: [members.map((m) => (_jsx(MemberRow, { email: m.email, role: m.role, isCurrentUser: m.email === currentUserEmail, currentUserRole: currentUserRole, appRoles: appRoles, appRole: appRoleByEmail.get(m.email.toLowerCase()) ?? null, canManageAppRoles: Boolean(appRoleData?.canManage) }, m.email))), pendingInvites.map((inv) => (_jsx(PendingInviteRow, { invite: inv, hasAppRoleColumn: Boolean(appRoles) }, inv.id)))] })) })] })] }));
|
|
120
|
+
return (_jsxs("section", { className: "overflow-hidden rounded-xl border border-border/70 bg-card text-card-foreground", children: [_jsxs("div", { className: "flex flex-col gap-3 px-5 py-4 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium", children: t("org.members") }), _jsx("p", { className: "text-xs text-muted-foreground", children: t("org.memberCount", { count: members.length }) })] }), canInvite && !showInviteForm && (_jsxs(Button, { type: "button", intent: "primary", emphasis: "solid", onClick: () => setShowInviteForm(true), className: "inline-flex items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90", children: [_jsx(IconUserPlus, { size: 14 }), t("org.inviteMembers")] }))] }), canInvite && showInviteForm && (_jsx("div", { className: "border-b border-border p-4", children: _jsx(BulkInviteForm, { currentUserRole: currentUserRole, onClose: () => setShowInviteForm(false) }) })), _jsx("div", { className: "divide-y divide-border/60 border-t border-border/60", children: isLoadingMembers && members.length === 0 ? ([0, 1, 2].map((i) => (_jsxs("div", { className: "flex items-center gap-3 px-5 py-4", children: [_jsx("div", { className: "size-8 animate-pulse rounded-full bg-muted" }), _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "h-3.5 animate-pulse rounded bg-muted", style: { width: `${180 + i * 48}px` } }), _jsx("div", { className: "h-3 w-20 animate-pulse rounded bg-muted" })] })] }, i)))) : members.length === 0 && pendingInvites.length === 0 ? (_jsx("div", { className: "px-5 py-10 text-center text-sm text-muted-foreground", children: t("org.noMembers") })) : (_jsxs(_Fragment, { children: [members.map((m) => (_jsx(MemberRow, { email: m.email, role: m.role, isCurrentUser: m.email === currentUserEmail, currentUserRole: currentUserRole, appRoles: appRoles, appRole: appRoleByEmail.get(m.email.toLowerCase()) ?? null, canManageAppRoles: Boolean(appRoleData?.canManage) }, m.email))), pendingInvites.map((inv) => (_jsx(PendingInviteRow, { invite: inv }, inv.id)))] })) })] }));
|
|
116
121
|
}
|
|
117
122
|
function DangerZoneCard({ orgName }) {
|
|
118
123
|
const t = useT();
|
|
@@ -126,11 +131,11 @@ function DangerZoneCard({ orgName }) {
|
|
|
126
131
|
return;
|
|
127
132
|
deleteOrg.mutate(orgName, { onSuccess: () => setOpen(false) });
|
|
128
133
|
}
|
|
129
|
-
return (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
return (_jsx("section", { className: "rounded-lg border border-destructive/40 bg-card p-4 space-y-3", children: _jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx(IconAlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-destructive" }), _jsxs("div", { className: "min-w-0 space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("h3", { className: "text-sm font-medium text-destructive", children: t("org.dangerZone") }), _jsx("p", { className: "text-sm leading-6 text-muted-foreground", children: _jsx(OrganizationDescription, { help: t("org.deleteOrgDescription"), children: "Delete this organization and all of its members." }) })] }), _jsxs(AlertDialog, { open: open, onOpenChange: (next) => {
|
|
135
|
+
setOpen(next);
|
|
136
|
+
if (!next)
|
|
137
|
+
setConfirmText("");
|
|
138
|
+
}, children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsx(Button, { type: "button", intent: "danger", emphasis: "outline", className: "cursor-pointer rounded-md border border-destructive/40 px-3 py-1.5 text-xs font-medium text-destructive hover:bg-destructive/10", children: t("org.deleteOrg") }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: t("org.deleteOrg") }), _jsx(AlertDialogDescription, { children: t("org.deleteOrgConfirmPrompt", { name: orgName }) })] }), _jsx("input", { type: "text", value: confirmText, onChange: (e) => setConfirmText(e.target.value), placeholder: t("org.deleteOrgConfirmPlaceholder"), className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-destructive", autoFocus: true }), _jsx(ErrorText, { error: deleteOrg.error }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { className: "cursor-pointer", children: t("org.cancel") }), _jsx(AlertDialogAction, { disabled: !canConfirm || deleteOrg.isPending, onClick: handleConfirm, className: "cursor-pointer bg-destructive text-destructive-foreground hover:bg-destructive/90 disabled:cursor-not-allowed disabled:opacity-50", children: deleteOrg.isPending ? (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx(IconLoader2, { size: 14, className: "animate-spin" }), t("org.deleteOrgPending")] })) : (t("org.deleteOrgConfirmCta")) })] })] })] })] })] }) }));
|
|
134
139
|
}
|
|
135
140
|
function roleLabel(role, t) {
|
|
136
141
|
if (role === "owner")
|
|
@@ -143,33 +148,43 @@ function RoleBadge({ role }) {
|
|
|
143
148
|
const t = useT();
|
|
144
149
|
return (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded border border-border px-2 py-1 text-xs text-muted-foreground", children: [_jsx(RoleIcon, { role: role }), roleLabel(role, t)] }));
|
|
145
150
|
}
|
|
146
|
-
function
|
|
151
|
+
function memberInitials(email) {
|
|
152
|
+
const localPart = email.split("@", 1)[0] ?? email;
|
|
153
|
+
const initials = localPart
|
|
154
|
+
.split(/[._-]+/)
|
|
155
|
+
.filter(Boolean)
|
|
156
|
+
.slice(0, 2)
|
|
157
|
+
.map((part) => part[0]?.toUpperCase())
|
|
158
|
+
.join("");
|
|
159
|
+
return initials || "?";
|
|
160
|
+
}
|
|
161
|
+
function PendingInviteRow({ invite }) {
|
|
147
162
|
const t = useT();
|
|
148
|
-
return (_jsxs(
|
|
163
|
+
return (_jsxs("div", { className: "flex flex-col gap-3 px-5 py-3.5 opacity-70 sm:flex-row sm:items-center", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [_jsx("div", { className: "flex size-8 shrink-0 items-center justify-center rounded-full border border-border bg-background text-xs font-medium text-muted-foreground", children: memberInitials(invite.email) }), _jsx("span", { className: "min-w-0 truncate text-sm", children: invite.email })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 sm:justify-end", children: [_jsx(RoleBadge, { role: invite.role }), _jsx("span", { className: "rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground", children: t("org.invited") })] })] }));
|
|
149
164
|
}
|
|
150
165
|
/** Sentinel for "clear the assignment" — Select cannot carry an empty value. */
|
|
151
166
|
const UNASSIGNED = "__unassigned__";
|
|
152
|
-
function
|
|
167
|
+
function AppRoleControl({ email, appRoles, appRole, canManage, }) {
|
|
153
168
|
const [editing, setEditing] = useState(false);
|
|
154
169
|
const setAppRole = useSetAppMemberRole(appRoles.appId);
|
|
155
170
|
const labelFor = (r) => appRoles.roleLabels?.[r] ?? r;
|
|
156
171
|
if (editing) {
|
|
157
|
-
return (
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
return (_jsxs(Select, { defaultOpen: true, value: appRole ?? UNASSIGNED, onOpenChange: (open) => {
|
|
173
|
+
if (!open)
|
|
174
|
+
setEditing(false);
|
|
175
|
+
}, onValueChange: (value) => {
|
|
176
|
+
const next = value === UNASSIGNED ? null : value;
|
|
177
|
+
if (next === appRole) {
|
|
178
|
+
setEditing(false);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
setAppRole.mutate({ email, role: next }, { onSuccess: () => setEditing(false) });
|
|
182
|
+
}, disabled: setAppRole.isPending, children: [_jsx(SelectTrigger, { autoFocus: true, className: "h-auto w-auto rounded-md border border-border bg-background px-2 py-1 text-xs", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: UNASSIGNED, children: appRoles.defaultRole ? labelFor(appRoles.defaultRole) : "—" }), appRoles.roles.map((r) => (_jsx(SelectItem, { value: r, children: labelFor(r) }, r)))] })] }));
|
|
168
183
|
}
|
|
169
184
|
// An unassigned member shows the app's default only as a hint. The default
|
|
170
185
|
// never satisfies a server guard, so it must not read as a granted role.
|
|
171
186
|
const display = appRole ? (_jsx("span", { className: "inline-flex items-center rounded border border-border px-2 py-1 text-xs text-muted-foreground", children: labelFor(appRole) })) : (_jsx("span", { className: "text-xs text-muted-foreground/70", children: appRoles.defaultRole ? labelFor(appRoles.defaultRole) : "—" }));
|
|
172
|
-
return
|
|
187
|
+
return canManage ? (_jsx(Button, { type: "button", onClick: () => setEditing(true), className: "cursor-pointer rounded hover:opacity-80", children: display })) : (display);
|
|
173
188
|
}
|
|
174
189
|
function MemberRow({ email, role, isCurrentUser, currentUserRole, appRoles, appRole, canManageAppRoles, }) {
|
|
175
190
|
const t = useT();
|
|
@@ -184,20 +199,20 @@ function MemberRow({ email, role, isCurrentUser, currentUserRole, appRoles, appR
|
|
|
184
199
|
!isCurrentUser &&
|
|
185
200
|
(currentUserRole === "owner" ||
|
|
186
201
|
(currentUserRole === "admin" && role === "member"));
|
|
187
|
-
return (_jsxs(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
202
|
+
return (_jsxs("div", { className: "flex flex-col gap-3 px-5 py-3.5 sm:flex-row sm:items-center", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [_jsx("div", { className: "flex size-8 shrink-0 items-center justify-center rounded-full border border-border bg-background text-xs font-medium text-muted-foreground", children: memberInitials(email) }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm", children: email }), isCurrentUser && (_jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: t("org.you") }))] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 sm:justify-end", children: [_jsx(RoleBadge, { role: role }), appRoles && (_jsx(AppRoleControl, { email: email, appRoles: appRoles, appRole: appRole ?? null, canManage: Boolean(canManageAppRoles) })), canManage && (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [editing ? (_jsxs(Select, { defaultOpen: true, value: role, onOpenChange: (open) => {
|
|
203
|
+
if (!open)
|
|
204
|
+
setEditing(false);
|
|
205
|
+
}, onValueChange: (value) => {
|
|
206
|
+
const next = value === "admin" ? "admin" : "member";
|
|
207
|
+
if (next !== role) {
|
|
208
|
+
changeRole.mutate({ email, role: next }, { onSuccess: () => setEditing(false) });
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
setEditing(false);
|
|
212
|
+
}
|
|
213
|
+
}, disabled: changeRole.isPending, children: [_jsx(SelectTrigger, { autoFocus: true, className: "h-auto w-auto rounded-md border border-border bg-background px-2 py-1 text-xs", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "member", children: t("org.member") }), _jsx(SelectItem, { value: "admin", children: t("org.admin") })] })] })) : (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", onClick: () => setEditing(true), className: "text-muted-foreground hover:text-foreground", children: _jsx(IconPencil, { size: 14 }) }) }), _jsx(TooltipContent, { children: t("org.changeRole") })] })), confirmingRemove ? (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { type: "button", intent: "neutral", emphasis: "ghost", onClick: () => setConfirmingRemove(false), className: "rounded px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground", children: t("org.cancel") }), _jsx(Button, { type: "button", intent: "danger", emphasis: "solid", disabled: removeMember.isPending, onClick: () => removeMember.mutate(email, {
|
|
214
|
+
onSettled: () => setConfirmingRemove(false),
|
|
215
|
+
}), className: "rounded bg-destructive px-1.5 py-0.5 text-[11px] text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50", children: t("org.remove") })] })) : (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", intent: "danger", emphasis: "ghost", disabled: removeMember.isPending, onClick: () => setConfirmingRemove(true), className: "text-muted-foreground hover:text-destructive disabled:opacity-50", children: _jsx(IconTrash, { size: 14 }) }) }), _jsx(TooltipContent, { children: t("org.removeMember") })] }))] }))] })] }));
|
|
201
216
|
}
|
|
202
217
|
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
203
218
|
function parseEmailList(input) {
|
|
@@ -334,18 +349,18 @@ function DomainSettingsSection({ domain, ownerEmail, }) {
|
|
|
334
349
|
onSuccess: () => setEditing(false),
|
|
335
350
|
});
|
|
336
351
|
}
|
|
337
|
-
return (
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
352
|
+
return (_jsx(SettingsRow, { id: "email-domain", label: "Email domain auto-join", description: _jsx(OrganizationDescription, { help: `Anyone who signs up with an email at this domain joins the organization automatically. Only your own email domain (${ownDomain || "—"}) can be used; free email providers are not allowed.`, docsUrl: "https://www.builder.io/c/docs/agent-native-authentication?utm_source=agent-native&utm_medium=product&utm_campaign=organization_settings&utm_content=domain_auto_join", children: "Automatically add members with your work email." }), control: !editing ? (_jsx("div", { className: "flex flex-wrap items-center justify-end gap-2", children: domain ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-2.5 py-1.5 text-sm", children: [_jsx(IconAt, { className: "h-3.5 w-3.5 text-muted-foreground" }), domain] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", onClick: () => {
|
|
353
|
+
setDraft(domain);
|
|
354
|
+
setEditing(true);
|
|
355
|
+
}, className: "text-muted-foreground hover:text-foreground", children: _jsx(IconPencil, { size: 14 }) }) }), _jsx(TooltipContent, { children: "Edit domain" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", intent: "danger", emphasis: "ghost", disabled: setOrgDomain.isPending, onClick: () => setOrgDomain.mutate(null), className: "text-muted-foreground hover:text-destructive disabled:opacity-50", children: _jsx(IconX, { size: 14 }) }) }), _jsx(TooltipContent, { children: "Remove domain" })] })] })) : (_jsxs(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => {
|
|
356
|
+
setDraft(ownDomain);
|
|
357
|
+
setEditing(true);
|
|
358
|
+
}, className: "flex items-center gap-1.5 rounded-md border border-border px-2.5 py-1.5 text-xs font-medium hover:bg-accent/50", children: [_jsx(IconAt, { size: 14 }), "Set domain"] })) })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "text", value: draft, onChange: (e) => setDraft(e.target.value), onKeyDown: (e) => {
|
|
359
|
+
if (e.key === "Enter")
|
|
360
|
+
save();
|
|
361
|
+
if (e.key === "Escape")
|
|
362
|
+
setEditing(false);
|
|
363
|
+
}, placeholder: ownDomain || "example.com", className: "w-44 rounded-md border border-border bg-background px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-foreground", autoFocus: true }), _jsx(Button, { type: "button", intent: "primary", emphasis: "solid", disabled: setOrgDomain.isPending, onClick: save, className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: setOrgDomain.isPending ? (_jsx(IconLoader2, { size: 14, className: "animate-spin" })) : ("Save") }), _jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => setEditing(false), className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground", children: "Cancel" })] })), children: setOrgDomain.error ? _jsx(ErrorText, { error: setOrgDomain.error }) : null }));
|
|
349
364
|
}
|
|
350
365
|
function WorkspaceUrlSettingsSection({ workspaceUrl, }) {
|
|
351
366
|
const setWorkspaceUrl = useSetOrgWorkspaceUrl();
|
|
@@ -361,15 +376,15 @@ function WorkspaceUrlSettingsSection({ workspaceUrl, }) {
|
|
|
361
376
|
onSuccess: () => setEditing(false),
|
|
362
377
|
});
|
|
363
378
|
}
|
|
364
|
-
return (
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
379
|
+
return (_jsx(SettingsRow, { id: "workspace-url", label: "Workspace URL", description: _jsx(OrganizationDescription, { help: "Members who land on another deployment can be sent to this workspace URL instead of an empty app.", docsUrl: "https://www.builder.io/c/docs/agent-native-deployment?utm_source=agent-native&utm_medium=product&utm_campaign=organization_settings&utm_content=workspace_url", children: "Send members to this workspace from another deployment." }), control: !editing ? (_jsx("div", { className: "flex flex-wrap items-center justify-end gap-2", children: workspaceUrl ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "inline-flex max-w-72 items-center gap-1.5 truncate rounded-md border border-border bg-background px-2.5 py-1.5 text-sm", children: [_jsx(IconExternalLink, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate", children: workspaceUrl })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", onClick: () => {
|
|
380
|
+
setDraft(workspaceUrl);
|
|
381
|
+
setEditing(true);
|
|
382
|
+
}, className: "text-muted-foreground hover:text-foreground", children: _jsx(IconPencil, { size: 14 }) }) }), _jsx(TooltipContent, { children: "Edit workspace URL" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", intent: "danger", emphasis: "ghost", disabled: setWorkspaceUrl.isPending, onClick: () => setWorkspaceUrl.mutate(null), className: "text-muted-foreground hover:text-destructive disabled:opacity-50", children: _jsx(IconX, { size: 14 }) }) }), _jsx(TooltipContent, { children: "Remove workspace URL" })] })] })) : (_jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => setEditing(true), className: "flex items-center gap-1.5 rounded-md border border-border px-2.5 py-1.5 text-xs font-medium hover:bg-accent/50", children: "Set URL" })) })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "text", value: draft, onChange: (e) => setDraft(e.target.value), onKeyDown: (e) => {
|
|
383
|
+
if (e.key === "Enter")
|
|
384
|
+
save();
|
|
385
|
+
if (e.key === "Escape")
|
|
386
|
+
setEditing(false);
|
|
387
|
+
}, placeholder: "workspace.example.com", className: "w-56 rounded-md border border-border bg-background px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-foreground", autoFocus: true }), _jsx(Button, { type: "button", intent: "primary", emphasis: "solid", disabled: setWorkspaceUrl.isPending, onClick: save, className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: setWorkspaceUrl.isPending ? (_jsx(IconLoader2, { size: 14, className: "animate-spin" })) : ("Save") }), _jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => setEditing(false), className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground", children: "Cancel" })] })), children: setWorkspaceUrl.error ? (_jsx(ErrorText, { error: setWorkspaceUrl.error })) : null }));
|
|
373
388
|
}
|
|
374
389
|
function AuthProviderSettingsSection({ requiredAuthProvider, }) {
|
|
375
390
|
const setAuthProvider = useSetOrgAuthProvider();
|
|
@@ -382,7 +397,7 @@ function AuthProviderSettingsSection({ requiredAuthProvider, }) {
|
|
|
382
397
|
}
|
|
383
398
|
setAuthProvider.mutate(null);
|
|
384
399
|
}
|
|
385
|
-
return (_jsxs(
|
|
400
|
+
return (_jsxs(_Fragment, { children: [_jsx(SettingsRow, { id: "organization-sign-in", label: "Organization sign-in", description: _jsx(OrganizationDescription, { help: "Require Google sign-in for every member. Enabling this revokes current sessions and rejects future password or non-Google sign-ins.", docsUrl: "https://www.builder.io/c/docs/agent-native-authentication?utm_source=agent-native&utm_medium=product&utm_campaign=organization_settings&utm_content=organization_sign_in", children: "Choose how members sign in." }), control: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "text-sm font-medium", children: enabled ? "Google required" : "Google optional" }), _jsx(Switch, { checked: enabled, disabled: setAuthProvider.isPending, onCheckedChange: changeProvider, "aria-label": "Require Google sign-in" })] }), children: setAuthProvider.error ? (_jsx(ErrorText, { error: setAuthProvider.error })) : null }), _jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Require Google sign-in?" }), _jsx(AlertDialogDescription, { children: "Every current session in this organization will be revoked. Members must use their Google Workspace account the next time they sign in. Continue only after Google sign-in is configured for this app." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: setAuthProvider.isPending, children: "Cancel" }), _jsx(AlertDialogAction, { disabled: setAuthProvider.isPending, onClick: () => {
|
|
386
401
|
setAuthProvider.mutate("google", {
|
|
387
402
|
onSuccess: () => setConfirmOpen(false),
|
|
388
403
|
});
|
|
@@ -463,19 +478,24 @@ function A2ASecretSection({ isSet }) {
|
|
|
463
478
|
});
|
|
464
479
|
}
|
|
465
480
|
const masked = isSet ? "••••••••••••" : "Not set";
|
|
466
|
-
return (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
481
|
+
return (_jsx(SettingsRow, { id: "cross-app-authentication", label: "Cross-app authentication", description: _jsx(OrganizationDescription, { help: "This secret authenticates cross-app delegation. Every app in the organization must share it.", children: "Share one secret across connected apps." }), control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-2.5 py-1.5 text-sm font-mono", children: [_jsx(IconKey, { className: "h-3.5 w-3.5 text-muted-foreground" }), secret ?? masked] }), isSet && (_jsxs(_Fragment, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", onClick: toggleReveal, disabled: revealA2ASecret.isPending, className: "text-muted-foreground hover:text-foreground", children: secret ? _jsx(IconEyeOff, { size: 14 }) : _jsx(IconEye, { size: 14 }) }) }), _jsx(TooltipContent, { children: secret ? "Hide secret" : "Reveal secret" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", onClick: copyToClipboard, disabled: revealA2ASecret.isPending, className: "text-muted-foreground hover:text-foreground", children: copied ? (_jsx(IconCheck, { size: 14, className: "text-primary" })) : (_jsx(IconCopy, { size: 14 })) }) }), _jsx(TooltipContent, { children: "Copy secret" })] })] })), _jsxs(Button, { type: "button", intent: "danger", emphasis: "outline", onClick: regenerate, disabled: setA2ASecret.isPending || syncA2ASecret.isPending, className: "inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1.5 text-xs font-medium hover:bg-accent/50 disabled:opacity-50", children: [setA2ASecret.isPending ? (_jsx(IconLoader2, { size: 14, className: "animate-spin" })) : (_jsx(IconRefresh, { size: 14 })), "Regenerate"] }), isSet && (_jsxs(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => syncToApps(), disabled: setA2ASecret.isPending || syncA2ASecret.isPending, className: "inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1.5 text-xs font-medium hover:bg-accent/50 disabled:opacity-50", children: [syncA2ASecret.isPending ? (_jsx(IconLoader2, { size: 14, className: "animate-spin" })) : (_jsx(IconCloudUpload, { size: 14 })), "Sync"] })), !pasteMode && (_jsxs(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => setPasteMode(true), className: "inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1.5 text-xs font-medium hover:bg-accent/50", children: [_jsx(IconKey, { size: 14 }), "Paste secret"] }))] }), children: (pasteMode ||
|
|
482
|
+
syncA2ASecret.isPending ||
|
|
483
|
+
syncResult ||
|
|
484
|
+
revealA2ASecret.error ||
|
|
485
|
+
setA2ASecret.error ||
|
|
486
|
+
syncA2ASecret.error) && (_jsxs("div", { className: "space-y-3", children: [syncA2ASecret.isPending && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Syncing to connected apps\u2026" })), syncResult && !syncA2ASecret.isPending && (_jsxs("div", { className: "space-y-1", children: [_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Synced to ", syncResult.succeeded, "/", syncResult.total, " app", syncResult.total === 1 ? "" : "s", syncResult.failed > 0 ? ` (${syncResult.failed} failed)` : "", "."] }), syncResult.failed > 0 && (_jsx("ul", { className: "list-disc space-y-0.5 ps-5 text-xs text-destructive", children: syncResult.results
|
|
487
|
+
.filter((r) => !r.ok)
|
|
488
|
+
.map((r) => (_jsxs("li", { children: [r.name, ": ", r.error || `HTTP ${r.status ?? "?"}`] }, r.id))) }))] })), pasteMode ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "text", value: pasteValue, onChange: (e) => setPasteValue(e.target.value), onKeyDown: (e) => {
|
|
489
|
+
if (e.key === "Enter")
|
|
490
|
+
saveSecret();
|
|
491
|
+
if (e.key === "Escape") {
|
|
492
|
+
setPasteMode(false);
|
|
493
|
+
setPasteValue("");
|
|
494
|
+
}
|
|
495
|
+
}, placeholder: "Paste A2A secret", className: "min-w-0 flex-1 rounded-md border border-border bg-background px-2.5 py-1.5 text-sm font-mono focus:outline-none focus:ring-1 focus:ring-foreground", autoFocus: true }), _jsx(Button, { type: "button", intent: "primary", emphasis: "solid", disabled: !pasteValue.trim() || setA2ASecret.isPending, onClick: saveSecret, className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: setA2ASecret.isPending ? (_jsx(IconLoader2, { size: 14, className: "animate-spin" })) : ("Save") }), _jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => {
|
|
472
496
|
setPasteMode(false);
|
|
473
497
|
setPasteValue("");
|
|
474
|
-
}
|
|
475
|
-
}, placeholder: "Paste A2A secret", className: "flex-1 rounded-md border border-border bg-background px-2.5 py-1.5 text-sm font-mono focus:outline-none focus:ring-1 focus:ring-foreground", autoFocus: true }), _jsx(Button, { type: "button", intent: "primary", emphasis: "solid", disabled: !pasteValue.trim() || setA2ASecret.isPending, onClick: saveSecret, className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: setA2ASecret.isPending ? (_jsx(IconLoader2, { size: 14, className: "animate-spin" })) : ("Save") }), _jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: () => {
|
|
476
|
-
setPasteMode(false);
|
|
477
|
-
setPasteValue("");
|
|
478
|
-
}, className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground", children: "Cancel" })] })), _jsx(ErrorText, { error: revealA2ASecret.error }), _jsx(ErrorText, { error: setA2ASecret.error }), _jsx(ErrorText, { error: syncA2ASecret.error })] }));
|
|
498
|
+
}, className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground", children: "Cancel" })] })) : null, _jsx(ErrorText, { error: revealA2ASecret.error }), _jsx(ErrorText, { error: setA2ASecret.error }), _jsx(ErrorText, { error: syncA2ASecret.error })] })) }));
|
|
479
499
|
}
|
|
480
500
|
/**
|
|
481
501
|
* Default Team management page. Templates can route directly to this component
|
|
@@ -484,7 +504,7 @@ function A2ASecretSection({ isSet }) {
|
|
|
484
504
|
export function TeamPage({ layout, title, showTitle = true, createOrgDescription, className, appRoles, }) {
|
|
485
505
|
const t = useT();
|
|
486
506
|
const { data: org, isLoading } = useOrg();
|
|
487
|
-
const content = (_jsxs("div", { className: `space-y-6 ${className ?? "
|
|
507
|
+
const content = (_jsxs("div", { className: `w-full space-y-6 ${className ?? ""}`, children: [showTitle ? (_jsx("h2", { className: "text-2xl font-bold tracking-tight", children: title ?? t("org.team") })) : null, isLoading && (_jsx("section", { className: "rounded-lg border border-border bg-card p-6", children: _jsx("div", { className: "text-sm text-muted-foreground", children: t("org.loading") }) })), !isLoading && (_jsxs(_Fragment, { children: [_jsx(PendingInvitationsCard, {}), org?.domainMatches && org.domainMatches.length > 0 && (_jsx(JoinByDomainCard, { matches: org.domainMatches })), !org?.orgId ? (_jsx(CreateOrgCard, { description: createOrgDescription })) : (_jsx(MembersCard, { appRoles: appRoles }))] }))] }));
|
|
488
508
|
const wrapped = (_jsx(TooltipProvider, { delayDuration: 200, children: content }));
|
|
489
509
|
return layout ? _jsx(_Fragment, { children: layout(wrapped) }) : wrapped;
|
|
490
510
|
}
|
|
@@ -37,7 +37,7 @@ export interface RequireSessionProps {
|
|
|
37
37
|
fallback?: React.ReactNode;
|
|
38
38
|
/**
|
|
39
39
|
* When true (default), unauthenticated visitors are redirected to the
|
|
40
|
-
* framework sign-in entry point (`/
|
|
40
|
+
* framework sign-in entry point (`/sign-in`) carrying a `c`
|
|
41
41
|
* continuation for the current URL — so they land back here once signed in.
|
|
42
42
|
* When false, `signedOut` is rendered instead and no navigation happens.
|
|
43
43
|
*/
|
|
@@ -29,8 +29,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
29
29
|
* surfaces that authenticate by another mechanism.
|
|
30
30
|
*/
|
|
31
31
|
import { useEffect, useRef } from "react";
|
|
32
|
-
import { signInJourney } from "../shared/sign-in-journey.js";
|
|
33
|
-
import {
|
|
32
|
+
import { SIGN_IN_ENTRY_PATH, signInJourney, } from "../shared/sign-in-journey.js";
|
|
33
|
+
import { appBasePath, appPath } from "./api-path.js";
|
|
34
34
|
import { DefaultSpinner } from "./DefaultSpinner.js";
|
|
35
35
|
import { useSession } from "./use-session.js";
|
|
36
36
|
/**
|
|
@@ -59,7 +59,7 @@ function currentJourney(returnTo) {
|
|
|
59
59
|
* directly and honour its `signInHref: null`.
|
|
60
60
|
*/
|
|
61
61
|
export function buildSignInReturnHref(opts) {
|
|
62
|
-
const base =
|
|
62
|
+
const base = appPath(SIGN_IN_ENTRY_PATH);
|
|
63
63
|
if (typeof window === "undefined")
|
|
64
64
|
return base;
|
|
65
65
|
return currentJourney(opts?.returnTo).signInHref ?? base;
|
|
@@ -46,10 +46,15 @@ export interface CreateMcpServerArgs {
|
|
|
46
46
|
description?: string;
|
|
47
47
|
}
|
|
48
48
|
export declare function useCreateMcpServer(): import("@tanstack/react-query").UseMutationResult<McpServer, Error, CreateMcpServerArgs, unknown>;
|
|
49
|
+
export interface ReconnectMcpServerArgs {
|
|
50
|
+
id: string;
|
|
51
|
+
scope: McpServerScope;
|
|
52
|
+
}
|
|
49
53
|
export declare function useDeleteMcpServer(): import("@tanstack/react-query").UseMutationResult<void, Error, {
|
|
50
54
|
id: string;
|
|
51
55
|
scope: McpServerScope;
|
|
52
56
|
}, unknown>;
|
|
57
|
+
export declare function useReconnectMcpServer(): import("@tanstack/react-query").UseMutationResult<void, Error, ReconnectMcpServerArgs, unknown>;
|
|
53
58
|
export interface TestMcpUrlResult {
|
|
54
59
|
ok: boolean;
|
|
55
60
|
error?: string;
|
|
@@ -59,6 +59,42 @@ export function useDeleteMcpServer() {
|
|
|
59
59
|
onSuccess: () => qc.invalidateQueries({ queryKey: LIST_KEY }),
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
+
async function readMcpMutationBody(res) {
|
|
63
|
+
const text = (await res.text()).trim();
|
|
64
|
+
if (!text)
|
|
65
|
+
return null;
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(text);
|
|
68
|
+
}
|
|
69
|
+
catch (cause) {
|
|
70
|
+
throw new Error(`Reconnect response was not valid JSON (HTTP ${res.status}).`, { cause });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function reconnectMcpServerUrl(args) {
|
|
74
|
+
return `${ENDPOINT}/${encodeURIComponent(args.id)}/reconnect?scope=${args.scope}`;
|
|
75
|
+
}
|
|
76
|
+
export function useReconnectMcpServer() {
|
|
77
|
+
const qc = useQueryClient();
|
|
78
|
+
return useMutation({
|
|
79
|
+
mutationFn: async (args) => {
|
|
80
|
+
const res = await fetch(reconnectMcpServerUrl(args), {
|
|
81
|
+
method: "POST",
|
|
82
|
+
credentials: "include",
|
|
83
|
+
});
|
|
84
|
+
const body = await readMcpMutationBody(res);
|
|
85
|
+
const error = typeof body?.error === "string" && body.error.trim()
|
|
86
|
+
? body.error.trim()
|
|
87
|
+
: undefined;
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
throw new Error(error || `Reconnect failed (${res.status})`);
|
|
90
|
+
}
|
|
91
|
+
if (body?.ok !== true) {
|
|
92
|
+
throw new Error(error || "Reconnect failed");
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
onSuccess: () => qc.invalidateQueries({ queryKey: LIST_KEY }),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
62
98
|
export function getMcpUrlValidationError(rawUrl) {
|
|
63
99
|
const trimmed = rawUrl.trim();
|
|
64
100
|
if (!trimmed)
|