628 lines · 22.5 KB · TypeScript
RawDownload
import { Effect } from "effect";import type { Layer } from "effect";import { HttpClient } from "effect/unstable/http"; import {  IntegrationAlreadyExistsError,  IntegrationDetectionResult,  IntegrationNotFoundError,  IntegrationSlug,  definePlugin,  mergeAuthTemplates,  sha256Hex,  type AuthMethodDescriptor,  type Integration,  type IntegrationConfig,  type IntegrationRecord,  type PluginCtx,  type StorageFailure,} from "@executor-js/sdk/core";import { describeApiKeyAuthMethod } from "@executor-js/sdk/http-auth";import {  checkHealthOpenApi,  compileAndPersistOpenApiSpecStreaming,  listHealthCheckCandidatesOpenApi,  decodeOpenApiIntegrationConfig,  invokeOpenApiBackedTool,  makeDefaultOpenapiStore,  normalizeOpenApiAuthInputs,  OpenApiExtractionError,  OpenApiParseError,  resolveOpenApiBackedAnnotations,  resolveOpenApiBackedTools,  type Authentication,  type AuthenticationInput,  type OpenApiPersistResult,  type OpenapiStore,} from "@executor-js/plugin-openapi"; import {  buildMicrosoftGraphOpenApiSpec,  decodeMicrosoftGraphIntegrationConfig,  microsoftGraphKeepPathItem,  type MicrosoftGraphSelectionInput,  type MicrosoftGraphUrlPolicy,  type MicrosoftGraphIntegrationConfig,  type MicrosoftGraphSpecBuild,} from "./graph";import {  MICROSOFT_CLIENT_CREDENTIALS_AUTH_TEMPLATE_SLUG,  MICROSOFT_GRAPH_BASE_URL,  microsoftGraphPreset,  microsoftGraphPresetForId,  microsoftServiceSlug,} from "./presets"; export const MICROSOFT_CUSTOM_WORKLOAD_ID = "custom"; export interface MicrosoftPresetWorkloadConfig {  readonly presetId: string;  readonly slug?: string;  readonly name?: string;} export interface MicrosoftCustomWorkloadConfig {  readonly custom: {    readonly customScopes: readonly string[];    readonly slug?: string;    readonly name: string;    readonly description?: string;    readonly specUrl?: string;    readonly authorizationUrl?: string;    readonly tokenUrl?: string;    readonly clientCredentialsTokenUrl?: string;  };} export type MicrosoftWorkloadConfig = MicrosoftPresetWorkloadConfig | MicrosoftCustomWorkloadConfig; export interface MicrosoftAddWorkloadsInput {  readonly workloads: readonly MicrosoftWorkloadConfig[];  readonly baseUrl?: string;} export interface MicrosoftAddWorkloadsAdded {  readonly slug: IntegrationSlug;  readonly presetId: string;  readonly toolCount: number;} export interface MicrosoftAddWorkloadsSkipped {  readonly slug: IntegrationSlug;  readonly presetId: string;  readonly reason: "already_exists";} export interface MicrosoftAddWorkloadsFailed {  readonly slug: IntegrationSlug;  readonly presetId: string;  readonly error: string;} export interface MicrosoftAddWorkloadsResult {  readonly added: readonly MicrosoftAddWorkloadsAdded[];  readonly skipped: readonly MicrosoftAddWorkloadsSkipped[];  readonly failed: readonly MicrosoftAddWorkloadsFailed[];} export interface MicrosoftConfigureInput {  readonly authenticationTemplate: readonly AuthenticationInput[];  readonly mode?: "merge" | "replace";} export interface MicrosoftUpdateInput {  readonly presetIds?: readonly string[];  readonly customScopes?: readonly string[];  readonly baseUrl?: string;  readonly specUrl?: string;  readonly authorizationUrl?: string;  readonly tokenUrl?: string;  readonly clientCredentialsTokenUrl?: string;} export interface MicrosoftUpdateResult {  readonly slug: IntegrationSlug;  readonly toolCount: number;  readonly addedTools: readonly string[];  readonly removedTools: readonly string[];} export interface MicrosoftPluginOptions {  readonly httpClientLayer?: Layer.Layer<HttpClient.HttpClient, never, never>;  /**   * Allows custom workload entries to point spec/base/OAuth URLs at a trusted   * https host other than the pinned Microsoft Graph endpoints, or at plain   * http on loopback (local Graph emulators). Off by default; hosts wire this   * to their own local-network dev posture (e.g. `allowLocalNetwork`), never   * in production.   */  readonly allowUnsafeUrlOverrides?: boolean;} const DEFAULT_MICROSOFT_SLUG = "microsoft_graph"; const isMicrosoftPresetWorkloadConfig = (  workload: MicrosoftWorkloadConfig,): workload is MicrosoftPresetWorkloadConfig => "presetId" in workload; const microsoftWorkloadEntryId = (workload: MicrosoftWorkloadConfig): string =>  isMicrosoftPresetWorkloadConfig(workload) ? workload.presetId : MICROSOFT_CUSTOM_WORKLOAD_ID; const microsoftWorkloadEntrySlug = (workload: MicrosoftWorkloadConfig): IntegrationSlug =>  isMicrosoftPresetWorkloadConfig(workload)    ? IntegrationSlug.make(workload.slug?.trim() || microsoftServiceSlug(workload.presetId))    : IntegrationSlug.make(workload.custom.slug?.trim() || "microsoft_graph_custom"); type MicrosoftAddWorkloadOutcome = {  readonly added: readonly MicrosoftAddWorkloadsAdded[];  readonly skipped: readonly MicrosoftAddWorkloadsSkipped[];  readonly failed: readonly MicrosoftAddWorkloadsFailed[];}; const microsoftAddWorkloadFailure = (  workload: MicrosoftWorkloadConfig,  slug: IntegrationSlug,  error: string,): MicrosoftAddWorkloadOutcome => ({  added: [],  skipped: [],  failed: [{ slug, presetId: microsoftWorkloadEntryId(workload), error }],}); const describeMicrosoftAuthMethods = (  record: IntegrationRecord,): readonly AuthMethodDescriptor[] => {  const config = decodeOpenApiIntegrationConfig(record.config);  if (!config) return [];  return (config.authenticationTemplate ?? []).map(    (template: Authentication): AuthMethodDescriptor => {      if (template.kind === "oauth2") {        const machineFlow =          String(template.slug) === MICROSOFT_CLIENT_CREDENTIALS_AUTH_TEMPLATE_SLUG;        return {          id: String(template.slug),          label: machineFlow ? "Microsoft OAuth (client credentials)" : "Microsoft OAuth",          kind: "oauth",          template: String(template.slug),          oauth: {            authorizationUrl: template.authorizationUrl,            tokenUrl: template.tokenUrl,            scopes: template.scopes,          },        };      }      return describeApiKeyAuthMethod(template);    },  );}; const describeMicrosoftIntegrationDisplay = (  record: IntegrationRecord,): { readonly url?: string } => {  const config = decodeMicrosoftGraphIntegrationConfig(record.config);  return { url: config?.baseUrl ?? MICROSOFT_GRAPH_BASE_URL };}; const makeMicrosoftPluginExtension = (  ctx: PluginCtx<OpenapiStore>,  httpClientLayer: Layer.Layer<HttpClient.HttpClient, never, never>,  urlPolicy?: MicrosoftGraphUrlPolicy,) => {  const persistGraphOperations = (    graph: MicrosoftGraphSpecBuild,    integration: string,    specHash: string,  ): Effect.Effect<OpenApiPersistResult, OpenApiExtractionError | StorageFailure> =>    // Stream the (full 37MB) source straight to persisted bindings + a    // content-addressed defs blob, never materializing the whole-document tree    // that OOMs the 128MB Workers isolate. `keepPathItem` applies the Microsoft    // Graph scope selection per path-item during the stream; a full-graph    // selection returns `undefined` (keep everything).    compileAndPersistOpenApiSpecStreaming({      specText: graph.specText,      integration,      storage: ctx.storage,      specHash,      keepPathItem: microsoftGraphKeepPathItem(graph),    });   const addMicrosoftOpenApiIntegration = (input: {    readonly selection: MicrosoftGraphSelectionInput;    readonly slug: IntegrationSlug;    readonly name: string;    readonly description: string;    readonly baseUrl?: string;  }) =>    Effect.gen(function* () {      const graph = yield* buildMicrosoftGraphOpenApiSpec(        input.selection,        httpClientLayer,        urlPolicy,      );      const slug = input.slug;       const existing = yield* ctx.core.integrations.get(slug);      if (existing) {        return yield* new IntegrationAlreadyExistsError({ slug });      }       const specHash = yield* sha256Hex(graph.specText);      const integrationConfig: MicrosoftGraphIntegrationConfig = {        specHash,        sourceUrl: graph.specUrl,        microsoftGraphPresetIds: graph.presetIds,        microsoftGraphCustomScopes: graph.customScopes,        microsoftGraphScopes: graph.scopes,        microsoftGraphExactPaths: graph.exactPaths,        microsoftGraphPathPrefixes: graph.pathPrefixes,        microsoftGraphTagPrefixes: graph.tagPrefixes,        microsoftGraphCoversFullGraph: graph.coversFullGraph,        microsoftGraphAuthorizationUrl: graph.authorizationUrl,        microsoftGraphTokenUrl: graph.tokenUrl,        microsoftGraphClientCredentialsTokenUrl: graph.clientCredentialsTokenUrl,        authenticationTemplate: graph.authenticationTemplate,        ...(input.baseUrl ? { baseUrl: input.baseUrl } : {}),      };       yield* ctx.storage.putSpec(specHash, graph.specText);       const persisted = yield* ctx.transaction(        Effect.gen(function* () {          yield* ctx.core.integrations.register({            slug,            name: input.name,            description: input.description,            config:              integrationConfig satisfies MicrosoftGraphIntegrationConfig as IntegrationConfig,            canRemove: true,            canRefresh: true,          });          return yield* persistGraphOperations(graph, String(slug), specHash);        }),      );       // Default the health check to `GET /me` (the canonical Graph identity      // endpoint) when the selected workloads include it, so connections report      // alive/expired + who-am-I out of the box. The operations were streamed      // to storage above, so find the binding there. Best-effort: a Graph      // selection without /me just leaves the check unconfigured (the editor      // remains available).      const meOperation = (yield* ctx.storage.listOperations(String(slug))).find(        (op) => op.binding.method.toLowerCase() === "get" && op.binding.pathTemplate === "/me",      );      if (meOperation) {        yield* ctx.core.integrations.setHealthCheck(slug, {          operation: meOperation.toolName,          identityField: "userPrincipalName",        });      }       return { slug, toolCount: persisted.toolCount };    });   const addOneWorkload = (workload: MicrosoftWorkloadConfig, baseUrl?: string) =>    Effect.gen(function* () {      if (!isMicrosoftPresetWorkloadConfig(workload)) {        if (workload.custom.customScopes.length === 0) {          return yield* new OpenApiParseError({            message: "Custom Microsoft Graph workload requires at least one scope",          });        }        return yield* addMicrosoftOpenApiIntegration({          selection: {            presetIds: [],            customScopes: workload.custom.customScopes,            ...(baseUrl ? { baseUrl } : {}),            ...(workload.custom.specUrl ? { specUrl: workload.custom.specUrl } : {}),            ...(workload.custom.authorizationUrl              ? { authorizationUrl: workload.custom.authorizationUrl }              : {}),            ...(workload.custom.tokenUrl ? { tokenUrl: workload.custom.tokenUrl } : {}),            ...(workload.custom.clientCredentialsTokenUrl              ? { clientCredentialsTokenUrl: workload.custom.clientCredentialsTokenUrl }              : {}),          },          slug: microsoftWorkloadEntrySlug(workload),          name: workload.custom.name.trim() || "Custom Microsoft Graph",          description: workload.custom.description ?? "Custom Microsoft Graph scopes.",          baseUrl,        });      }       const preset = microsoftGraphPresetForId(workload.presetId);      if (!preset) {        return yield* new OpenApiParseError({          message: `Microsoft Graph workload preset is not available: ${workload.presetId}`,        });      }       // A per-workload integration filters the Graph tree to just this one      // preset. The bare GET /me identity keep is seeded by the shared      // keepPathItem filter, so every workload source gets its own /me health      // check even when the profile workload is not selected. Scopes are this      // preset's scopes plus the identity scope (User.Read), derived by      // `microsoftGraphScopesForPresetIds` inside the spec build.      return yield* addMicrosoftOpenApiIntegration({        selection: { presetIds: [preset.id] },        slug: IntegrationSlug.make(workload.slug?.trim() || microsoftServiceSlug(preset.id)),        name: workload.name?.trim() || preset.name,        description: preset.summary,        baseUrl,      });    });   const addWorkloads = (input: MicrosoftAddWorkloadsInput) =>    Effect.gen(function* () {      // Strict {concurrency: 1}: parsing the 37MB Graph tree already strains the      // 128MB Workers isolate, so two workload adds in flight recreate the OOM      // in aggregate. Each add must fully settle before the next begins.      const outcomes = yield* Effect.forEach(        input.workloads,        (workload): Effect.Effect<MicrosoftAddWorkloadOutcome, never> => {          const slug = microsoftWorkloadEntrySlug(workload);          const presetId = microsoftWorkloadEntryId(workload);          return addOneWorkload(workload, input.baseUrl).pipe(            Effect.map(              (result): MicrosoftAddWorkloadOutcome => ({                added: [                  {                    slug: result.slug,                    presetId,                    toolCount: result.toolCount,                  },                ],                skipped: [],                failed: [],              }),            ),            Effect.catchTag("IntegrationAlreadyExistsError", () =>              Effect.succeed({                added: [],                skipped: [                  {                    slug,                    presetId,                    reason: "already_exists" as const,                  },                ],                failed: [],              }),            ),            Effect.catchTags({              OpenApiParseError: (error: OpenApiParseError) =>                Effect.succeed(microsoftAddWorkloadFailure(workload, slug, error.message)),              OpenApiExtractionError: (error: OpenApiExtractionError) =>                Effect.succeed(microsoftAddWorkloadFailure(workload, slug, error.message)),            }),            Effect.catch(() =>              Effect.succeed(                microsoftAddWorkloadFailure(workload, slug, "Failed to add Microsoft workload"),              ),            ),          );        },        { concurrency: 1 },      );       return {        added: outcomes.flatMap((outcome) => outcome.added),        skipped: outcomes.flatMap((outcome) => outcome.skipped),        failed: outcomes.flatMap((outcome) => outcome.failed),      };    });   const updateGraph = (rawSlug: string, input?: MicrosoftUpdateInput) =>    Effect.gen(function* () {      const slug = IntegrationSlug.make(rawSlug);      const record = yield* ctx.core.integrations.get(slug);      const current = record ? decodeMicrosoftGraphIntegrationConfig(record.config) : null;      if (!record || !current) {        return yield* new IntegrationNotFoundError({ slug });      }       const graph = yield* buildMicrosoftGraphOpenApiSpec(        {          presetIds: input?.presetIds ?? current.microsoftGraphPresetIds,          customScopes: input?.customScopes ?? current.microsoftGraphCustomScopes,          baseUrl: input?.baseUrl ?? current.baseUrl,          specUrl: input?.specUrl ?? current.sourceUrl,          authorizationUrl: input?.authorizationUrl ?? current.microsoftGraphAuthorizationUrl,          tokenUrl: input?.tokenUrl ?? current.microsoftGraphTokenUrl,          clientCredentialsTokenUrl:            input?.clientCredentialsTokenUrl ?? current.microsoftGraphClientCredentialsTokenUrl,        },        httpClientLayer,        urlPolicy,      );      const previousOperations = yield* ctx.storage.listOperations(rawSlug);      const previousNames = new Set(previousOperations.map((op) => op.toolName));       const specHash = yield* sha256Hex(graph.specText);      yield* ctx.storage.putSpec(specHash, graph.specText);       const nextConfig: MicrosoftGraphIntegrationConfig = {        ...current,        specHash,        sourceUrl: graph.specUrl,        microsoftGraphPresetIds: graph.presetIds,        microsoftGraphCustomScopes: graph.customScopes,        microsoftGraphScopes: graph.scopes,        microsoftGraphExactPaths: graph.exactPaths,        microsoftGraphPathPrefixes: graph.pathPrefixes,        microsoftGraphTagPrefixes: graph.tagPrefixes,        microsoftGraphCoversFullGraph: graph.coversFullGraph,        microsoftGraphAuthorizationUrl: graph.authorizationUrl,        microsoftGraphTokenUrl: graph.tokenUrl,        microsoftGraphClientCredentialsTokenUrl: graph.clientCredentialsTokenUrl,        authenticationTemplate: graph.authenticationTemplate,        ...(input?.baseUrl ? { baseUrl: input.baseUrl } : {}),      };       const persisted = yield* ctx.transaction(        Effect.gen(function* () {          yield* ctx.core.integrations.update(slug, {            config: nextConfig satisfies MicrosoftGraphIntegrationConfig as IntegrationConfig,          });          return yield* persistGraphOperations(graph, rawSlug, specHash);        }),      );      const nextNames = new Set(persisted.toolNames);       const connections = yield* ctx.connections.list({ integration: slug });      yield* Effect.forEach(        connections,        (connection) =>          ctx.connections            .refresh({              owner: connection.owner,              integration: connection.integration,              name: connection.name,            })            .pipe(Effect.catchTag("ConnectionNotFoundError", () => Effect.succeed([]))),        { discard: true },      ).pipe(Effect.catchTag("IntegrationNotFoundError", () => Effect.void));       return {        slug,        toolCount: persisted.toolCount,        addedTools: [...nextNames].filter((name) => !previousNames.has(name)).sort(),        removedTools: [...previousNames].filter((name) => !nextNames.has(name)).sort(),      };    });   return {    addWorkloads,    updateGraph,    removeGraph: (slug: string) =>      ctx.transaction(        Effect.gen(function* () {          yield* ctx.storage.removeOperations(slug);          yield* ctx.core.integrations

Lines 1–500 of 628.