Merge upstream/dev into feature/mcp-migration

This commit is contained in:
CREDO23 2026-04-22 19:53:26 +02:00
commit 4915675f45
54 changed files with 2050 additions and 359 deletions

View file

@ -303,5 +303,79 @@ export const AUTO_INDEX_DEFAULTS: Record<string, AutoIndexConfig> = {
export const AUTO_INDEX_CONNECTOR_TYPES = new Set<string>(Object.keys(AUTO_INDEX_DEFAULTS));
// ============================================================================
// CONNECTOR TELEMETRY REGISTRY
// ----------------------------------------------------------------------------
// Single source of truth for "what does this connector_type look like in
// analytics?". Any connector added to the lists above is automatically
// picked up here, so adding a new integration does NOT require touching
// `lib/posthog/events.ts` or per-connector tracking code.
// ============================================================================
export type ConnectorTelemetryGroup = "oauth" | "composio" | "crawler" | "other" | "unknown";
export interface ConnectorTelemetryMeta {
connector_type: string;
connector_title: string;
connector_group: ConnectorTelemetryGroup;
is_oauth: boolean;
}
const CONNECTOR_TELEMETRY_REGISTRY: ReadonlyMap<string, ConnectorTelemetryMeta> = (() => {
const map = new Map<string, ConnectorTelemetryMeta>();
for (const c of OAUTH_CONNECTORS) {
map.set(c.connectorType, {
connector_type: c.connectorType,
connector_title: c.title,
connector_group: "oauth",
is_oauth: true,
});
}
for (const c of COMPOSIO_CONNECTORS) {
map.set(c.connectorType, {
connector_type: c.connectorType,
connector_title: c.title,
connector_group: "composio",
is_oauth: true,
});
}
for (const c of CRAWLERS) {
map.set(c.connectorType, {
connector_type: c.connectorType,
connector_title: c.title,
connector_group: "crawler",
is_oauth: false,
});
}
for (const c of OTHER_CONNECTORS) {
map.set(c.connectorType, {
connector_type: c.connectorType,
connector_title: c.title,
connector_group: "other",
is_oauth: false,
});
}
return map;
})();
/**
* Returns telemetry metadata for a connector_type, or a minimal "unknown"
* record so tracking never no-ops for connectors that exist in the backend
* but were forgotten in the UI registry.
*/
export function getConnectorTelemetryMeta(connectorType: string): ConnectorTelemetryMeta {
const hit = CONNECTOR_TELEMETRY_REGISTRY.get(connectorType);
if (hit) return hit;
return {
connector_type: connectorType,
connector_title: connectorType,
connector_group: "unknown",
is_oauth: false,
};
}
// Re-export IndexingConfigState from schemas for backward compatibility
export type { IndexingConfigState } from "./connector-popup.schemas";

View file

@ -24,6 +24,8 @@ import { isSelfHosted } from "@/lib/env-config";
import {
trackConnectorConnected,
trackConnectorDeleted,
trackConnectorSetupFailure,
trackConnectorSetupStarted,
trackIndexWithDateRangeOpened,
trackIndexWithDateRangeStarted,
trackPeriodicIndexingStarted,
@ -232,10 +234,20 @@ export const useConnectorDialog = () => {
if (result.error) {
const oauthConnector = result.connector
? OAUTH_CONNECTORS.find((c) => c.id === result.connector)
? OAUTH_CONNECTORS.find((c) => c.id === result.connector) ||
COMPOSIO_CONNECTORS.find((c) => c.id === result.connector)
: null;
const name = oauthConnector?.title || "connector";
if (oauthConnector) {
trackConnectorSetupFailure(
Number(searchSpaceId),
oauthConnector.connectorType,
result.error,
"oauth_callback"
);
}
if (result.error === "duplicate_account") {
toast.error(`This ${name} account is already connected`, {
description: "Please use a different account or manage the existing connection.",
@ -351,6 +363,8 @@ export const useConnectorDialog = () => {
// Set connecting state immediately to disable button and show spinner
setConnectingId(connector.id);
trackConnectorSetupStarted(Number(searchSpaceId), connector.connectorType, "oauth_click");
try {
// Check if authEndpoint already has query parameters
const separator = connector.authEndpoint.includes("?") ? "&" : "?";
@ -372,6 +386,12 @@ export const useConnectorDialog = () => {
window.location.href = validatedData.auth_url;
} catch (error) {
console.error(`Error connecting to ${connector.title}:`, error);
trackConnectorSetupFailure(
Number(searchSpaceId),
connector.connectorType,
error instanceof Error ? error.message : "oauth_initiation_failed",
"oauth_init"
);
if (error instanceof Error && error.message.includes("Invalid auth URL")) {
toast.error(`Invalid response from ${connector.title} OAuth endpoint`);
} else {
@ -395,6 +415,11 @@ export const useConnectorDialog = () => {
if (!searchSpaceId) return;
setConnectingId("webcrawler-connector");
trackConnectorSetupStarted(
Number(searchSpaceId),
EnumConnectorName.WEBCRAWLER_CONNECTOR,
"webcrawler_quick_add"
);
try {
await createConnector({
data: {
@ -444,6 +469,12 @@ export const useConnectorDialog = () => {
}
} catch (error) {
console.error("Error creating webcrawler connector:", error);
trackConnectorSetupFailure(
Number(searchSpaceId),
EnumConnectorName.WEBCRAWLER_CONNECTOR,
error instanceof Error ? error.message : "webcrawler_create_failed",
"webcrawler_quick_add"
);
toast.error("Failed to create web crawler connector");
} finally {
setConnectingId(null);
@ -455,6 +486,8 @@ export const useConnectorDialog = () => {
(connectorType: string) => {
if (!searchSpaceId) return;
trackConnectorSetupStarted(Number(searchSpaceId), connectorType, "non_oauth_click");
// Handle Obsidian specifically on Desktop & Cloud
if (connectorType === EnumConnectorName.OBSIDIAN_CONNECTOR && !selfHosted && isDesktop) {
setIsOpen(false);
@ -683,6 +716,12 @@ export const useConnectorDialog = () => {
}
} catch (error) {
console.error("Error creating connector:", error);
trackConnectorSetupFailure(
Number(searchSpaceId),
connectingConnectorType ?? formData.connector_type,
error instanceof Error ? error.message : "connector_create_failed",
"non_oauth_form"
);
toast.error(error instanceof Error ? error.message : "Failed to create connector");
} finally {
isCreatingConnectorRef.current = false;