// ---------------------------------------------------------------------------// 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 }); });
// ---------------------------------------------------------------------------
// 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 });
});