86 lines · 3.1 KB · TypeScript
RawDownload
// ---------------------------------------------------------------------------// Cloud executor — stateless, per-request, new SDK shape// ---------------------------------------------------------------------------//// Each invocation of `createScopedExecutor` runs inside a request-scoped// Effect and yields a fresh executor bound to the current DbService's// per-request postgres.js client. Cloudflare Workers + Hyperdrive demand// fresh connections per request, so "build once" means "once per request"// here. import { Effect } from "effect"; import {  Scope,  ScopeId,  collectSchemas,  createExecutor,} from "@executor/sdk";import {  makePostgresAdapter,  makePostgresBlobStore,} from "@executor/storage-postgres";import { openApiPlugin } from "@executor/plugin-openapi";import { mcpPlugin } from "@executor/plugin-mcp";import { graphqlPlugin } from "@executor/plugin-graphql";import { workosVaultPlugin } from "@executor/plugin-workos-vault"; import { DbService } from "./db";import { server } from "../env"; // ---------------------------------------------------------------------------// Plugin list — one place, used for both the runtime and the CLI config// (executor.config.ts). No stdio MCP in cloud; no keychain/file-secrets/// 1password/google-discovery.//// NOTE: the CLI config (executor.config.ts) imports these same plugins with// stub credentials because it only reads `plugin.schema`. Here we pass// real credentials from the env.// --------------------------------------------------------------------------- const createOrgPlugins = () =>  [    openApiPlugin(),    mcpPlugin({ dangerouslyAllowStdioMCP: false }),    graphqlPlugin(),    workosVaultPlugin({      credentials: {        apiKey: server.WORKOS_API_KEY,        clientId: server.WORKOS_CLIENT_ID,      },    }),  ] as const; // ---------------------------------------------------------------------------// Create a fresh executor for a scope (stateless, per-request).//// Today "scope" is the WorkOS organization — orgs are the only scope// level the cloud app exposes. When workspace / user scopes land, this// function grows to accept a `ScopeStack` instead of a single scope id,// and nothing downstream (executor, plugins, storage) has to change.// --------------------------------------------------------------------------- export const createScopedExecutor = (  scopeId: string,  scopeName: string,) =>  Effect.gen(function* () {    const { db } = yield* DbService;     const plugins = createOrgPlugins();    const schema = collectSchemas(plugins);    const adapter = makePostgresAdapter({ db, schema });    const blobs = makePostgresBlobStore({ db });     const scope = new Scope({      id: ScopeId.make(scopeId),      name: scopeName,      createdAt: new Date(),    });     // The executor surface returns raw `StorageFailure`; translation to    // the opaque `InternalError({ traceId })` happens at the HTTP edge    // via `withCapture` (see `api/protected-layers.ts`). That's    // where `ErrorCaptureLive` (Sentry) gets wired in.    return yield* createExecutor({ scope, adapter, blobs, plugins });  });