2069 lines · 78.8 KB · TypeScript
RawDownload
import { useEffect, useState, type ReactNode } from "react";import { Link, useNavigate } from "@tanstack/react-router";import {  type CompleteSourceOAuthResult,  type ConnectSourceBatchPayload,  type ConnectSourceBatchResult,  type ConnectSourcePayload,  type ConnectSourceResult,  type CreateWorkspaceOauthClientPayload,  type DiscoverSourcePayload,  type InstanceConfig,  type Loadable,  type SecretListItem,  type Source,  type SourceDiscoveryResult,  type WorkspaceOauthClient,  useConnectSourceBatch,  useConnectSource,  useCreateWorkspaceOauthClient,  useCreateSecret,  useDiscoverSource,  useInvalidateExecutorQueries,  useInstanceConfig,  useRefreshSecrets,  useSecrets,  useStartSourceOAuth,  useWorkspaceOauthClients,} from "@executor/react";import { Badge } from "../components/ui/badge";import { Button } from "../components/ui/button";import { LocalMcpInstallCard } from "../components/local-mcp-install-card";import { SourceFavicon } from "../components/source-favicon";import {  IconArrowLeft,  IconCheck,  IconDiscover,  IconPlus,  IconSpinner,} from "../components/icons";import { cn } from "../lib/utils";import { sourceTemplates, type SourceTemplate } from "./source-templates";import { getDomain } from "tldts"; // ---------------------------------------------------------------------------// State machine// --------------------------------------------------------------------------- type FlowPhase =  | "idle"  | "discovering"  | "editing"  | "connecting"  | "connected"  | "credential_required"  | "oauth_required"; type ProbeAuthKind = "none" | "bearer" | "basic" | "headers"; type ProbeAuthState = {  kind: ProbeAuthKind;  token: string;  headerName: string;  prefix: string;  username: string;  password: string;  headersText: string;}; type ConnectFormState = {  kind: "mcp" | "openapi" | "graphql" | "google_discovery";  endpoint: string;  specUrl: string;  service: string;  version: string;  discoveryUrl: string;  name: string;  namespace: string;  transport: "" | "auto" | "streamable-http" | "sse";  authKind: "none" | "bearer" | "oauth2";  authHeaderName: string;  authPrefix: string;  bearerToken: string;  bearerProviderId: string;  bearerHandle: string;  workspaceOauthClientId: string;  oauthClientId: string;  oauthClientSecret: string;  queryParamsText: string;  headersText: string;}; type OAuthRequiredInfo = {  source: Source;  sessionId: string;  authorizationUrl: string;}; type BatchOAuthRequiredInfo = {  sessionId: string;  authorizationUrl: string;  sourceIds: ReadonlyArray<string>;}; // ---------------------------------------------------------------------------// Constants// --------------------------------------------------------------------------- const kindOptions: ReadonlyArray<ConnectFormState["kind"]> = [  "mcp",  "openapi",  "graphql",  "google_discovery",]; const transportOptions: ReadonlyArray<"auto" | "streamable-http" | "sse"> = [  "auto",  "streamable-http",  "sse",]; const authOptions: ReadonlyArray<ConnectFormState["authKind"]> = ["none", "bearer", "oauth2"]; const probeAuthOptions: ReadonlyArray<ProbeAuthKind> = ["none", "bearer", "basic", "headers"]; const SOURCE_OAUTH_POPUP_RESULT_TIMEOUT_MS = 2 * 60_000;const SOURCE_OAUTH_POPUP_RESULT_STORAGE_KEY_PREFIX = "executor:oauth-result:"; // ---------------------------------------------------------------------------// Helpers// --------------------------------------------------------------------------- const trimToNull = (value: string): string | null => {  const trimmed = value.trim();  return trimmed.length > 0 ? trimmed : null;}; const namespaceFromUrl = (url: string): string => {  try {    const domain = getDomain(url);    if (!domain) return "";    // Strip the TLD: "github.com" -> "github", "linear.app" -> "linear"    const dot = domain.indexOf(".");    return dot > 0 ? domain.slice(0, dot) : domain;  } catch {    return "";  }}; const googleDiscoveryNamespace = (service: string): string =>  `google.${service.trim().toLowerCase().replace(/[^a-z0-9]+/g, ".").replace(/^\.+|\.+$/g, "")}`; const googleDiscoveryDefaultsFromUrl = (value: string): {  service: string;  version: string;  discoveryUrl: string;} | null => {  try {    const url = new URL(value);    const byDirectory = url.pathname.match(/^\/discovery\/v1\/apis\/([^/]+)\/([^/]+)\/rest$/);    if (byDirectory) {      return {        service: decodeURIComponent(byDirectory[1] ?? ""),        version: decodeURIComponent(byDirectory[2] ?? ""),        discoveryUrl: url.toString(),      };    }     const versionParam = url.searchParams.get("version");    const version = versionParam ? trimToNull(versionParam) : null;    const isHostScopedDiscovery = url.pathname === "/$discovery/rest"      && url.hostname.endsWith(".googleapis.com")      && url.hostname !== "www.googleapis.com";    if (version && isHostScopedDiscovery) {      return {        service: url.hostname.split(".")[0] ?? "",        version,        discoveryUrl: url.toString(),      };    }     return null;  } catch {    return null;  }}; const parseJsonStringMap = (label: string, text: string): Record<string, string> | null => {  const trimmed = text.trim();  if (trimmed.length === 0) {    return null;  }   let parsed: unknown;  try {    parsed = JSON.parse(trimmed);  } catch {    throw new Error(`${label} must be valid JSON.`);  }   if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {    throw new Error(`${label} must be a JSON object with string values.`);  }   const entries = Object.entries(parsed as Record<string, unknown>);  const normalized: Record<string, string> = {};  for (const [key, value] of entries) {    if (typeof value !== "string") {      throw new Error(`${label} must only contain string values.`);    }    normalized[key] = value;  }   return Object.keys(normalized).length === 0 ? null : normalized;}; // ---------------------------------------------------------------------------// Derived defaults from discovery result// --------------------------------------------------------------------------- const defaultConnectForm = (discovery?: SourceDiscoveryResult): ConnectFormState => {  if (!discovery || discovery.detectedKind === "unknown") {    return {      kind: "openapi",      endpoint: discovery?.endpoint ?? "",      specUrl: discovery?.specUrl ?? "",      service: "",      version: "",      discoveryUrl: "",      name: discovery?.name ?? "",      namespace: discovery?.namespace || namespaceFromUrl(discovery?.endpoint ?? ""),      transport: "",      authKind: "none",      authHeaderName: "Authorization",      authPrefix: "Bearer ",      bearerToken: "",      bearerProviderId: "",      bearerHandle: "",      workspaceOauthClientId: "",      oauthClientId: "",      oauthClientSecret: "",      queryParamsText: "",      headersText: "",    };  }   const kind = discovery.detectedKind as ConnectFormState["kind"];  const auth = discovery.authInference;   // Map auth suggestion to what backend connect supports  let authKind: ConnectFormState["authKind"] = "none";  let authHeaderName = "Authorization";  let authPrefix = "Bearer ";   if (auth.supported) {    if (auth.suggestedKind === "bearer" || auth.suggestedKind === "apiKey") {      authKind = "bearer";      authHeaderName = auth.headerName ?? "Authorization";      authPrefix = auth.prefix ?? "Bearer ";    } else if (auth.suggestedKind === "oauth2") {      authKind = "oauth2";      authHeaderName = auth.headerName ?? "Authorization";      authPrefix = auth.prefix ?? "Bearer ";    } else if (auth.suggestedKind === "basic") {      // Backend connect doesn't support basic auth natively; map to bearer      authKind = "bearer";      authHeaderName = "Authorization";      authPrefix = "Basic ";    }  }   const googleDiscoveryDefaults = kind === "google_discovery"    ? googleDiscoveryDefaultsFromUrl(discovery.specUrl ?? discovery.endpoint)    : null;   return {    kind,    endpoint: discovery.endpoint,    specUrl: discovery.specUrl ?? "",    service: googleDiscoveryDefaults?.service ?? "",    version: googleDiscoveryDefaults?.version ?? "",    discoveryUrl: googleDiscoveryDefaults?.discoveryUrl ?? "",    name: discovery.name ?? "",    namespace: discovery.namespace || namespaceFromUrl(discovery.endpoint),    transport: kind === "mcp" ? (discovery.transport ?? "auto") : "",    authKind,    authHeaderName,    authPrefix,    bearerToken: "",    bearerProviderId: "",    bearerHandle: "",    workspaceOauthClientId: "",    oauthClientId: "",    oauthClientSecret: "",    queryParamsText: "",    headersText: "",  };}; const connectFormFromTemplate = (template: SourceTemplate): ConnectFormState => ({  ...defaultConnectForm(),  kind: template.kind as ConnectFormState["kind"],  endpoint: template.endpoint,  specUrl: "specUrl" in template ? template.specUrl : "",  service: "service" in template ? template.service : "",  version: "version" in template ? template.version : "",  discoveryUrl: "discoveryUrl" in template ? template.discoveryUrl : "",  name: template.name,  namespace: "service" in template    ? googleDiscoveryNamespace(template.service)    : namespaceFromUrl(template.endpoint),  transport: template.kind === "mcp" ? "auto" : "",  authKind: template.kind === "google_discovery" ? "oauth2" : "none",  workspaceOauthClientId: "",}); const authKindForSourceKind = (  currentAuthKind: ConnectFormState["authKind"],  nextKind: ConnectFormState["kind"],): ConnectFormState["authKind"] =>  nextKind === "google_discovery" && currentAuthKind === "none"    ? "oauth2"    : currentAuthKind; const buildProbeAuth = (state: ProbeAuthState): DiscoverSourcePayload["probeAuth"] => {  if (state.kind === "none") return { kind: "none" };  if (state.kind === "bearer") {    if (!state.token.trim()) throw new Error("Token is required for bearer probe auth.");    return {      kind: "bearer",      headerName: trimToNull(state.headerName),      prefix: trimToNull(state.prefix),      token: state.token.trim(),    };  }  if (state.kind === "basic") {    if (!state.username.trim()) throw new Error("Username is required for basic probe auth.");    return {      kind: "basic",      username: state.username.trim(),      password: state.password,    };  }  // headers  const headers = parseJsonStringMap("Probe headers", state.headersText);  if (!headers) throw new Error("At least one header is required for headers probe auth.");  return { kind: "headers", headers };}; const buildConnectPayload = (form: ConnectFormState): ConnectSourcePayload => {  if (form.kind === "mcp") {    const endpoint = form.endpoint.trim();    if (!endpoint) throw new Error("Endpoint is required.");    return {      kind: "mcp",      endpoint,      name: trimToNull(form.name),      namespace: trimToNull(form.namespace),      transport: form.transport === "" ? "auto" : form.transport,      queryParams: parseJsonStringMap("Query params", form.queryParamsText),      headers: parseJsonStringMap("Request headers", form.headersText),    };  }   // Build HTTP auth for openapi/graphql  const auth = buildHttpAuth(form);   if (form.kind === "openapi") {    const endpoint = form.endpoint.trim();    if (!endpoint) throw new Error("Endpoint is required.");    const specUrl = form.specUrl.trim();    if (!specUrl) throw new Error("OpenAPI sources require a spec URL.");    return {      kind: "openapi",      endpoint,      specUrl,      name: trimToNull(form.name),      namespace: trimToNull(form.namespace),      auth,    };  }   if (form.kind === "google_discovery") {    const service = form.service.trim();    const version = form.version.trim();    if (!service) throw new Error("Google Discovery sources require a service name.");    if (!version) throw new Error("Google Discovery sources require a version.");    if (      form.authKind === "oauth2"      && form.workspaceOauthClientId.trim().length === 0      && form.oauthClientId.trim().length === 0    ) {      throw new Error("Google OAuth requires a workspace OAuth client or a new client ID.");    }    return {      kind: "google_discovery",      service,      version,      discoveryUrl: trimToNull(form.discoveryUrl),      name: trimToNull(form.name),      namespace: trimToNull(form.namespace),      workspaceOauthClientId:        form.authKind === "oauth2" && form.workspaceOauthClientId.trim().length > 0          ? form.workspaceOauthClientId.trim()          : undefined,      oauthClient:        form.authKind === "oauth2" && form.workspaceOauthClientId.trim().length === 0          ? {              clientId: form.oauthClientId.trim(),              clientSecret: trimToNull(form.oauthClientSecret),            }          : null,      auth,    };  }   const endpoint = form.endpoint.trim();  if (!endpoint) throw new Error("Endpoint is required.");  return {    kind: "graphql",    endpoint,    name: trimToNull(form.name),    namespace: trimToNull(form.namespace),    auth,  };}; const buildHttpAuth = (  form: ConnectFormState,): { kind: "none" } | { kind: "bearer"; headerName?: string | null; prefix?: string | null; token?: string | null; tokenRef?: { providerId: string; handle: string } | null } | undefined => {  if (form.authKind === "none") return { kind: "none" };   if (form.authKind === "bearer") {    const headerName = trimToNull(form.authHeaderName);    const prefix = form.authPrefix.length === 0 ? null : form.authPrefix;     // Prefer secret ref if set    if (form.bearerProviderId.trim() && form.bearerHandle.trim()) {      return {        kind: "bearer",        headerName,        prefix,        tokenRef: {          providerId: form.bearerProviderId.trim(),          handle: form.bearerHandle.trim(),        },      };    }     // Fall back to inline token    if (form.bearerToken.trim()) {      return {        kind: "bearer",        headerName,        prefix,        token: form.bearerToken.trim(),      };    }     throw new Error("Bearer auth requires a token. Select or create a secret, or enter a token directly.");  }   // oauth2 is handled via the connect result flow, not pre-filled  return undefined;}; // ---------------------------------------------------------------------------// OAuth popup helpers (shared with source-editor.tsx)// --------------------------------------------------------------------------- type SourceOAuthPopupMessage =  | {      type: "executor:oauth-result";      ok: true;      sessionId: string;      auth: CompleteSourceOAuthResult["auth"];    }  | {      type: "executor:oauth-result";      ok: false;      sessionId: string | null;      error: string;    }  | {      type: "executor:source-oauth-result";      ok: true;      sourceId: string;    }  | {      type: "executor:source-oauth-result";      ok: false;      error: string;    }; const readStoredSourceOAuthPopupResult = (sessionId: string): SourceOAuthPopupMessage | null => {  if (typeof window === "undefined") return null;  const raw = window.localStorage.getItem(    `${SOURCE_OAUTH_POPUP_RESULT_STORAGE_KEY_PREFIX}${sessionId}`,  );  if (!raw) return null;  try {

Lines 1–500 of 2069.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t