825 lines · 30.4 KB · TypeScript
RawDownload
import { Effect, Option, Predicate, Schema } from "effect"; import {  ScopeId,  SourceDetectionResult,  ToolResult,  Usage,  defaultSourceInstallScopeId,  definePlugin,  tool,  resolveSecretBackedMap,  type PluginCtx,  type StaticToolSchema,  type StorageFailure,  type ToolAnnotations,} from "@executor-js/sdk/core"; import {  googleDiscoverySchema,  makeGoogleDiscoveryStore,  type GoogleDiscoveryStore,} from "./binding-store";import { googleDiscoveryPresets } from "./presets";import { extractGoogleDiscoveryManifest } from "./document";import { annotationsForOperation, invokeGoogleDiscoveryTool } from "./invoke";import { GoogleDiscoveryParseError, GoogleDiscoverySourceError } from "./errors";import {  GoogleDiscoveryAuth,  GoogleDiscoveryFetchCredentials,  GoogleDiscoveryStoredSourceData as GoogleDiscoveryStoredSourceDataSchema,  type GoogleDiscoveryManifest,  type GoogleDiscoveryManifestMethod,  type GoogleDiscoveryMethodBinding,} from "./types";import type { GoogleDiscoveryStoredSourceData } from "./types"; // ---------------------------------------------------------------------------// Upstream-error message extraction// --------------------------------------------------------------------------- const GOOGLE_BODY_CAP = 1024;const UpstreamMessageBody = Schema.Struct({ message: Schema.String });const UpstreamErrorMessageBody = Schema.Struct({ errorMessage: Schema.String });const UpstreamNestedErrorBody = Schema.Struct({ error: UpstreamMessageBody });const UpstreamErrorsArrayBody = Schema.Struct({  errors: Schema.Array(    Schema.Struct({      detail: Schema.optional(Schema.String),      message: Schema.optional(Schema.String),      title: Schema.optional(Schema.String),    }),  ),}); const decodeUpstreamMessageBody = Schema.decodeUnknownOption(UpstreamMessageBody);const decodeUpstreamErrorMessageBody = Schema.decodeUnknownOption(UpstreamErrorMessageBody);const decodeUpstreamNestedErrorBody = Schema.decodeUnknownOption(UpstreamNestedErrorBody);const decodeUpstreamErrorsArrayBody = Schema.decodeUnknownOption(UpstreamErrorsArrayBody); const googleClampedStringify = (value: unknown): string => {  let s: string;  // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: JSON.stringify may throw on cycles; fall back to String() so the upstream body can still be surfaced as ToolError.details fallback text  try {    s = JSON.stringify(value);  } catch {    s = String(value);  }  return s.length > GOOGLE_BODY_CAP ? `${s.slice(0, GOOGLE_BODY_CAP)}…` : s;}; const firstNonEmpty = (...values: readonly (string | undefined)[]): string | undefined =>  values.find((value) => value !== undefined && value.length > 0); const googleExtractUpstreamMessage = (body: unknown, status: number): string => {  if (typeof body === "string") {    return body.length > 0 ? body : `Upstream returned HTTP ${status}`;  }  const nested = Option.getOrUndefined(decodeUpstreamNestedErrorBody(body));  const messageBody = Option.getOrUndefined(decodeUpstreamMessageBody(body));  const errorMessageBody = Option.getOrUndefined(decodeUpstreamErrorMessageBody(body));  const errorsBody = Option.getOrUndefined(decodeUpstreamErrorsArrayBody(body));  const arrayMessage = errorsBody?.errors    .map(({ detail, message: upstreamMessage, title }) =>      firstNonEmpty(detail, upstreamMessage, title),    )    .find((message) => message !== undefined);  const message = firstNonEmpty(    nested?.error.message,    messageBody?.message,    errorMessageBody?.errorMessage,    arrayMessage,  );  if (message !== undefined) return message;  if (body !== null && typeof body === "object") {    return googleClampedStringify(body);  }  return `Upstream returned HTTP ${status}`;}; // ---------------------------------------------------------------------------// Public input / output shapes// --------------------------------------------------------------------------- export interface GoogleDiscoveryProbeOperation {  readonly toolPath: string;  readonly method: string;  readonly pathTemplate: string;  readonly description: string | null;} export interface GoogleDiscoveryProbeResult {  readonly name: string;  readonly title: string | null;  readonly service: string;  readonly version: string;  readonly toolCount: number;  readonly scopes: readonly string[];  readonly operations: readonly GoogleDiscoveryProbeOperation[];} export interface GoogleDiscoveryUpdateSourceInput {  readonly name?: string;  /** Rewrite the source's auth — typically after a successful   *  re-authenticate, to point at a freshly minted Connection. */  readonly auth?: GoogleDiscoveryAuth;} const GoogleDiscoveryProbeInputSchema = Schema.Struct({  discoveryUrl: Schema.String,  credentials: Schema.optional(GoogleDiscoveryFetchCredentials),}); const GoogleDiscoveryProbeOutputSchema = Schema.Struct({  name: Schema.String,  title: Schema.NullOr(Schema.String),  service: Schema.String,  version: Schema.String,  toolCount: Schema.Number,  scopes: Schema.Array(Schema.String),  operations: Schema.Array(    Schema.Struct({      toolPath: Schema.String,      method: Schema.String,      pathTemplate: Schema.String,      description: Schema.NullOr(Schema.String),    }),  ),}); const GoogleDiscoveryAddSourceInputSchema = Schema.Struct({  name: Schema.String,  scope: Schema.String,  discoveryUrl: Schema.String,  credentials: Schema.optional(GoogleDiscoveryFetchCredentials),  namespace: Schema.optional(Schema.String),  auth: GoogleDiscoveryAuth,});const GoogleDiscoveryStaticAddSourceInputSchema = Schema.Struct({  name: Schema.String,  discoveryUrl: Schema.String,  credentials: Schema.optional(GoogleDiscoveryFetchCredentials),  namespace: Schema.optional(Schema.String),  auth: GoogleDiscoveryAuth,});export type GoogleDiscoveryProbeInput = typeof GoogleDiscoveryProbeInputSchema.Type;export type GoogleDiscoveryAddSourceInput = typeof GoogleDiscoveryAddSourceInputSchema.Type; const GoogleDiscoveryAddSourceOutputSchema = Schema.Struct({  namespace: Schema.String,  source: Schema.Struct({    id: Schema.String,    scope: Schema.String,  }),  toolCount: Schema.Number,}); const GoogleDiscoveryGetSourceInputSchema = Schema.Struct({  namespace: Schema.String,  scope: Schema.String,}); const GoogleDiscoveryGetSourceOutputSchema = Schema.Struct({  source: Schema.NullOr(Schema.Unknown),}); const GoogleDiscoveryConfigureInputSchema = Schema.Struct({  name: Schema.optional(Schema.String),  auth: Schema.optional(GoogleDiscoveryAuth),});const GoogleDiscoveryConfigureSourceInputSchema = Schema.Struct({  source: Schema.Struct({    id: Schema.String,    scope: Schema.String,  }),  ...GoogleDiscoveryConfigureInputSchema.fields,});const GoogleDiscoveryConfigureSourceOutputSchema = Schema.Struct({  configured: Schema.Boolean,}); const schemaToStaticToolSchema = <A, I>(schema: Schema.Decoder<A, I>): StaticToolSchema<A, I> =>  Schema.toStandardSchemaV1(Schema.toStandardJSONSchemaV1(schema) as never) as StaticToolSchema<    A,    I  >; const GoogleDiscoveryProbeInputStandardSchema = schemaToStaticToolSchema(  GoogleDiscoveryProbeInputSchema,);const GoogleDiscoveryProbeOutputStandardSchema = schemaToStaticToolSchema(  GoogleDiscoveryProbeOutputSchema,);const GoogleDiscoveryAddSourceInputStandardSchema = schemaToStaticToolSchema(  GoogleDiscoveryStaticAddSourceInputSchema,);const GoogleDiscoveryAddSourceOutputStandardSchema = schemaToStaticToolSchema(  GoogleDiscoveryAddSourceOutputSchema,);const GoogleDiscoveryGetSourceInputStandardSchema = schemaToStaticToolSchema(  GoogleDiscoveryGetSourceInputSchema,);const GoogleDiscoveryGetSourceOutputStandardSchema = schemaToStaticToolSchema(  GoogleDiscoveryGetSourceOutputSchema,);const GoogleDiscoveryConfigureSourceInputStandardSchema = schemaToStaticToolSchema(  GoogleDiscoveryConfigureSourceInputSchema,);const GoogleDiscoveryConfigureSourceOutputStandardSchema = schemaToStaticToolSchema(  GoogleDiscoveryConfigureSourceOutputSchema,); const resolveStaticScopeInput = (  ctx: { readonly scopes: readonly { readonly id: ScopeId; readonly name: string }[] },  value: string,): string =>  String(    ctx.scopes.find((scope) => scope.name === value || String(scope.id) === value)?.id ?? value,  ); /** * Errors any Google Discovery extension method may surface. */export type GoogleDiscoveryExtensionFailure =  | GoogleDiscoveryParseError  | GoogleDiscoverySourceError  | StorageFailure; // ---------------------------------------------------------------------------// URL normalization + slug helpers (unchanged)// --------------------------------------------------------------------------- const DISCOVERY_SERVICE_HOST = "https://www.googleapis.com/discovery/v1/apis";const decodeString = Schema.decodeUnknownSync(Schema.String);const isGoogleDiscoverySourceError = (error: unknown): error is GoogleDiscoverySourceError =>  Predicate.isTagged("GoogleDiscoverySourceError")(error); const normalizeDiscoveryUrl = (discoveryUrl: string): string => {  const trimmed = discoveryUrl.trim();  if (trimmed.length === 0) return trimmed;  if (!URL.canParse(trimmed)) return trimmed;  const parsed = new URL(trimmed);  if (parsed.pathname !== "/$discovery/rest") return trimmed;  const version = parsed.searchParams.get("version")?.trim();  if (!version) return trimmed;  const host = parsed.hostname.toLowerCase();  if (!host.endsWith(".googleapis.com")) return trimmed;  const rawService = host.slice(0, -".googleapis.com".length);  const service =    rawService === "calendar-json"      ? "calendar"      : rawService.endsWith("-json")        ? rawService.slice(0, -5)        : rawService;  if (!service) return trimmed;  return `${DISCOVERY_SERVICE_HOST}/${service}/${version}/rest`;}; const resolveGoogleDiscoveryCredentials = (  credentials: GoogleDiscoveryFetchCredentials | undefined,  ctx: PluginCtx<GoogleDiscoveryStore>,): Effect.Effect<  { headers?: Record<string, string>; queryParams?: Record<string, string> } | undefined,  GoogleDiscoverySourceError> =>  Effect.gen(function* () {    if (!credentials) return undefined;    const headers = yield* resolveSecretBackedMap({      values: credentials.headers,      getSecret: ctx.secrets.get,      onMissing: (name) =>        new GoogleDiscoverySourceError({          message: `Secret not found for header "${name}"`,        }),      onError: (_error, name) =>        new GoogleDiscoverySourceError({          message: `Secret not found for header "${name}"`,        }),    }).pipe(      Effect.mapError((err) =>        isGoogleDiscoverySourceError(err)          ? err          : new GoogleDiscoverySourceError({ message: "Secret resolution failed" }),      ),    );    const queryParams = yield* resolveSecretBackedMap({      values: credentials.queryParams,      getSecret: ctx.secrets.get,      onMissing: (name) =>        new GoogleDiscoverySourceError({          message: `Secret not found for query parameter "${name}"`,        }),      onError: (_error, name) =>        new GoogleDiscoverySourceError({          message: `Secret not found for query parameter "${name}"`,        }),    }).pipe(      Effect.mapError((err) =>        isGoogleDiscoverySourceError(err)          ? err          : new GoogleDiscoverySourceError({ message: "Secret resolution failed" }),      ),    );    return {      ...(headers ? { headers } : {}),      ...(queryParams ? { queryParams } : {}),    };  }); const fetchDiscoveryDocument = (  discoveryUrl: string,  credentials?: {    readonly headers?: Record<string, string>;    readonly queryParams?: Record<string, string>;  },) =>  Effect.gen(function* () {    const response = yield* Effect.tryPromise({      try: () => {        const url = new URL(normalizeDiscoveryUrl(discoveryUrl));        for (const [key, value] of Object.entries(credentials?.queryParams ?? {})) {          url.searchParams.set(key, value);        }        return fetch(url.toString(), {          headers: credentials?.headers,          signal: AbortSignal.timeout(20_000),        });      },      catch: () =>        new GoogleDiscoverySourceError({          message: "Google Discovery fetch failed",        }),    });    if (!response.ok) {      return yield* new GoogleDiscoverySourceError({        message: `Google Discovery fetch failed with status ${response.status}`,      });    }    return yield* Effect.tryPromise({      try: () => response.text(),      catch: () =>        new GoogleDiscoverySourceError({          message: "Google Discovery response body read failed",        }),    });  }); const normalizeSlug = (value: string): string =>  value    .toLowerCase()    .replace(/[^a-z0-9]+/g, "_")    .replace(/^_+|_+$/g, ""); const deriveNamespace = (input: { name: string; service: string; version: string }): string =>  normalizeSlug(    input.name || `google_${input.service}_${input.version.replace(/[^a-zA-Z0-9]+/g, "_")}`,  ) || `google_${input.service}`; // Connection refresh state is owned by the canonical `"oauth2"`// ConnectionProvider registered by core. `ctx.oauth.start` stamps the// Google-specific token endpoint + scopes onto the connection's// providerState at mint time — no plugin-owned schema needed. // ---------------------------------------------------------------------------// Register a parsed manifest against the executor core + plugin storage.// Runs inside a transaction.// --------------------------------------------------------------------------- const registerManifest = (  ctx: PluginCtx<GoogleDiscoveryStore>,  namespace: string,  scope: string,  manifest: GoogleDiscoveryManifest,  sourceData: GoogleDiscoveryStoredSourceData,) =>  Effect.gen(function* () {    yield* ctx.storage.removeBindingsBySource(namespace, scope);    yield* ctx.core.sources.unregister({ id: namespace, targetScope: scope }).pipe(Effect.ignore);     yield* ctx.core.sources.register({      id: namespace,      scope,      kind: "googleDiscovery",      name: sourceData.name,      url: sourceData.rootUrl,      canRemove: true,      canRefresh: true,      canEdit: true,      tools: manifest.methods.map((method: GoogleDiscoveryManifestMethod) => ({        name: method.toolPath,        description: Option.getOrElse(          method.description,          () => `${method.binding.method.toUpperCase()} ${method.binding.pathTemplate}`,        ),        inputSchema: Option.getOrUndefined(method.inputSchema),        outputSchema: Option.getOrUndefined(method.outputSchema),      })),    });     if (Object.keys(manifest.schemaDefinitions).length > 0) {      yield* ctx.core.definitions.register({        sourceId: namespace,        scope,        definitions: manifest.schemaDefinitions,      });    }     yield* Effect.forEach(      manifest.methods,      (method) =>        ctx.storage.putBinding(`${namespace}.${method.toolPath}`, namespace, scope, method.binding),      { discard: true },    );     yield* ctx.storage.putSource({      namespace,      scope,      name: sourceData.name,      config: sourceData,    });     return manifest.methods.length;  }); const makeGoogleDiscoveryPluginExtension = (ctx: PluginCtx<GoogleDiscoveryStore>) => ({  probeDiscovery: (input: string | GoogleDiscoveryProbeInput) =>    Effect.gen(function* () {      const discoveryUrl = typeof input === "string" ? input : input.discoveryUrl;      const credentials =        typeof input === "string"          ? undefined          : yield* resolveGoogleDiscoveryCredentials(input.credentials, ctx);      const text = yield* fetchDiscoveryDocument(discoveryUrl, credentials);      const manifest = yield* extractGoogleDiscoveryManifest(text);      const scopes = Object.keys(        Option.isSome(manifest.oauthScopes) ? manifest.oauthScopes.value : {},      ).sort();      const operations = manifest.methods.map((method) => ({        toolPath: method.toolPath,        method: method.binding.method,        pathTemplate: method.binding.pathTemplate,        description: Option.isSome(method.description) ? method.description.value : null,      }));      return {        name: Option.isSome(manifest.title)          ? manifest.title.value          : `${manifest.service} ${manifest.version}`,        title: Option.isSome(manifest.title) ? manifest.title.value : null,        service: manifest.service,        version: manifest.version,        toolCount: manifest.methods.length,        scopes,        operations,      };    }),   addSource: (input: GoogleDiscoveryAddSourceInput) =>    ctx.transaction(      Effect.gen(function* () {        const credentials = yield* resolveGoogleDiscoveryCredentials(input.credentials, ctx);        const text = yield* fetchDiscoveryDocument(input.discoveryUrl, credentials);        const manifest = yield* extractGoogleDiscoveryManifest(text);        const namespace =          input.namespace ??          deriveNamespace({            name: input.name,            service: manifest.service,            version: manifest.version,          });        const sourceData = GoogleDiscoveryStoredSourceDataSchema.make({          name: input.name,          discoveryUrl: normalizeDiscoveryUrl(input.discoveryUrl),          credentials: input.credentials,          service: manifest.service,          version: manifest.version,          rootUrl: manifest.rootUrl,          servicePath: manifest.servicePath,          auth: input.auth,        });        const toolCount = yield* registerManifest(          ctx,

Lines 1–500 of 825.