import { Effect } from "effect"; import { definePlugin } from "@executor-js/sdk/core"; import { makeConfiguredWorkOSVaultClient, type WorkOSVaultClient, WorkOSVaultClientInstantiationError, type WorkOSVaultCredentials, } from "./client"; import { WORKOS_VAULT_PROVIDER_KEY, makeWorkOSVaultSecretProvider, makeWorkosVaultStore, workosVaultSchema, type WorkOSVaultContextForScope, type WorkosVaultStore, } from "./secret-store"; // --------------------------------------------------------------------------- // Plugin options — either pass a pre-built client (for tests / injection) // or the WorkOS credentials to build one at startup. An `objectPrefix` // override is available for multi-tenant installations. // --------------------------------------------------------------------------- export interface WorkOSVaultPluginOptions { readonly client?: WorkOSVaultClient; readonly credentials?: WorkOSVaultCredentials; readonly objectPrefix?: string; /** * Override the default scope-id → vault-context mapping. Each key * returned becomes an independent KEK-matching dimension, so hosts * whose scope ids have a non-default shape can split them into * meaningful fields (user/org/workspace/…) rather than a single * opaque string. */ readonly contextForScope?: WorkOSVaultContextForScope; } const makeWorkOSVaultExtension = () => ({ providerKey: WORKOS_VAULT_PROVIDER_KEY, }) as const; export type WorkOSVaultExtension = ReturnType; // The plugin's typed store is just its metadata-store wrapper. The // secret provider closes over this store plus the resolved WorkOS // client; the scope id is threaded in per-call by the executor's // secrets facade. type WorkosVaultPluginStore = WorkosVaultStore; const buildClient = ( options: WorkOSVaultPluginOptions | undefined, ): Effect.Effect => { if (options?.client) return Effect.succeed(options.client); if (options?.credentials) { return makeConfiguredWorkOSVaultClient(options.credentials); } return Effect.fail( new WorkOSVaultClientInstantiationError({ cause: "workosVaultPlugin requires either `client` or `credentials` to be provided", }), ); }; export const workosVaultPlugin = definePlugin((options?: WorkOSVaultPluginOptions) => ({ id: "workosVault" as const, packageName: "@executor-js/plugin-workos-vault", schema: workosVaultSchema, storage: (deps): WorkosVaultPluginStore => makeWorkosVaultStore(deps), extension: makeWorkOSVaultExtension, secretProviders: (ctx) => { // Build (or accept) the WorkOS client once at startup. If // credentials are bad this throws synchronously via Effect.runSync, // which is what we want — the executor fails to start rather // than surfacing bad credentials on first secret access. const client = Effect.runSync(buildClient(options)); return [ makeWorkOSVaultSecretProvider({ client, store: ctx.storage, objectPrefix: options?.objectPrefix, contextForScope: options?.contextForScope, }), ]; }, }));