@executor-js/sdk 1.5.16 → 1.5.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-EIHWBY6T.js → chunk-4XPVLX62.js} +36 -1
- package/dist/chunk-4XPVLX62.js.map +1 -0
- package/dist/{chunk-SK3PCJHJ.js → chunk-EJKXSREX.js} +10 -1
- package/dist/chunk-EJKXSREX.js.map +1 -0
- package/dist/{chunk-6EED5LAL.js → chunk-PCSRC6WP.js} +1 -1
- package/dist/{chunk-6EED5LAL.js.map → chunk-PCSRC6WP.js.map} +1 -1
- package/dist/{chunk-VICUTMT6.js → chunk-UWBP7WLB.js} +192 -22
- package/dist/chunk-UWBP7WLB.js.map +1 -0
- package/dist/client.d.ts +13 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/core-schema.d.ts +2 -0
- package/dist/core-schema.d.ts.map +1 -1
- package/dist/core-tools.d.ts +3 -0
- package/dist/core-tools.d.ts.map +1 -1
- package/dist/core.js +4 -4
- package/dist/executor.d.ts +1 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/host-internal.js +1 -1
- package/dist/index.js +3 -3
- package/dist/oauth-client.d.ts +5 -0
- package/dist/oauth-client.d.ts.map +1 -1
- package/dist/oauth-helpers.d.ts +1 -0
- package/dist/oauth-helpers.d.ts.map +1 -1
- package/dist/oauth-service.d.ts +4 -0
- package/dist/oauth-service.d.ts.map +1 -1
- package/dist/server-connection.d.ts +7 -0
- package/dist/server-connection.d.ts.map +1 -1
- package/dist/shared.js +2 -2
- package/dist/testing.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-EIHWBY6T.js.map +0 -1
- package/dist/chunk-SK3PCJHJ.js.map +0 -1
- package/dist/chunk-VICUTMT6.js.map +0 -1
|
@@ -62,6 +62,40 @@ var providerAuthorizeExtras = (authorizationUrl) => {
|
|
|
62
62
|
}
|
|
63
63
|
return {};
|
|
64
64
|
};
|
|
65
|
+
var hostnameFromCallbackDomain = (callbackDomain) => {
|
|
66
|
+
const trimmed = callbackDomain.trim();
|
|
67
|
+
if (trimmed.length === 0) return void 0;
|
|
68
|
+
const candidate = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
|
|
69
|
+
if (!URL.canParse(candidate)) return void 0;
|
|
70
|
+
const url = new URL(candidate);
|
|
71
|
+
if (url.port !== "" || url.username !== "" || url.password !== "") return void 0;
|
|
72
|
+
if (url.pathname !== "/" && url.pathname !== "") return void 0;
|
|
73
|
+
return url.hostname.toLowerCase();
|
|
74
|
+
};
|
|
75
|
+
var siblingParentDomainOf = (hostname) => {
|
|
76
|
+
const labels = hostname.split(".");
|
|
77
|
+
if (labels.length < 3) return void 0;
|
|
78
|
+
const parent = labels.slice(1).join(".");
|
|
79
|
+
return parent.includes(".") ? parent : void 0;
|
|
80
|
+
};
|
|
81
|
+
var rebindTokenEndpointHostToCallbackDomain = (configuredTokenUrl, callbackDomain) => {
|
|
82
|
+
if (!callbackDomain) return configuredTokenUrl;
|
|
83
|
+
if (!URL.canParse(configuredTokenUrl)) return configuredTokenUrl;
|
|
84
|
+
const configured = new URL(configuredTokenUrl);
|
|
85
|
+
if (configured.protocol !== "https:") return configuredTokenUrl;
|
|
86
|
+
const targetHost = hostnameFromCallbackDomain(callbackDomain);
|
|
87
|
+
if (!targetHost) return configuredTokenUrl;
|
|
88
|
+
const configuredHost = configured.hostname.toLowerCase();
|
|
89
|
+
if (targetHost === configuredHost) return configuredTokenUrl;
|
|
90
|
+
const configuredParent = siblingParentDomainOf(configuredHost);
|
|
91
|
+
const targetParent = siblingParentDomainOf(targetHost);
|
|
92
|
+
if (!configuredParent || !targetParent || configuredParent !== targetParent) {
|
|
93
|
+
return configuredTokenUrl;
|
|
94
|
+
}
|
|
95
|
+
const rebound = new URL(configuredTokenUrl);
|
|
96
|
+
rebound.hostname = targetHost;
|
|
97
|
+
return rebound.toString();
|
|
98
|
+
};
|
|
65
99
|
var isOAuth2Error = Predicate.isTagged("OAuth2Error");
|
|
66
100
|
var responseFromOAuthErrorCause = (cause) => {
|
|
67
101
|
if (cause instanceof Response) return cause;
|
|
@@ -296,9 +330,10 @@ export {
|
|
|
296
330
|
createOAuthState,
|
|
297
331
|
buildAuthorizationUrl,
|
|
298
332
|
providerAuthorizeExtras,
|
|
333
|
+
rebindTokenEndpointHostToCallbackDomain,
|
|
299
334
|
exchangeAuthorizationCode,
|
|
300
335
|
exchangeClientCredentials,
|
|
301
336
|
refreshAccessToken,
|
|
302
337
|
shouldRefreshToken
|
|
303
338
|
};
|
|
304
|
-
//# sourceMappingURL=chunk-
|
|
339
|
+
//# sourceMappingURL=chunk-4XPVLX62.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/oauth-helpers.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// OAuth 2.0 helpers — generic, isomorphic building blocks.\n//\n// Thin wrappers around `oauth4webapi` (stateless; pure Web Crypto +\n// `fetch`, no deps; runs unchanged in Node, CF Workers, and browsers).\n// Each public helper is a single `Effect.tryPromise` call that delegates\n// the RFC work to the library and normalises the failure surface into\n// `OAuth2Error`.\n//\n// What stays hand-rolled:\n// - `OAuth2Error` — our tagged error; we want a stable shape across\n// every token-endpoint call\n// - `shouldRefreshToken` — skew check, trivial\n// - `buildAuthorizationUrl` — the library doesn't expose a raw\n// authorization-URL builder (it prefers PAR); a 30-line manual\n// construction keeps the call sync and lets callers opt out of PAR\n// ---------------------------------------------------------------------------\n\nimport { Data, Effect, Predicate } from \"effect\";\nimport * as oauth from \"oauth4webapi\";\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class OAuth2Error extends Data.TaggedError(\"OAuth2Error\")<{\n readonly message: string;\n /**\n * RFC 6749 §5.2 error code, when the token endpoint returned one\n * (`invalid_grant`, `invalid_client`, `unauthorized_client`, ...).\n * Callers use this to distinguish terminal failures (a refresh token\n * the AS no longer honours → re-auth required) from transient ones.\n */\n readonly error?: string;\n readonly cause?: unknown;\n}> {}\n\n// ---------------------------------------------------------------------------\n// Token response shape (RFC 6749 §5.1)\n// ---------------------------------------------------------------------------\n\nexport type OAuth2TokenResponse = {\n readonly access_token: string;\n readonly token_type?: string;\n readonly refresh_token?: string;\n readonly expires_in?: number;\n readonly scope?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Refresh tokens this many ms before expiry to avoid mid-request expiration. */\nexport const OAUTH2_REFRESH_SKEW_MS = 60_000;\n\n/** Default token-endpoint timeout. */\nexport const OAUTH2_DEFAULT_TIMEOUT_MS = 20_000;\n\nexport interface OAuthEndpointUrlPolicy {\n readonly allowHttp?: boolean;\n}\n\nconst isLoopbackHttpUrl = (value: string): boolean => {\n if (!URL.canParse(value)) return false;\n const url = new URL(value);\n if (url.protocol !== \"http:\") return false;\n const hostname = url.hostname.toLowerCase();\n return (\n hostname === \"localhost\" ||\n hostname === \"0.0.0.0\" ||\n hostname === \"::1\" ||\n hostname === \"[::1]\" ||\n hostname.startsWith(\"127.\")\n );\n};\n\nexport const isSupportedOAuthEndpointUrl = (\n value: string,\n policy: OAuthEndpointUrlPolicy = {},\n): boolean => {\n if (!URL.canParse(value)) return false;\n const url = new URL(value);\n return (\n url.protocol === \"https:\" ||\n isLoopbackHttpUrl(value) ||\n (url.protocol === \"http:\" && policy.allowHttp === true)\n );\n};\n\nexport const assertSupportedOAuthEndpointUrl = (\n value: string,\n label = \"OAuth endpoint URL\",\n policy: OAuthEndpointUrlPolicy = {},\n): string => {\n if (isSupportedOAuthEndpointUrl(value, policy)) return value;\n // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: synchronous assertion helper used by URL constructors and Effect.try wrappers\n throw new TypeError(`${label} must use https: or loopback http:`);\n};\n\n// ---------------------------------------------------------------------------\n// PKCE (RFC 7636) — straight delegation to `oauth4webapi`\n// ---------------------------------------------------------------------------\n\nexport const createPkceCodeVerifier = (): string => oauth.generateRandomCodeVerifier();\n\nexport const createPkceCodeChallenge = (verifier: string): Promise<string> =>\n oauth.calculatePKCECodeChallenge(verifier);\n\n/** RFC 6749 `state` — an unguessable correlation token minted by `oauth.start`\n * and redeemed by `oauth.complete`. */\nexport const createOAuthState = (): string => oauth.generateRandomState();\n\n// ---------------------------------------------------------------------------\n// Authorization URL builder\n// ---------------------------------------------------------------------------\n\nexport type BuildAuthorizationUrlInput = {\n readonly authorizationUrl: string;\n readonly clientId: string;\n readonly redirectUrl: string;\n readonly scopes: readonly string[];\n readonly state: string;\n /** Pre-computed base64url S256 challenge (from `createPkceCodeChallenge`). */\n readonly codeChallenge: string;\n /** Separator between scopes. RFC 6749 says space; some providers use comma. */\n readonly scopeSeparator?: string;\n /** RFC 8707 Resource Indicator. MCP Authorization 2025-06-18 §\"Resource\n * Parameter Implementation\" requires clients to send this on every\n * authorization request, regardless of AS support. */\n readonly resource?: string;\n /** Provider-specific extras (e.g. Google's `access_type=offline`). */\n readonly extraParams?: Readonly<Record<string, string>>;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n};\n\n/** Build an RFC 6749 §4.1.1 authorization URL. Sync; pre-computed\n * challenge lets this stay out of the Promise world. */\nexport const buildAuthorizationUrl = (input: BuildAuthorizationUrlInput): string => {\n const url = new URL(\n assertSupportedOAuthEndpointUrl(\n input.authorizationUrl,\n \"Authorization URL\",\n input.endpointUrlPolicy,\n ),\n );\n // Benign default kept by design: a single space is the RFC 6749 scope\n // separator. Callers targeting a legacy comma-separated provider pass\n // `scopeSeparator` explicitly (see the field's JSDoc).\n const separator = input.scopeSeparator ?? \" \";\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUrl);\n url.searchParams.set(\"response_type\", \"code\");\n if (input.scopes.length > 0) {\n url.searchParams.set(\"scope\", input.scopes.join(separator));\n }\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n if (input.resource) {\n url.searchParams.set(\"resource\", input.resource);\n }\n if (input.extraParams) {\n for (const [k, v] of Object.entries(input.extraParams)) {\n url.searchParams.set(k, v);\n }\n }\n return url.toString();\n};\n\n/** Provider-specific authorize-URL extras that are NOT RFC 6749 params, so the\n * generic flow must add them per-provider (keyed off the authorization host).\n *\n * Google: `access_type=offline` + `prompt=consent` are required to receive (and\n * keep receiving, across reconnects / scope changes) a REFRESH TOKEN — without\n * them Google issues an access-token-only grant that dies in ~1h and a\n * re-consent can silently keep the old scope set. Do not add\n * `include_granted_scopes=true` here: with historical grants on the same Google\n * consent app, Google folds those unrelated scopes into the new consent flow and\n * can fail inside accounts.google.com before returning to our callback. */\nexport const providerAuthorizeExtras = (\n authorizationUrl: string,\n): Readonly<Record<string, string>> => {\n // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: URL() throws on invalid input → no provider extras\n try {\n const host = new URL(authorizationUrl).host.toLowerCase();\n if (host === \"accounts.google.com\") {\n return { access_type: \"offline\", prompt: \"consent\" };\n }\n } catch {\n // Unparseable authorization URL — let buildAuthorizationUrl surface the error.\n }\n return {};\n};\n\n// ---------------------------------------------------------------------------\n// Regional token-endpoint rebind\n//\n// Some authorization servers publish a single static metadata document that\n// advertises one region's token endpoint, but issue authorization codes that\n// are only redeemable at the *regional* host the user's org actually lives on.\n// The region comes back on the callback as a non-standard `domain` (or `site`)\n// query param: Datadog returns `domain=us5.datadoghq.com` while its metadata\n// statically advertises `app.datadoghq.com`. Redeeming the code at the\n// advertised host then fails with `invalid_grant`.\n//\n// `rebindTokenEndpointHostToCallbackDomain` swaps ONLY the hostname of the\n// configured token URL to the callback-supplied host, and ONLY when that host\n// is a sibling subdomain of the configured one (same parent after stripping the\n// leftmost DNS label, e.g. `app.datadoghq.com` and `us5.datadoghq.com` both\n// reduce to `datadoghq.com`). The token request carries the client secret, the\n// code, and the PKCE verifier, so an attacker-influenced `domain` must never be\n// able to point it at an arbitrary origin. Anything that fails the sibling\n// check, fails to parse, or isn't https falls back to the configured URL\n// unchanged.\n// ---------------------------------------------------------------------------\n\nconst hostnameFromCallbackDomain = (callbackDomain: string): string | undefined => {\n const trimmed = callbackDomain.trim();\n if (trimmed.length === 0) return undefined;\n // Datadog sends `domain` as a bare host and `site` as a full origin; accept\n // either by tolerating an optional scheme, then taking only the hostname.\n const candidate = trimmed.includes(\"://\") ? trimmed : `https://${trimmed}`;\n if (!URL.canParse(candidate)) return undefined;\n const url = new URL(candidate);\n // A legitimate regional host carries no port, credentials, or path.\n if (url.port !== \"\" || url.username !== \"\" || url.password !== \"\") return undefined;\n if (url.pathname !== \"/\" && url.pathname !== \"\") return undefined;\n return url.hostname.toLowerCase();\n};\n\n/** Parent domain after stripping the leftmost DNS label, or `undefined` when\n * the host has no sibling space (a single label, or a parent that is a bare\n * TLD). `app.datadoghq.com` -> `datadoghq.com`; `foo.com` -> undefined. */\nconst siblingParentDomainOf = (hostname: string): string | undefined => {\n const labels = hostname.split(\".\");\n if (labels.length < 3) return undefined;\n const parent = labels.slice(1).join(\".\");\n // Require the parent to itself be multi-label so a 2-label configured host\n // can never rebind across an entire TLD (e.g. foo.com -> bar.com).\n return parent.includes(\".\") ? parent : undefined;\n};\n\nexport const rebindTokenEndpointHostToCallbackDomain = (\n configuredTokenUrl: string,\n callbackDomain: string | null | undefined,\n): string => {\n if (!callbackDomain) return configuredTokenUrl;\n if (!URL.canParse(configuredTokenUrl)) return configuredTokenUrl;\n const configured = new URL(configuredTokenUrl);\n if (configured.protocol !== \"https:\") return configuredTokenUrl;\n const targetHost = hostnameFromCallbackDomain(callbackDomain);\n if (!targetHost) return configuredTokenUrl;\n const configuredHost = configured.hostname.toLowerCase();\n if (targetHost === configuredHost) return configuredTokenUrl;\n const configuredParent = siblingParentDomainOf(configuredHost);\n const targetParent = siblingParentDomainOf(targetHost);\n if (!configuredParent || !targetParent || configuredParent !== targetParent) {\n return configuredTokenUrl;\n }\n const rebound = new URL(configuredTokenUrl);\n rebound.hostname = targetHost;\n return rebound.toString();\n};\n\n// ---------------------------------------------------------------------------\n// Error mapping — `oauth4webapi`'s `process*Response` failure shapes are\n// either a WWW-Authenticate challenge or an RFC 6749 §5.2 error body,\n// both exposed via `.error` / `.error_description`. Probing the envelope\n// preserves RFC 6749 error-code semantics (e.g., mapping `invalid_grant`\n// to reauth-required) across wrappers.\n// ---------------------------------------------------------------------------\n\nconst isOAuth2Error = Predicate.isTagged(\"OAuth2Error\") as (cause: unknown) => cause is OAuth2Error;\n\nconst responseFromOAuthErrorCause = (cause: unknown): Response | undefined => {\n if (cause instanceof Response) return cause;\n if (typeof cause !== \"object\" || cause === null) return undefined;\n const envelope = cause as {\n readonly cause?: unknown;\n readonly response?: unknown;\n };\n if (envelope.response instanceof Response) return envelope.response;\n if (envelope.cause instanceof Response) return envelope.cause;\n return undefined;\n};\n\nconst redactTokenEndpointBody = (body: string): string =>\n body\n .replaceAll(\n /(\"(?:access_token|refresh_token|id_token|client_secret)\"\\s*:\\s*\")[^\"]*(\")/gi,\n \"$1[redacted]$2\",\n )\n .replaceAll(\n /((?:access_token|refresh_token|id_token|client_secret|code)=)[^&\\s]*/gi,\n \"$1[redacted]\",\n );\n\nconst tokenEndpointHttpSummary = async (response: Response): Promise<string> => {\n const status = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : \"\"}`;\n const contentType = response.headers.get(\"content-type\");\n const url = response.url ? ` from ${response.url}` : \"\";\n const parts = [`${status}${url}`];\n if (contentType) parts.push(`content-type ${contentType}`);\n const preview = await bodyPreviewFromResponse(response);\n if (preview) parts.push(`body: ${preview}`);\n return parts.join(\"; \");\n};\n\nconst bodyPreviewFromResponse = async (response: Response): Promise<string | undefined> => {\n const text = await Promise.resolve()\n .then(() => response.clone().text())\n .then(\n (value) => value.trim(),\n () => \"\",\n );\n if (!text) return undefined;\n const redacted = redactTokenEndpointBody(text.replaceAll(/\\s+/g, \" \"));\n return redacted.length > 500 ? `${redacted.slice(0, 500)}...` : redacted;\n};\n\nconst toOAuth2Error = (cause: unknown): OAuth2Error => {\n if (isOAuth2Error(cause)) return cause;\n if (typeof cause === \"object\" && cause !== null) {\n const c = cause as {\n error?: unknown;\n error_description?: unknown;\n message?: unknown;\n };\n const code = typeof c.error === \"string\" ? c.error : undefined;\n const description =\n typeof c.error_description === \"string\"\n ? c.error_description\n : typeof c.message === \"string\"\n ? c.message\n : undefined;\n return new OAuth2Error({\n message: `OAuth token exchange failed: ${description ?? code ?? \"unknown error\"}`,\n error: code,\n cause,\n });\n }\n return new OAuth2Error({\n message: \"OAuth token exchange failed\",\n cause,\n });\n};\n\nconst toOAuth2ErrorWithHttpSummary = (cause: unknown): Effect.Effect<OAuth2Error> => {\n if (isOAuth2Error(cause)) return Effect.succeed(cause);\n const base = toOAuth2Error(cause);\n const response = responseFromOAuthErrorCause(cause);\n if (!response) return Effect.succeed(base);\n return Effect.promise(() => tokenEndpointHttpSummary(response)).pipe(\n Effect.map(\n (summary) =>\n new OAuth2Error({\n message: `${base.message} (${summary})`,\n error: base.error,\n cause,\n }),\n ),\n );\n};\n\nconst failOAuth2WithHttpSummary = (cause: unknown): Effect.Effect<never, OAuth2Error> =>\n toOAuth2ErrorWithHttpSummary(cause).pipe(Effect.flatMap((error) => Effect.fail(error)));\n\n// ---------------------------------------------------------------------------\n// oauth4webapi adapter helpers\n// ---------------------------------------------------------------------------\n\nexport type ClientAuthMethod = \"body\" | \"basic\";\n\n/**\n * The token-endpoint client-auth transport used when a caller doesn't specify\n * one. `\"body\"` is `client_secret_post` (the secret in the form body) — the\n * method our DCR registers (`token_endpoint_auth_method: client_secret_post`)\n * and the one every confidential client in the v2 model uses. EXPLICIT and\n * documented rather than a hidden inline `?? \"body\"`: callers that need\n * `client_secret_basic` pass `clientAuth: \"basic\"`. For PUBLIC clients (no\n * secret) the method is irrelevant — `pickClientAuth` returns `None()`.\n */\nexport const DEFAULT_CLIENT_AUTH_METHOD: ClientAuthMethod = \"body\";\n\nconst asFromTokenUrl = (\n tokenUrl: string,\n endpointUrlPolicy: OAuthEndpointUrlPolicy = {},\n): oauth.AuthorizationServer => {\n assertSupportedOAuthEndpointUrl(tokenUrl, \"Token URL\", endpointUrlPolicy);\n const url = new URL(tokenUrl);\n return {\n issuer: `${url.protocol}//${url.host}`,\n token_endpoint: tokenUrl,\n };\n};\n\nconst asFromTokenUrlAndIssuer = (\n tokenUrl: string,\n issuerUrl: string | null | undefined,\n options: {\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n } = {},\n): oauth.AuthorizationServer => {\n const as = asFromTokenUrl(tokenUrl, options.endpointUrlPolicy);\n const withIssuer = issuerUrl ? { ...as, issuer: issuerUrl } : as;\n return options.idTokenSigningAlgValuesSupported\n ? {\n ...withIssuer,\n id_token_signing_alg_values_supported: [...options.idTokenSigningAlgValuesSupported],\n }\n : withIssuer;\n};\n\nconst oauth4webapiRequestOptions = (\n targetUrl: string,\n timeoutMs: number | undefined,\n endpointUrlPolicy: OAuthEndpointUrlPolicy = {},\n): Record<string, unknown> => {\n const options: Record<string, unknown> = {\n signal: AbortSignal.timeout(timeoutMs ?? OAUTH2_DEFAULT_TIMEOUT_MS),\n };\n if (\n isLoopbackHttpUrl(targetUrl) ||\n (URL.canParse(targetUrl) &&\n new URL(targetUrl).protocol === \"http:\" &&\n endpointUrlPolicy.allowHttp === true)\n ) {\n (options as { [oauth.allowInsecureRequests]?: boolean })[oauth.allowInsecureRequests] = true;\n }\n return options;\n};\n\n// Select the token-endpoint client authentication. The secret's presence is the\n// EXPLICIT public-vs-confidential discriminator in the v2 model: a registered\n// client either has a secret (confidential — authenticate it) or has none\n// (public PKCE — `None()`, RFC 7636). This is not a silent guess: `loadClient`\n// persists a non-empty secret for confidential clients and null/\"\" for public\n// ones, so an absent secret here unambiguously means \"public client\". The\n// `method` only chooses HOW a present secret is sent (post vs basic).\nconst pickClientAuth = (\n clientSecret: string | null | undefined,\n method: ClientAuthMethod,\n): oauth.ClientAuth => {\n if (!clientSecret) return oauth.None();\n return method === \"basic\"\n ? oauth.ClientSecretBasic(clientSecret)\n : oauth.ClientSecretPost(clientSecret);\n};\n\nconst tokenResponseFrom = (r: oauth.TokenEndpointResponse): OAuth2TokenResponse => ({\n access_token: r.access_token,\n token_type: r.token_type,\n refresh_token: r.refresh_token,\n expires_in: typeof r.expires_in === \"number\" ? r.expires_in : undefined,\n scope: r.scope,\n});\n\n// MCP source connections are pure OAuth 2.0 — we never request `openid` and\n// never consume `id_token`. Some providers (PostHog, etc.) front an OIDC\n// backend and emit an `id_token` anyway; oauth4webapi then strict-validates\n// its claims against the AS metadata and rejects mismatches we don't care\n// about. Strip the field before delegation.\nconst stripIdToken = async (response: Response): Promise<Response> => {\n const body = await response\n .clone()\n .json()\n .then(\n (value: unknown) => value,\n () => null,\n );\n if (!body || typeof body !== \"object\" || !(\"id_token\" in (body as Record<string, unknown>))) {\n return response;\n }\n const { id_token: _ignored, ...rest } = body as Record<string, unknown>;\n return new Response(JSON.stringify(rest), {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n};\n\nconst processTokenEndpointResponse = async (\n as: oauth.AuthorizationServer,\n client: oauth.Client,\n response: Response,\n): Promise<OAuth2TokenResponse> =>\n tokenResponseFrom(\n await oauth.processGenericTokenEndpointResponse(as, client, await stripIdToken(response)),\n );\n\n// ---------------------------------------------------------------------------\n// Exchange authorization code → tokens\n// ---------------------------------------------------------------------------\n\nexport type ExchangeAuthorizationCodeInput = {\n readonly tokenUrl: string;\n readonly issuerUrl?: string | null;\n readonly clientId: string;\n readonly clientSecret?: string | null;\n readonly redirectUrl: string;\n readonly codeVerifier: string;\n readonly code: string;\n readonly clientAuth?: ClientAuthMethod;\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n /** RFC 8707 Resource Indicator. MCP Auth spec MUST-requires this on\n * the token request when the client knows the resource it intends\n * to call. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n};\n\nexport const exchangeAuthorizationCode = (\n input: ExchangeAuthorizationCodeInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrlAndIssuer(input.tokenUrl, input.issuerUrl, {\n idTokenSigningAlgValuesSupported: input.idTokenSigningAlgValuesSupported,\n endpointUrlPolicy: input.endpointUrlPolicy,\n });\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n // `authorizationCodeGrantRequest` requires its `callbackParameters`\n // to have been returned from `validateAuthResponse`. Our public API\n // takes the `code` directly (the UI already validated `state` by\n // looking up the session), so skip the library's state-validation\n // rail and go through the generic grant request instead.\n const params = new URLSearchParams({\n code: input.code,\n redirect_uri: input.redirectUrl,\n code_verifier: input.codeVerifier,\n });\n if (input.resource) {\n params.set(\"resource\", input.resource);\n }\n const response = await oauth.genericTokenEndpointRequest(\n as,\n client,\n clientAuth,\n \"authorization_code\",\n params,\n oauth4webapiRequestOptions(input.tokenUrl, input.timeoutMs, input.endpointUrlPolicy),\n );\n return await processTokenEndpointResponse(as, client, response);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Exchange client credentials → tokens (RFC 6749 §4.4)\n// ---------------------------------------------------------------------------\n\nexport type ExchangeClientCredentialsInput = {\n readonly tokenUrl: string;\n readonly clientId: string;\n readonly clientSecret: string;\n readonly scopes?: readonly string[];\n readonly scopeSeparator?: string;\n readonly clientAuth?: ClientAuthMethod;\n /** RFC 8707 Resource Indicator. MCP Authorization 2025-06-18 requires this\n * on token requests when the client knows the protected resource. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n};\n\nexport const exchangeClientCredentials = (\n input: ExchangeClientCredentialsInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrl(input.tokenUrl, input.endpointUrlPolicy);\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n const params = new URLSearchParams();\n if (input.scopes && input.scopes.length > 0) {\n params.set(\"scope\", input.scopes.join(input.scopeSeparator ?? \" \"));\n }\n if (input.resource) {\n params.set(\"resource\", input.resource);\n }\n const response = await oauth.clientCredentialsGrantRequest(\n as,\n client,\n clientAuth,\n params,\n oauth4webapiRequestOptions(input.tokenUrl, input.timeoutMs, input.endpointUrlPolicy),\n );\n const result = await oauth.processClientCredentialsResponse(as, client, response);\n return tokenResponseFrom(result);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Refresh access token\n// ---------------------------------------------------------------------------\n\nexport type RefreshAccessTokenInput = {\n readonly tokenUrl: string;\n readonly issuerUrl?: string | null;\n readonly clientId: string;\n readonly clientSecret?: string | null;\n readonly refreshToken: string;\n readonly scopes?: readonly string[];\n readonly scopeSeparator?: string;\n readonly clientAuth?: ClientAuthMethod;\n readonly idTokenSigningAlgValuesSupported?: readonly string[];\n /** RFC 8707 Resource Indicator — MCP spec MUST-requires this on\n * refresh requests so the new access token's audience is bound to\n * the same resource. */\n readonly resource?: string;\n readonly timeoutMs?: number;\n readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;\n};\n\nexport const refreshAccessToken = (\n input: RefreshAccessTokenInput,\n): Effect.Effect<OAuth2TokenResponse, OAuth2Error> =>\n Effect.tryPromise({\n try: async () => {\n const as = asFromTokenUrlAndIssuer(input.tokenUrl, input.issuerUrl, {\n idTokenSigningAlgValuesSupported: input.idTokenSigningAlgValuesSupported,\n endpointUrlPolicy: input.endpointUrlPolicy,\n });\n const client: oauth.Client = { client_id: input.clientId };\n const clientAuth = pickClientAuth(\n input.clientSecret,\n input.clientAuth ?? DEFAULT_CLIENT_AUTH_METHOD,\n );\n const extraParams = new URLSearchParams();\n if (input.scopes && input.scopes.length > 0) {\n extraParams.set(\"scope\", input.scopes.join(input.scopeSeparator ?? \" \"));\n }\n if (input.resource) {\n extraParams.set(\"resource\", input.resource);\n }\n const additionalParameters =\n Array.from(extraParams.keys()).length > 0 ? extraParams : undefined;\n const response = await oauth.refreshTokenGrantRequest(\n as,\n client,\n clientAuth,\n input.refreshToken,\n {\n ...oauth4webapiRequestOptions(input.tokenUrl, input.timeoutMs, input.endpointUrlPolicy),\n additionalParameters,\n },\n );\n const result = await oauth.processRefreshTokenResponse(\n as,\n client,\n await stripIdToken(response),\n );\n return tokenResponseFrom(result);\n },\n catch: (cause) => cause,\n }).pipe(Effect.catch(failOAuth2WithHttpSummary));\n\n// ---------------------------------------------------------------------------\n// Refresh-needed predicate\n// ---------------------------------------------------------------------------\n\nexport const shouldRefreshToken = (input: {\n readonly expiresAt: number | null;\n readonly now?: number;\n readonly skewMs?: number;\n}): boolean => {\n if (input.expiresAt === null) return false;\n const now = input.now ?? Date.now();\n const skew = input.skewMs ?? OAUTH2_REFRESH_SKEW_MS;\n return input.expiresAt <= now + skew;\n};\n"],"mappings":"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","names":[]}
|
|
@@ -54,6 +54,7 @@ var getExecutorServerAuthorizationHeader = (connection) => {
|
|
|
54
54
|
const auth = connection.auth;
|
|
55
55
|
if (!auth) return null;
|
|
56
56
|
if (auth.kind === "bearer") return `Bearer ${auth.token}`;
|
|
57
|
+
if (auth.kind === "oauth") return `Bearer ${auth.accessToken}`;
|
|
57
58
|
const encoded = encodeBasicCredentials(
|
|
58
59
|
`${auth.username ?? DEFAULT_EXECUTOR_SERVER_USERNAME}:${auth.password}`
|
|
59
60
|
);
|
|
@@ -68,6 +69,14 @@ var ExecutorServerAuthJson = Schema.Union([
|
|
|
68
69
|
Schema.Struct({
|
|
69
70
|
kind: Schema.Literal("bearer"),
|
|
70
71
|
token: Schema.String
|
|
72
|
+
}),
|
|
73
|
+
Schema.Struct({
|
|
74
|
+
kind: Schema.Literal("oauth"),
|
|
75
|
+
accessToken: Schema.String,
|
|
76
|
+
refreshToken: Schema.optional(Schema.String),
|
|
77
|
+
expiresAt: Schema.optional(Schema.Number),
|
|
78
|
+
tokenEndpoint: Schema.optional(Schema.String),
|
|
79
|
+
clientId: Schema.optional(Schema.String)
|
|
71
80
|
})
|
|
72
81
|
]);
|
|
73
82
|
var ExecutorServerConnectionJson = Schema.Struct({
|
|
@@ -159,4 +168,4 @@ export {
|
|
|
159
168
|
isOAuthPopupResult,
|
|
160
169
|
InternalError
|
|
161
170
|
};
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
171
|
+
//# sourceMappingURL=chunk-EJKXSREX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server-connection.ts","../src/oauth-popup-types.ts","../src/api-errors.ts"],"sourcesContent":["import { Option, Schema } from \"effect\";\n\nexport const DEFAULT_EXECUTOR_SERVER_ORIGIN = \"http://127.0.0.1:4000\";\nexport const DEFAULT_EXECUTOR_SERVER_USERNAME = \"executor\";\n\nexport type ExecutorServerConnectionKind = \"http\" | \"desktop-sidecar\";\nexport type ExecutorLocalServerKind = \"cli-daemon\" | \"desktop-sidecar\" | \"foreground\";\n\nexport type ExecutorServerAuth =\n | {\n readonly kind: \"basic\";\n readonly username?: string;\n readonly password: string;\n }\n | {\n readonly kind: \"bearer\";\n readonly token: string;\n }\n | {\n // OAuth 2.0 device-flow credential from `executor login` against a hosted\n // server. The access token is sent as a Bearer; `refreshToken` +\n // `expiresAt` (epoch seconds) + `tokenEndpoint` + `clientId` let the CLI\n // refresh it before expiry without a fresh browser login.\n readonly kind: \"oauth\";\n readonly accessToken: string;\n readonly refreshToken?: string;\n readonly expiresAt?: number;\n readonly tokenEndpoint?: string;\n readonly clientId?: string;\n };\n\nexport interface ExecutorServerConnection {\n readonly kind: ExecutorServerConnectionKind;\n readonly key: string;\n readonly origin: string;\n readonly apiBaseUrl: string;\n readonly displayName: string;\n readonly auth?: ExecutorServerAuth;\n}\n\nexport interface ExecutorServerConnectionInput {\n readonly kind?: ExecutorServerConnectionKind;\n readonly key?: string;\n readonly origin?: string;\n readonly apiBaseUrl?: string;\n readonly displayName?: string;\n readonly auth?: ExecutorServerAuth;\n}\n\nexport interface ExecutorLocalServerManifest {\n readonly version: 1;\n readonly kind: ExecutorLocalServerKind;\n readonly pid: number;\n readonly startedAt: string;\n readonly dataDir: string;\n readonly scopeDir: string | null;\n readonly connection: ExecutorServerConnection;\n readonly owner: {\n readonly client: \"cli\" | \"desktop\";\n readonly version: string | null;\n readonly executablePath: string | null;\n };\n}\n\nconst stripTrailingSlash = (value: string): string => value.replace(/\\/+$/, \"\");\n\nconst displayNameFromOrigin = (origin: string): string =>\n origin.replace(/^https?:\\/\\//, \"\").replace(/\\/+$/, \"\");\n\nexport const normalizeExecutorServerOrigin = (raw: string): string => {\n const trimmed = stripTrailingSlash(raw.trim());\n if (!trimmed) return DEFAULT_EXECUTOR_SERVER_ORIGIN;\n\n const parsed = new URL(/^https?:\\/\\//.test(trimmed) ? trimmed : `http://${trimmed}`);\n if (parsed.pathname === \"/api\") {\n parsed.pathname = \"/\";\n }\n parsed.search = \"\";\n parsed.hash = \"\";\n return stripTrailingSlash(parsed.toString());\n};\n\nexport const apiBaseUrlForServerOrigin = (origin: string): string => `${origin}/api`;\n\nexport const originFromApiBaseUrl = (raw: string): string => {\n const parsed = new URL(raw);\n if (parsed.pathname.endsWith(\"/api\")) {\n parsed.pathname = parsed.pathname.slice(0, -\"/api\".length) || \"/\";\n }\n parsed.search = \"\";\n parsed.hash = \"\";\n return normalizeExecutorServerOrigin(parsed.toString());\n};\n\nexport const normalizeExecutorServerConnection = (\n input: ExecutorServerConnectionInput = {},\n): ExecutorServerConnection => {\n const origin = normalizeExecutorServerOrigin(\n input.origin ??\n (input.apiBaseUrl ? originFromApiBaseUrl(input.apiBaseUrl) : DEFAULT_EXECUTOR_SERVER_ORIGIN),\n );\n const apiBaseUrl = stripTrailingSlash(input.apiBaseUrl ?? apiBaseUrlForServerOrigin(origin));\n const kind = input.kind ?? \"http\";\n\n return {\n kind,\n key: input.key ?? `${kind}:${origin}`,\n origin,\n apiBaseUrl,\n displayName: input.displayName ?? displayNameFromOrigin(origin),\n ...(input.auth ? { auth: input.auth } : {}),\n };\n};\n\nconst encodeBasicCredentials = (credentials: string): string | null => {\n if (typeof globalThis.btoa === \"function\") {\n return globalThis.btoa(credentials);\n }\n\n const buffer = (\n globalThis as {\n readonly Buffer?: {\n readonly from: (value: string) => { readonly toString: (encoding: \"base64\") => string };\n };\n }\n ).Buffer;\n if (buffer) {\n return buffer.from(credentials).toString(\"base64\");\n }\n\n return null;\n};\n\nexport const getExecutorServerAuthorizationHeader = (\n connection: ExecutorServerConnection,\n): string | null => {\n const auth = connection.auth;\n if (!auth) return null;\n if (auth.kind === \"bearer\") return `Bearer ${auth.token}`;\n if (auth.kind === \"oauth\") return `Bearer ${auth.accessToken}`;\n const encoded = encodeBasicCredentials(\n `${auth.username ?? DEFAULT_EXECUTOR_SERVER_USERNAME}:${auth.password}`,\n );\n return encoded ? `Basic ${encoded}` : null;\n};\n\nconst ExecutorServerAuthJson = Schema.Union([\n Schema.Struct({\n kind: Schema.Literal(\"basic\"),\n username: Schema.optional(Schema.String),\n password: Schema.String,\n }),\n Schema.Struct({\n kind: Schema.Literal(\"bearer\"),\n token: Schema.String,\n }),\n Schema.Struct({\n kind: Schema.Literal(\"oauth\"),\n accessToken: Schema.String,\n refreshToken: Schema.optional(Schema.String),\n expiresAt: Schema.optional(Schema.Number),\n tokenEndpoint: Schema.optional(Schema.String),\n clientId: Schema.optional(Schema.String),\n }),\n]);\n\nconst ExecutorServerConnectionJson = Schema.Struct({\n kind: Schema.optional(Schema.Literals([\"http\", \"desktop-sidecar\"])),\n key: Schema.optional(Schema.String),\n origin: Schema.String,\n apiBaseUrl: Schema.optional(Schema.String),\n displayName: Schema.optional(Schema.String),\n auth: Schema.optional(ExecutorServerAuthJson),\n});\n\nconst ExecutorLocalServerManifestJson = Schema.Struct({\n version: Schema.Literal(1),\n kind: Schema.Literals([\"cli-daemon\", \"desktop-sidecar\", \"foreground\"]),\n pid: Schema.Number,\n startedAt: Schema.String,\n dataDir: Schema.String,\n scopeDir: Schema.NullOr(Schema.String),\n connection: ExecutorServerConnectionJson,\n owner: Schema.Struct({\n client: Schema.Literals([\"cli\", \"desktop\"]),\n version: Schema.NullOr(Schema.String),\n executablePath: Schema.NullOr(Schema.String),\n }),\n});\n\nconst decodeUnknownJsonOption = Schema.decodeUnknownOption(Schema.UnknownFromJsonString);\nconst decodeExecutorLocalServerManifestJson = Schema.decodeUnknownOption(\n ExecutorLocalServerManifestJson,\n);\n\nconst canNormalizeServerOrigin = (origin: string): boolean => {\n const trimmed = stripTrailingSlash(origin.trim());\n if (!trimmed) return true;\n return URL.canParse(/^https?:\\/\\//.test(trimmed) ? trimmed : `http://${trimmed}`);\n};\n\nexport const parseExecutorLocalServerManifest = (\n raw: string,\n): ExecutorLocalServerManifest | null => {\n const json = decodeUnknownJsonOption(raw);\n if (Option.isNone(json)) return null;\n const decoded = decodeExecutorLocalServerManifestJson(json.value);\n if (Option.isNone(decoded)) return null;\n const parsed = decoded.value;\n if (\n !Number.isInteger(parsed.pid) ||\n parsed.pid <= 0 ||\n !canNormalizeServerOrigin(parsed.connection.origin)\n ) {\n return null;\n }\n\n const connection = normalizeExecutorServerConnection(parsed.connection);\n return {\n version: 1,\n kind: parsed.kind,\n pid: parsed.pid,\n startedAt: parsed.startedAt,\n dataDir: parsed.dataDir,\n scopeDir: parsed.scopeDir,\n connection,\n owner: {\n client: parsed.owner.client,\n version: parsed.owner.version,\n executablePath: parsed.owner.executablePath,\n },\n };\n};\n\nexport const serializeExecutorLocalServerManifest = (\n manifest: ExecutorLocalServerManifest,\n): string => `${JSON.stringify(manifest, null, 2)}\\n`;\n","// ---------------------------------------------------------------------------\n// OAuth popup result — the message shape exchanged between the popup window\n// (opened during authorization) and the opener (the onboarding UI). Both the\n// server-side HTML generator and the client-side popup opener agree on this\n// shape so that success / failure can be communicated reliably via both\n// `postMessage` and `BroadcastChannel`.\n// ---------------------------------------------------------------------------\n\n/** Message type literal used to identify our popup results. */\nexport const OAUTH_POPUP_MESSAGE_TYPE = \"executor:oauth-result\" as const;\n\nexport type OAuthPopupResult<TAuth> =\n | ({\n readonly type: typeof OAUTH_POPUP_MESSAGE_TYPE;\n readonly ok: true;\n readonly sessionId: string;\n } & TAuth)\n | {\n readonly type: typeof OAUTH_POPUP_MESSAGE_TYPE;\n readonly ok: false;\n readonly sessionId: string | null;\n /** Short user-facing summary. */\n readonly error: string;\n /** Full technical detail. Omitted when identical to `error`. */\n readonly errorDetails?: string;\n };\n\nexport const isOAuthPopupResult = <TAuth>(value: unknown): value is OAuthPopupResult<TAuth> =>\n typeof value === \"object\" &&\n value !== null &&\n (value as { type?: unknown }).type === OAUTH_POPUP_MESSAGE_TYPE;\n","// ---------------------------------------------------------------------------\n// Wire-level HTTP errors. Lives in the SDK so plugin `HttpApiGroup`\n// definitions (which sit on the SDK side of the dependency graph and\n// must stay publishable) can declare them without dragging in the\n// server-only `@executor-js/api` package. The HTTP edge in\n// `@executor-js/api` re-exports these and pairs them with the\n// translation/capture helpers used by handlers.\n// ---------------------------------------------------------------------------\n\nimport { Schema } from \"effect\";\n\n/** Public 500 surface. Opaque by schema — only `traceId` crosses the wire. */\nexport class InternalError extends Schema.TaggedErrorClass<InternalError>()(\n \"InternalError\",\n {\n /** Opaque correlation id for backend lookup (Sentry event id, log line, etc.). */\n traceId: Schema.String,\n },\n { httpApiStatus: 500 },\n) {}\n"],"mappings":"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","names":["Schema"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/policies.ts","../src/elicitation.ts","../src/errors.ts","../src/oauth-client.ts","../src/types.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Tool policies — pattern matcher + policy resolution. Pure functions; the\n// executor stitches them into `tools.list`, `execute`, and the public\n// `executor.policies` CRUD surface. Plugins consume the same surface.\n//\n// v2: policies are owner-scoped (org | user) instead of scope-stacked. Each\n// owner contributes its first matching rule by local position; the final answer\n// is the most restrictive matched action across owners, so a user preference\n// cannot weaken an org guardrail (org = outer, user = inner).\n// ---------------------------------------------------------------------------\n\nimport { Match, Schema } from \"effect\";\n\nimport type { ToolPolicyAction, ToolPolicyRow } from \"./core-schema\";\nimport { Owner, PolicyId } from \"./ids\";\n\nexport interface ToolPolicy {\n readonly id: PolicyId;\n readonly owner: Owner;\n readonly pattern: string;\n readonly action: ToolPolicyAction;\n /** Fractional-indexing key. Lower lex order = higher precedence. */\n readonly position: string;\n readonly createdAt: Date;\n readonly updatedAt: Date;\n}\n\nexport interface CreateToolPolicyInput {\n readonly owner: Owner;\n readonly pattern: string;\n /** Optional explicit position. Defaults to a key above the current minimum\n * (top of the owner's list; highest precedence). */\n readonly action: ToolPolicyAction;\n readonly position?: string;\n}\n\nexport interface UpdateToolPolicyInput {\n readonly id: string;\n readonly owner: Owner;\n readonly pattern?: string;\n readonly action?: ToolPolicyAction;\n readonly position?: string;\n}\n\nexport interface RemoveToolPolicyInput {\n readonly id: string;\n readonly owner: Owner;\n}\n\n// ---------------------------------------------------------------------------\n// Match result.\n// ---------------------------------------------------------------------------\n\nexport interface PolicyMatch {\n readonly action: ToolPolicyAction;\n readonly pattern: string;\n readonly policyId: string;\n}\n\nexport type PolicySource = \"user\" | \"plugin-default\";\n\nexport interface EffectivePolicy {\n readonly action: ToolPolicyAction;\n readonly source: PolicySource;\n readonly pattern?: string;\n readonly policyId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Pattern matching. Grammar (matched against the full tool address\n// `<integration>.<owner>.<connection>.<tool>` or a shorter form the executor\n// passes in):\n// - universal: `*`\n// - exact: `vercel.dns.create`\n// - subtree (trailing `*`): `vercel.dns.*` — the literal prefix plus anything deeper\n// - plugin-wide: `vercel.*`\n// - mid-segment `*`: `vercel.*.*.dns.create` — each NON-trailing `*` matches\n// EXACTLY ONE segment (e.g. wildcard the owner/connection\n// segments to target a tool across every connection).\n// A `*` is always a complete segment: mid-pattern it consumes one segment,\n// trailing it is a subtree. Partial wildcards (`me*`) and a leading `*` (other\n// than the universal `*`) are rejected by `isValidPattern`.\n// ---------------------------------------------------------------------------\n\nexport const matchPattern = (pattern: string, toolId: string): boolean => {\n if (pattern === \"*\") return true;\n const patternSegments = pattern.split(\".\");\n const toolSegments = toolId.split(\".\");\n for (let i = 0; i < patternSegments.length; i++) {\n const seg = patternSegments[i]!;\n if (seg === \"*\") {\n // Trailing `*` is a subtree: the literal prefix already matched, so the\n // address matches at this position and anything deeper (or nothing).\n if (i === patternSegments.length - 1) return toolSegments.length >= i;\n // A non-trailing `*` consumes EXACTLY ONE segment; one must exist here.\n if (i >= toolSegments.length) return false;\n continue;\n }\n if (i >= toolSegments.length || toolSegments[i] !== seg) return false;\n }\n // Pattern exhausted with no trailing `*`: an exact match requires equal length.\n return patternSegments.length === toolSegments.length;\n};\n\nexport const isValidPattern = (pattern: string): boolean => {\n if (pattern.length === 0) return false;\n if (pattern === \"*\") return true;\n if (pattern.startsWith(\".\") || pattern.endsWith(\".\")) return false;\n if (pattern.includes(\"..\")) return false;\n if (pattern.startsWith(\"*\")) return false;\n const segments = pattern.split(\".\");\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i]!;\n if (seg.length === 0) return false;\n // A `*` segment must be the WHOLE segment — no partial wildcards (`me*`).\n // A `*` is valid mid-pattern (one segment) or trailing (subtree).\n if (seg.includes(\"*\") && seg !== \"*\") return false;\n }\n return true;\n};\n\n// ---------------------------------------------------------------------------\n// Resolution — each owner contributes its first matching rule by local\n// position; the most restrictive matched action across owners wins. Caller\n// passes an `ownerRank` so the resolver doesn't need to know which owner is\n// the outer guardrail.\n// ---------------------------------------------------------------------------\n\nexport const comparePolicyRow = (\n a: Pick<ToolPolicyRow, \"position\" | \"id\">,\n b: Pick<ToolPolicyRow, \"position\" | \"id\">,\n): number => {\n const pa = a.position;\n const pb = b.position;\n if (pa < pb) return -1;\n if (pa > pb) return 1;\n const ia = a.id;\n const ib = b.id;\n return ia < ib ? -1 : ia > ib ? 1 : 0;\n};\n\nconst actionRestrictionRank = (action: ToolPolicyAction): number =>\n Match.value(action).pipe(\n Match.when(\"block\", () => 3),\n Match.when(\"require_approval\", () => 2),\n Match.when(\"approve\", () => 1),\n Match.exhaustive,\n );\n\nconst moreRestrictive = <T extends { readonly action: ToolPolicyAction }>(\n current: T | undefined,\n candidate: T,\n): T => {\n if (!current) return candidate;\n const currentRank = actionRestrictionRank(current.action);\n const candidateRank = actionRestrictionRank(candidate.action);\n return candidateRank > currentRank ? candidate : current;\n};\n\nexport const resolveToolPolicy = (\n toolId: string,\n policies: readonly ToolPolicyRow[],\n ownerRank: (row: Pick<ToolPolicyRow, \"owner\">) => number,\n): PolicyMatch | undefined => {\n if (policies.length === 0) return undefined;\n const sorted = [...policies].sort((a, b) => {\n const sa = ownerRank(a);\n const sb = ownerRank(b);\n if (sa !== sb) return sa - sb;\n return comparePolicyRow(a, b);\n });\n const firstMatchByOwner = new Map<string, PolicyMatch>();\n for (const row of sorted) {\n if (firstMatchByOwner.has(row.owner)) continue;\n if (matchPattern(row.pattern, toolId)) {\n firstMatchByOwner.set(row.owner, {\n action: row.action as ToolPolicyAction,\n pattern: row.pattern,\n policyId: row.id,\n });\n }\n }\n let selected: PolicyMatch | undefined;\n for (const match of firstMatchByOwner.values()) {\n selected = moreRestrictive(selected, match);\n }\n return selected;\n};\n\n// ---------------------------------------------------------------------------\n// Layered resolution — user-authored rules + plugin default `requiresApproval`.\n// ---------------------------------------------------------------------------\n\nconst liftPlugin = (defaultRequiresApproval: boolean | undefined): EffectivePolicy =>\n defaultRequiresApproval\n ? { action: \"require_approval\", source: \"plugin-default\" }\n : { action: \"approve\", source: \"plugin-default\" };\n\nconst liftUser = (match: PolicyMatch): EffectivePolicy => ({\n action: match.action,\n source: \"user\",\n pattern: match.pattern,\n policyId: match.policyId,\n});\n\nexport const resolveEffectivePolicy = (\n toolId: string,\n policies: readonly ToolPolicyRow[],\n ownerRank: (row: Pick<ToolPolicyRow, \"owner\">) => number,\n defaultRequiresApproval?: boolean,\n): EffectivePolicy => {\n const match = resolveToolPolicy(toolId, policies, ownerRank);\n return match ? liftUser(match) : liftPlugin(defaultRequiresApproval);\n};\n\nexport const effectivePolicyFromSorted = (\n toolId: string,\n sortedPolicies: readonly (Pick<ToolPolicy, \"pattern\" | \"action\" | \"id\"> &\n Partial<Pick<ToolPolicy, \"owner\">>)[],\n defaultRequiresApproval?: boolean,\n): EffectivePolicy => {\n const firstMatchByOwner = new Map<string, EffectivePolicy>();\n for (const p of sortedPolicies) {\n const ownerKey = \"owner\" in p && p.owner ? String(p.owner) : \"__flat__\";\n if (firstMatchByOwner.has(ownerKey)) continue;\n if (matchPattern(p.pattern, toolId)) {\n firstMatchByOwner.set(ownerKey, {\n action: p.action,\n source: \"user\",\n pattern: p.pattern,\n policyId: p.id,\n });\n }\n }\n let selected: EffectivePolicy | undefined;\n for (const match of firstMatchByOwner.values()) {\n selected = moreRestrictive(selected, match);\n }\n return selected ?? liftPlugin(defaultRequiresApproval);\n};\n\n// ---------------------------------------------------------------------------\n// Row → public projection.\n// ---------------------------------------------------------------------------\n\nexport const rowToToolPolicy = (row: ToolPolicyRow): ToolPolicy => ({\n id: PolicyId.make(row.id),\n owner: row.owner as Owner,\n pattern: row.pattern,\n action: row.action as ToolPolicyAction,\n position: row.position,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n});\n\nexport const ToolPolicyActionSchema = Schema.Literals([\"approve\", \"require_approval\", \"block\"]);\n","import { Effect, Schema } from \"effect\";\n\nimport { ElicitationId, ToolAddress } from \"./ids\";\n\n/* A tool that needs user input mid-call suspends and the host's `onElicitation`\n * handler (executor-level, overridable per `execute`) answers. Tools that never\n * elicit never trigger it. Schema-tagged so requests/responses cross the wire. */\n\n/** Tool needs structured input from the user (render a form). */\nexport const FormElicitation = Schema.TaggedStruct(\"FormElicitation\", {\n message: Schema.String,\n /** JSON Schema describing the fields to collect. */\n requestedSchema: Schema.Record(Schema.String, Schema.Unknown),\n});\nexport type FormElicitation = typeof FormElicitation.Type;\n\n/** Tool needs the user to visit a URL (OAuth, approval page, etc.). */\nexport const UrlElicitation = Schema.TaggedStruct(\"UrlElicitation\", {\n message: Schema.String,\n url: Schema.String,\n /** Unique id so the host can correlate the callback. */\n elicitationId: ElicitationId,\n});\nexport type UrlElicitation = typeof UrlElicitation.Type;\n\nexport type ElicitationRequest = FormElicitation | UrlElicitation;\n\nexport const ElicitationAction = Schema.Literals([\"accept\", \"decline\", \"cancel\"]);\nexport type ElicitationAction = typeof ElicitationAction.Type;\n\nexport const ElicitationResponse = Schema.Struct({\n action: ElicitationAction,\n /** Present when `action` is \"accept\" — the data the user provided. */\n content: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n});\nexport type ElicitationResponse = typeof ElicitationResponse.Type;\n\n/** Handler input — the tool address being invoked, its args, and the request. */\nexport interface ElicitationContext {\n readonly address: ToolAddress;\n readonly args: unknown;\n readonly request: ElicitationRequest;\n}\n\n/** Host-provided handler the SDK calls when a tool suspends for input. */\nexport type ElicitationHandler = (ctx: ElicitationContext) => Effect.Effect<ElicitationResponse>;\n\n/** Executor-level elicitation policy: a handler, or `\"accept-all\"` to\n * auto-accept every request (tests / non-interactive hosts). */\nexport type OnElicitation = ElicitationHandler | \"accept-all\";\n\n/** Per-call options for `execute`. */\nexport interface InvokeOptions {\n /** Override the executor-level handler for this single call. */\n readonly onElicitation?: OnElicitation;\n}\n\n/** A tool was declined or cancelled during elicitation. */\nexport class ElicitationDeclinedError extends Schema.TaggedErrorClass<ElicitationDeclinedError>()(\n \"ElicitationDeclinedError\",\n {\n address: ToolAddress,\n action: Schema.Literals([\"decline\", \"cancel\"]),\n },\n) {\n // Derived message so telemetry (span status, logs) labels the failure\n // instead of rendering an Error with an empty message.\n override get message(): string {\n return `Tool approval ${this.action === \"cancel\" ? \"cancelled\" : \"declined\"}: ${this.address}`;\n }\n}\n","import { Schema } from \"effect\";\n\nimport { ElicitationDeclinedError } from \"./elicitation\";\nimport type { StorageFailure } from \"./fuma-runtime\";\nimport { ConnectionName, IntegrationSlug, Owner, ProviderKey, ToolAddress } from \"./ids\";\n\n/* The failure set the SDK surfaces. `execute`'s invoke failures are ported from\n * v1 but re-keyed by `address` (the full `tools.<integration>.<owner>.<connection>.<tool>`\n * handle) instead of an opaque tool id. Storage failures reuse fuma-runtime's\n * `StorageError`/`UniqueViolationError` (`StorageFailure`) — not redefined here. */\n\n// ---------------------------------------------------------------------------\n// Tool lifecycle\n// ---------------------------------------------------------------------------\n\n/* Tagged errors without an explicit `message` field define a `message` getter:\n * `Schema.TaggedErrorClass` instances are real Errors with `message: \"\"`, and\n * an empty message propagates everywhere errors are rendered — span\n * status.message in the tracer, Cause.pretty output, log lines — leaving the\n * failure unlabeled in telemetry. The getter is derived from the schema fields\n * (not an own property), so encoding/serialization is unaffected. */\n\nexport class ToolNotFoundError extends Schema.TaggedErrorClass<ToolNotFoundError>()(\n \"ToolNotFoundError\",\n {\n address: ToolAddress,\n suggestions: Schema.optional(Schema.Array(ToolAddress)),\n },\n) {\n override get message(): string {\n return `Tool not found: ${this.address}`;\n }\n}\n\nexport class ToolInvocationError extends Schema.TaggedErrorClass<ToolInvocationError>()(\n \"ToolInvocationError\",\n {\n address: ToolAddress,\n message: Schema.String,\n cause: Schema.optional(Schema.Unknown),\n },\n) {}\n\n/** Tool invocation was rejected because a workspace `tool_policy` rule with\n * `action: \"block\"` matched. `pattern` is the matched policy pattern. */\nexport class ToolBlockedError extends Schema.TaggedErrorClass<ToolBlockedError>()(\n \"ToolBlockedError\",\n {\n address: ToolAddress,\n pattern: Schema.String,\n },\n) {\n override get message(): string {\n return `Tool blocked by policy \"${this.pattern}\": ${this.address}`;\n }\n}\n\n/** Tool row exists but its owning plugin isn't loaded in this executor config. */\nexport class PluginNotLoadedError extends Schema.TaggedErrorClass<PluginNotLoadedError>()(\n \"PluginNotLoadedError\",\n {\n address: ToolAddress,\n pluginId: Schema.String,\n },\n) {\n override get message(): string {\n return `Plugin \"${this.pluginId}\" is not loaded for tool: ${this.address}`;\n }\n}\n\n/** Tool was found but its owning plugin has no `invokeTool` handler. */\nexport class NoHandlerError extends Schema.TaggedErrorClass<NoHandlerError>()(\"NoHandlerError\", {\n address: ToolAddress,\n pluginId: Schema.String,\n}) {\n override get message(): string {\n return `Plugin \"${this.pluginId}\" has no invokeTool handler for tool: ${this.address}`;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Integration / connection lifecycle\n// ---------------------------------------------------------------------------\n\nexport class IntegrationNotFoundError extends Schema.TaggedErrorClass<IntegrationNotFoundError>()(\n \"IntegrationNotFoundError\",\n { slug: IntegrationSlug },\n) {\n override get message(): string {\n return `Integration not found: ${this.slug}`;\n }\n}\n\n/** An \"add integration\" operation targeted a slug (namespace) that is already\n * registered. The core `integrations.register` primitive upserts by design\n * (for idempotent boot re-registration); add-operation layers gate on this to\n * prevent silently clobbering an existing integration's tools, connections,\n * and policies. */\nexport class IntegrationAlreadyExistsError extends Schema.TaggedErrorClass<IntegrationAlreadyExistsError>()(\n \"IntegrationAlreadyExistsError\",\n { slug: IntegrationSlug },\n { httpApiStatus: 409 },\n) {\n override get message(): string {\n return `Integration already exists: ${this.slug}`;\n }\n}\n\n/** `integrations.remove` was called on an integration declared statically by a\n * plugin at startup (`canRemove: false`). */\nexport class IntegrationRemovalNotAllowedError extends Schema.TaggedErrorClass<IntegrationRemovalNotAllowedError>()(\n \"IntegrationRemovalNotAllowedError\",\n { slug: IntegrationSlug },\n) {\n override get message(): string {\n return `Integration cannot be removed (declared statically by a plugin): ${this.slug}`;\n }\n}\n\nexport class ConnectionNotFoundError extends Schema.TaggedErrorClass<ConnectionNotFoundError>()(\n \"ConnectionNotFoundError\",\n {\n owner: Owner,\n integration: IntegrationSlug,\n name: ConnectionName,\n },\n) {\n override get message(): string {\n return `Connection not found: ${this.integration}.${this.owner}.${this.name}`;\n }\n}\n\n/** A connection create request was rejected before anything was written: the\n * input is structurally invalid (no credential inputs for a credentialed\n * template, mixed pasted/external origins, …) or targets owner `user` in a\n * context that has no user subject. The message says which — it is safe to\n * show to the caller. */\nexport class InvalidConnectionInputError extends Schema.TaggedErrorClass<InvalidConnectionInputError>()(\n \"InvalidConnectionInputError\",\n { message: Schema.String },\n) {}\n\n/** A connection references a credential provider key that isn't registered on\n * the executor. */\nexport class CredentialProviderNotRegisteredError extends Schema.TaggedErrorClass<CredentialProviderNotRegisteredError>()(\n \"CredentialProviderNotRegisteredError\",\n { provider: ProviderKey },\n) {\n override get message(): string {\n return `Credential provider not registered: ${this.provider}`;\n }\n}\n\n/** A connection's value could not be resolved — the provider returned nothing,\n * or an OAuth token refresh failed and the user must re-auth. */\nexport class CredentialResolutionError extends Schema.TaggedErrorClass<CredentialResolutionError>()(\n \"CredentialResolutionError\",\n {\n owner: Owner,\n integration: IntegrationSlug,\n name: ConnectionName,\n message: Schema.String,\n /** True when the stored grant is permanently invalid and the user must\n * sign in again (RFC 6749 §5.2 invalid_grant and friends). */\n reauthRequired: Schema.optional(Schema.Boolean),\n },\n) {}\n\n// ---------------------------------------------------------------------------\n// Union — the failure channel of `execute`.\n// ---------------------------------------------------------------------------\n\nexport type ExecuteError =\n | ToolNotFoundError\n | ToolInvocationError\n | ToolBlockedError\n | PluginNotLoadedError\n | NoHandlerError\n | ConnectionNotFoundError\n | CredentialProviderNotRegisteredError\n | CredentialResolutionError\n | ElicitationDeclinedError\n | StorageFailure;\n\n/** Convenience union spanning every typed error the SDK raises. */\nexport type ExecutorError =\n | ExecuteError\n | IntegrationNotFoundError\n | IntegrationRemovalNotAllowedError;\n","import type { Effect } from \"effect\";\nimport { Schema } from \"effect\";\n\nimport type { Connection } from \"./connection\";\nimport type { StorageFailure } from \"./fuma-runtime\";\nimport {\n type AuthTemplateSlug,\n type ConnectionName,\n type IntegrationSlug,\n OAuthClientSlug,\n OAuthState,\n type Owner,\n} from \"./ids\";\n\n/* The v2 OAuth surface contracts. OAuth is a credential mechanism, not an\n * integration type. A client is a registered app; running its flow mints a\n * Connection. The client is self-contained (carries its own endpoints) and\n * integration-independent, so the same app can back connections on whatever\n * integrations share that provider.\n *\n * The OAuth 2.1 *implementation* (PKCE, DCR, token exchange + refresh) lives in\n * `oauth-helpers` / `oauth-discovery` / `oauth-service`; these are the public\n * input/output shapes the executor's `oauth.*` namespace speaks. */\n\nexport type OAuthGrant = \"authorization_code\" | \"client_credentials\";\n\n/** Provider OAuth config an integration declares as one of its auth templates —\n * what to request. (The flow itself runs off the self-contained OAuthClient.)\n * Keyed `kind: \"oauth2\"` like every auth method across the plugins. */\nexport interface OAuthAuthentication {\n readonly slug: AuthTemplateSlug;\n readonly kind: \"oauth2\";\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n readonly scopes: readonly string[];\n}\n\n/** A registered OAuth app — pure app identity: clientId/secret + its endpoints.\n * Owner-scoped: a shared org app or a user's own BYO app. The app does NOT carry\n * scopes — what to request is the INTEGRATION's concern (`OAuthAuthentication.\n * scopes`, surfaced via the declared auth method), so the same app can back any\n * integration without pinning a scope set. */\nexport interface OAuthClient {\n readonly owner: Owner;\n readonly slug: OAuthClientSlug;\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n readonly grant: OAuthGrant;\n readonly clientId: string;\n /** The literal client secret. Stored out-of-band in the credential provider\n * (vault item id), never inline. Empty string for public / PKCE clients. */\n readonly clientSecret: string;\n /** RFC 8707 Resource Indicator (MCP). Carried so the refresh request can keep\n * the re-minted token bound to the same resource. Null/omitted otherwise. */\n readonly resource?: string | null;\n}\n\nexport type OAuthClientOrigin =\n | { readonly kind: \"manual\" }\n | {\n readonly kind: \"dynamic_client_registration\";\n readonly integration?: IntegrationSlug | null;\n };\n\nexport type CreateOAuthClientInput = OAuthClient & {\n readonly origin?: OAuthClientOrigin;\n};\n\n/** Metadata-only projection of a registered client for listing in the UI.\n * Deliberately omits `clientSecret` — the secret is never returned over the\n * read surface. `clientId` is included (it is not a secret; it is sent in the\n * authorize URL the user's browser visits). */\nexport interface OAuthClientSummary {\n readonly owner: Owner;\n readonly slug: OAuthClientSlug;\n readonly grant: OAuthGrant;\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n readonly resource?: string | null;\n readonly clientId: string;\n readonly origin: OAuthClientOrigin;\n}\n\n/** Flow-aware result of `oauth.start` — the status says what's next. */\nexport type ConnectResult =\n | { readonly status: \"connected\"; readonly connection: Connection }\n | {\n readonly status: \"redirect\";\n readonly authorizationUrl: string;\n readonly state: OAuthState;\n };\n\n/** Start a flow through a client to mint a connection for one integration.\n * `template` is the integration's oauth template the minted token is applied\n * through. */\nexport interface OAuthStartInput {\n readonly client: OAuthClientSlug;\n /** The owner that owns `client`. Supplied explicitly (the picker knows it), so\n * a Personal connection can be minted through a shared Workspace app without\n * any owner-derivation rule. A Workspace connection must use a Workspace app. */\n readonly clientOwner: Owner;\n /** The owner the minted CONNECTION is saved under (may differ from `clientOwner`). */\n readonly owner: Owner;\n readonly name: ConnectionName;\n readonly integration: IntegrationSlug;\n readonly template: AuthTemplateSlug;\n readonly identityLabel?: string | null;\n /** Browser-facing callback URL for this flow. Defaults to the executor's configured redirectUri. */\n readonly redirectUri?: string | null;\n}\n\nexport interface OAuthCompleteInput {\n readonly state: OAuthState;\n readonly code: string;\n}\n\n/** Probe a base/issuer URL for OAuth 2.1 authorization-server metadata so the\n * onboarding UI can pre-fill a client's endpoints. */\nexport interface OAuthProbeInput {\n readonly url: string;\n}\n\nexport interface OAuthProbeResult {\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n /** RFC 8707 resource indicator discovered from protected-resource metadata.\n * Persist this on DCR clients so authorize/token/refresh requests stay bound\n * to the protected resource. */\n readonly resource?: string | null;\n readonly scopesSupported?: readonly string[];\n /** Whether the server advertises dynamic client registration (RFC 7591). */\n readonly registrationEndpoint?: string | null;\n /** RFC 8414 `token_endpoint_auth_methods_supported`. Surfaced so DCR can pick\n * a public (\"none\") client when the server allows it. */\n readonly tokenEndpointAuthMethodsSupported?: readonly string[];\n}\n\n/** Mint an OAuth client via RFC 7591 Dynamic Client Registration and persist it.\n * The user pastes NO client id/secret — the authorization server mints a\n * (public, PKCE) client which is stored as an owner-scoped `oauth_client`. */\nexport interface RegisterDynamicClientInput {\n readonly owner: Owner;\n readonly slug: OAuthClientSlug;\n /** RFC 7591 registration endpoint advertised by the authorization server. */\n readonly registrationEndpoint: string;\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n /** RFC 8707 Resource Indicator (MCP). Persisted on the minted client when known. */\n readonly resource?: string | null;\n readonly scopes: readonly string[];\n /** Auth methods the server advertises. When it allows `none` a public\n * (PKCE-only, no secret) client is registered; otherwise `client_secret_post`. */\n readonly tokenEndpointAuthMethodsSupported?: readonly string[];\n /** Human label for the registered app (RFC 7591 `client_name`). */\n readonly clientName?: string;\n /** Browser-facing callback URL to register. Defaults to the executor's configured redirectUri. */\n readonly redirectUri?: string | null;\n /** Integration that requested this dynamic client, when known. */\n readonly originIntegration?: IntegrationSlug | null;\n}\n\nexport class OAuthStartError extends Schema.TaggedErrorClass<OAuthStartError>()(\"OAuthStartError\", {\n message: Schema.String,\n}) {}\n\nexport class OAuthCompleteError extends Schema.TaggedErrorClass<OAuthCompleteError>()(\n \"OAuthCompleteError\",\n {\n message: Schema.String,\n /** True when the auth-code exchange failed in a way the user must restart. */\n restartRequired: Schema.optional(Schema.Boolean),\n },\n) {}\n\nexport class OAuthProbeError extends Schema.TaggedErrorClass<OAuthProbeError>()(\"OAuthProbeError\", {\n message: Schema.String,\n}) {}\n\nexport class OAuthRegisterDynamicError extends Schema.TaggedErrorClass<OAuthRegisterDynamicError>()(\n \"OAuthRegisterDynamicError\",\n { message: Schema.String },\n) {}\n\nexport class OAuthSessionNotFoundError extends Schema.TaggedErrorClass<OAuthSessionNotFoundError>()(\n \"OAuthSessionNotFoundError\",\n { state: OAuthState },\n) {}\n\n/** The OAuth surface the executor's `oauth.*` namespace and `ctx.oauth` expose.\n * Implemented by `makeOAuthService` (oauth-service.ts), wired by the executor\n * with the deps it needs to mint connections. */\nexport interface OAuthService {\n readonly createClient: (\n input: CreateOAuthClientInput,\n ) => Effect.Effect<OAuthClientSlug, StorageFailure>;\n /** Mint a client via RFC 7591 Dynamic Client Registration (no pre-shared\n * client id/secret) and persist it as an owner-scoped `oauth_client`. */\n readonly registerDynamicClient: (\n input: RegisterDynamicClientInput,\n ) => Effect.Effect<OAuthClientSlug, OAuthRegisterDynamicError | StorageFailure>;\n /** All registered clients visible to the caller (their org's shared clients +\n * their own user clients), as metadata-only summaries — never the secret. */\n readonly listClients: () => Effect.Effect<readonly OAuthClientSummary[], StorageFailure>;\n /** Permanently remove a registered OAuth app, keyed by (owner, slug). The\n * owner policy on `oauth_client` prevents removing another subject's user app.\n * Idempotent: removing an already-gone app succeeds. Connections that\n * referenced the slug keep their stored value and fail at the next token\n * refresh, prompting a reconnect — this op never cascades into connections. */\n readonly removeClient: (\n owner: Owner,\n slug: OAuthClientSlug,\n ) => Effect.Effect<void, StorageFailure>;\n readonly start: (\n input: OAuthStartInput,\n ) => Effect.Effect<ConnectResult, OAuthStartError | StorageFailure>;\n readonly complete: (\n input: OAuthCompleteInput,\n ) => Effect.Effect<Connection, OAuthCompleteError | OAuthSessionNotFoundError | StorageFailure>;\n readonly cancel: (state: OAuthState) => Effect.Effect<void, StorageFailure>;\n readonly probe: (\n input: OAuthProbeInput,\n ) => Effect.Effect<OAuthProbeResult, OAuthProbeError | StorageFailure>;\n}\n","// ---------------------------------------------------------------------------\n// Public projections beyond the core domain types. The integration / connection\n// / tool views live in their own domain files (`integration.ts`, `connection.ts`,\n// `tool.ts`); this file holds the schema-side views and the onboarding URL\n// autodetect result.\n// ---------------------------------------------------------------------------\n\nimport { Schema } from \"effect\";\n\nimport { ToolAddress } from \"./ids\";\n\n// ---------------------------------------------------------------------------\n// ToolSchemaView — the full schema-side view of a tool, returned by\n// `executor.tools.schema(address)`. Includes JSON schema roots plus shared\n// definitions for schema exploration, and optionally TypeScript preview strings.\n// ---------------------------------------------------------------------------\n\nexport const ToolSchemaView = Schema.Struct({\n address: ToolAddress,\n name: Schema.optional(Schema.String),\n description: Schema.optional(Schema.String),\n inputSchema: Schema.optional(Schema.Unknown),\n outputSchema: Schema.optional(Schema.Unknown),\n schemaDefinitions: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n inputTypeScript: Schema.optional(Schema.String),\n outputTypeScript: Schema.optional(Schema.String),\n typeScriptDefinitions: Schema.optional(Schema.Record(Schema.String, Schema.String)),\n});\nexport type ToolSchemaView = typeof ToolSchemaView.Type;\n\n// ---------------------------------------------------------------------------\n// Integration detection — optional capability on `PluginSpec.detect`. When a\n// user pastes a URL in the onboarding UI, `executor.integrations.detect(url)`\n// asks every plugin \"is this yours?\" and returns the best-confidence match so\n// the UI can auto-fill the onboarding form for the right plugin.\n// ---------------------------------------------------------------------------\n\nexport const IntegrationDetectionResult = Schema.Struct({\n /** Plugin id that recognized the URL (e.g. \"openapi\", \"graphql\"). */\n kind: Schema.String,\n /** Confidence tier — UI uses this to pick a winner when multiple plugins\n * claim a URL. */\n confidence: Schema.Literals([\"high\", \"medium\", \"low\"]),\n /** The (possibly normalized) endpoint the plugin will use. */\n endpoint: Schema.String,\n /** Human-readable name suggestion, typically derived from spec title or URL. */\n name: Schema.String,\n /** Slug suggestion — the plugin's recommendation for the integration slug. */\n slug: Schema.String,\n});\nexport type IntegrationDetectionResult = typeof IntegrationDetectionResult.Type;\n"],"mappings":";;;;;;;;;;;;AAWA,SAAS,OAAO,cAAc;AAyEvB,IAAM,eAAe,CAAC,SAAiB,WAA4B;AACxE,MAAI,YAAY,IAAK,QAAO;AAC5B,QAAM,kBAAkB,QAAQ,MAAM,GAAG;AACzC,QAAM,eAAe,OAAO,MAAM,GAAG;AACrC,WAAS,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;AAC/C,UAAM,MAAM,gBAAgB,CAAC;AAC7B,QAAI,QAAQ,KAAK;AAGf,UAAI,MAAM,gBAAgB,SAAS,EAAG,QAAO,aAAa,UAAU;AAEpE,UAAI,KAAK,aAAa,OAAQ,QAAO;AACrC;AAAA,IACF;AACA,QAAI,KAAK,aAAa,UAAU,aAAa,CAAC,MAAM,IAAK,QAAO;AAAA,EAClE;AAEA,SAAO,gBAAgB,WAAW,aAAa;AACjD;AAEO,IAAM,iBAAiB,CAAC,YAA6B;AAC1D,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI,YAAY,IAAK,QAAO;AAC5B,MAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,EAAG,QAAO;AAC7D,MAAI,QAAQ,SAAS,IAAI,EAAG,QAAO;AACnC,MAAI,QAAQ,WAAW,GAAG,EAAG,QAAO;AACpC,QAAM,WAAW,QAAQ,MAAM,GAAG;AAClC,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,MAAM,SAAS,CAAC;AACtB,QAAI,IAAI,WAAW,EAAG,QAAO;AAG7B,QAAI,IAAI,SAAS,GAAG,KAAK,QAAQ,IAAK,QAAO;AAAA,EAC/C;AACA,SAAO;AACT;AASO,IAAM,mBAAmB,CAC9B,GACA,MACW;AACX,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,EAAE;AACb,MAAI,KAAK,GAAI,QAAO;AACpB,MAAI,KAAK,GAAI,QAAO;AACpB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,EAAE;AACb,SAAO,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI;AACtC;AAEA,IAAM,wBAAwB,CAAC,WAC7B,MAAM,MAAM,MAAM,EAAE;AAAA,EAClB,MAAM,KAAK,SAAS,MAAM,CAAC;AAAA,EAC3B,MAAM,KAAK,oBAAoB,MAAM,CAAC;AAAA,EACtC,MAAM,KAAK,WAAW,MAAM,CAAC;AAAA,EAC7B,MAAM;AACR;AAEF,IAAM,kBAAkB,CACtB,SACA,cACM;AACN,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,cAAc,sBAAsB,QAAQ,MAAM;AACxD,QAAM,gBAAgB,sBAAsB,UAAU,MAAM;AAC5D,SAAO,gBAAgB,cAAc,YAAY;AACnD;AAEO,IAAM,oBAAoB,CAC/B,QACA,UACA,cAC4B;AAC5B,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAM,SAAS,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM;AAC1C,UAAM,KAAK,UAAU,CAAC;AACtB,UAAM,KAAK,UAAU,CAAC;AACtB,QAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,WAAO,iBAAiB,GAAG,CAAC;AAAA,EAC9B,CAAC;AACD,QAAM,oBAAoB,oBAAI,IAAyB;AACvD,aAAW,OAAO,QAAQ;AACxB,QAAI,kBAAkB,IAAI,IAAI,KAAK,EAAG;AACtC,QAAI,aAAa,IAAI,SAAS,MAAM,GAAG;AACrC,wBAAkB,IAAI,IAAI,OAAO;AAAA,QAC/B,QAAQ,IAAI;AAAA,QACZ,SAAS,IAAI;AAAA,QACb,UAAU,IAAI;AAAA,MAChB,CAAC;AAAA,IACH;AAAA,EACF;AACA,MAAI;AACJ,aAAW,SAAS,kBAAkB,OAAO,GAAG;AAC9C,eAAW,gBAAgB,UAAU,KAAK;AAAA,EAC5C;AACA,SAAO;AACT;AAMA,IAAM,aAAa,CAAC,4BAClB,0BACI,EAAE,QAAQ,oBAAoB,QAAQ,iBAAiB,IACvD,EAAE,QAAQ,WAAW,QAAQ,iBAAiB;AAEpD,IAAM,WAAW,CAAC,WAAyC;AAAA,EACzD,QAAQ,MAAM;AAAA,EACd,QAAQ;AAAA,EACR,SAAS,MAAM;AAAA,EACf,UAAU,MAAM;AAClB;AAEO,IAAM,yBAAyB,CACpC,QACA,UACA,WACA,4BACoB;AACpB,QAAM,QAAQ,kBAAkB,QAAQ,UAAU,SAAS;AAC3D,SAAO,QAAQ,SAAS,KAAK,IAAI,WAAW,uBAAuB;AACrE;AAEO,IAAM,4BAA4B,CACvC,QACA,gBAEA,4BACoB;AACpB,QAAM,oBAAoB,oBAAI,IAA6B;AAC3D,aAAW,KAAK,gBAAgB;AAC9B,UAAM,WAAW,WAAW,KAAK,EAAE,QAAQ,OAAO,EAAE,KAAK,IAAI;AAC7D,QAAI,kBAAkB,IAAI,QAAQ,EAAG;AACrC,QAAI,aAAa,EAAE,SAAS,MAAM,GAAG;AACnC,wBAAkB,IAAI,UAAU;AAAA,QAC9B,QAAQ,EAAE;AAAA,QACV,QAAQ;AAAA,QACR,SAAS,EAAE;AAAA,QACX,UAAU,EAAE;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AACA,MAAI;AACJ,aAAW,SAAS,kBAAkB,OAAO,GAAG;AAC9C,eAAW,gBAAgB,UAAU,KAAK;AAAA,EAC5C;AACA,SAAO,YAAY,WAAW,uBAAuB;AACvD;AAMO,IAAM,kBAAkB,CAAC,SAAoC;AAAA,EAClE,IAAI,SAAS,KAAK,IAAI,EAAE;AAAA,EACxB,OAAO,IAAI;AAAA,EACX,SAAS,IAAI;AAAA,EACb,QAAQ,IAAI;AAAA,EACZ,UAAU,IAAI;AAAA,EACd,WAAW,IAAI;AAAA,EACf,WAAW,IAAI;AACjB;AAEO,IAAM,yBAAyB,OAAO,SAAS,CAAC,WAAW,oBAAoB,OAAO,CAAC;;;AC/P9F,SAAiB,UAAAA,eAAc;AASxB,IAAM,kBAAkBC,QAAO,aAAa,mBAAmB;AAAA,EACpE,SAASA,QAAO;AAAA;AAAA,EAEhB,iBAAiBA,QAAO,OAAOA,QAAO,QAAQA,QAAO,OAAO;AAC9D,CAAC;AAIM,IAAM,iBAAiBA,QAAO,aAAa,kBAAkB;AAAA,EAClE,SAASA,QAAO;AAAA,EAChB,KAAKA,QAAO;AAAA;AAAA,EAEZ,eAAe;AACjB,CAAC;AAKM,IAAM,oBAAoBA,QAAO,SAAS,CAAC,UAAU,WAAW,QAAQ,CAAC;AAGzE,IAAM,sBAAsBA,QAAO,OAAO;AAAA,EAC/C,QAAQ;AAAA;AAAA,EAER,SAASA,QAAO,SAASA,QAAO,OAAOA,QAAO,QAAQA,QAAO,OAAO,CAAC;AACvE,CAAC;AAwBM,IAAM,2BAAN,cAAuCA,QAAO,iBAA2C;AAAA,EAC9F;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,QAAQA,QAAO,SAAS,CAAC,WAAW,QAAQ,CAAC;AAAA,EAC/C;AACF,EAAE;AAAA;AAAA;AAAA,EAGA,IAAa,UAAkB;AAC7B,WAAO,iBAAiB,KAAK,WAAW,WAAW,cAAc,UAAU,KAAK,KAAK,OAAO;AAAA,EAC9F;AACF;;;ACtEA,SAAS,UAAAC,eAAc;AAsBhB,IAAM,oBAAN,cAAgCC,QAAO,iBAAoC;AAAA,EAChF;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,aAAaA,QAAO,SAASA,QAAO,MAAM,WAAW,CAAC;AAAA,EACxD;AACF,EAAE;AAAA,EACA,IAAa,UAAkB;AAC7B,WAAO,mBAAmB,KAAK,OAAO;AAAA,EACxC;AACF;AAEO,IAAM,sBAAN,cAAkCA,QAAO,iBAAsC;AAAA,EACpF;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,SAASA,QAAO;AAAA,IAChB,OAAOA,QAAO,SAASA,QAAO,OAAO;AAAA,EACvC;AACF,EAAE;AAAC;AAII,IAAM,mBAAN,cAA+BA,QAAO,iBAAmC;AAAA,EAC9E;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,SAASA,QAAO;AAAA,EAClB;AACF,EAAE;AAAA,EACA,IAAa,UAAkB;AAC7B,WAAO,2BAA2B,KAAK,OAAO,MAAM,KAAK,OAAO;AAAA,EAClE;AACF;AAGO,IAAM,uBAAN,cAAmCA,QAAO,iBAAuC;AAAA,EACtF;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,UAAUA,QAAO;AAAA,EACnB;AACF,EAAE;AAAA,EACA,IAAa,UAAkB;AAC7B,WAAO,WAAW,KAAK,QAAQ,6BAA6B,KAAK,OAAO;AAAA,EAC1E;AACF;AAGO,IAAM,iBAAN,cAA6BA,QAAO,iBAAiC,EAAE,kBAAkB;AAAA,EAC9F,SAAS;AAAA,EACT,UAAUA,QAAO;AACnB,CAAC,EAAE;AAAA,EACD,IAAa,UAAkB;AAC7B,WAAO,WAAW,KAAK,QAAQ,yCAAyC,KAAK,OAAO;AAAA,EACtF;AACF;AAMO,IAAM,2BAAN,cAAuCA,QAAO,iBAA2C;AAAA,EAC9F;AAAA,EACA,EAAE,MAAM,gBAAgB;AAC1B,EAAE;AAAA,EACA,IAAa,UAAkB;AAC7B,WAAO,0BAA0B,KAAK,IAAI;AAAA,EAC5C;AACF;AAOO,IAAM,gCAAN,cAA4CA,QAAO,iBAAgD;AAAA,EACxG;AAAA,EACA,EAAE,MAAM,gBAAgB;AAAA,EACxB,EAAE,eAAe,IAAI;AACvB,EAAE;AAAA,EACA,IAAa,UAAkB;AAC7B,WAAO,+BAA+B,KAAK,IAAI;AAAA,EACjD;AACF;AAIO,IAAM,oCAAN,cAAgDA,QAAO,iBAAoD;AAAA,EAChH;AAAA,EACA,EAAE,MAAM,gBAAgB;AAC1B,EAAE;AAAA,EACA,IAAa,UAAkB;AAC7B,WAAO,oEAAoE,KAAK,IAAI;AAAA,EACtF;AACF;AAEO,IAAM,0BAAN,cAAsCA,QAAO,iBAA0C;AAAA,EAC5F;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AACF,EAAE;AAAA,EACA,IAAa,UAAkB;AAC7B,WAAO,yBAAyB,KAAK,WAAW,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI;AAAA,EAC7E;AACF;AAOO,IAAM,8BAAN,cAA0CA,QAAO,iBAA8C;AAAA,EACpG;AAAA,EACA,EAAE,SAASA,QAAO,OAAO;AAC3B,EAAE;AAAC;AAII,IAAM,uCAAN,cAAmDA,QAAO,iBAAuD;AAAA,EACtH;AAAA,EACA,EAAE,UAAU,YAAY;AAC1B,EAAE;AAAA,EACA,IAAa,UAAkB;AAC7B,WAAO,uCAAuC,KAAK,QAAQ;AAAA,EAC7D;AACF;AAIO,IAAM,4BAAN,cAAwCA,QAAO,iBAA4C;AAAA,EAChG;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,IACN,SAASA,QAAO;AAAA;AAAA;AAAA,IAGhB,gBAAgBA,QAAO,SAASA,QAAO,OAAO;AAAA,EAChD;AACF,EAAE;AAAC;;;ACrKH,SAAS,UAAAC,eAAc;AAgKhB,IAAM,kBAAN,cAA8BC,QAAO,iBAAkC,EAAE,mBAAmB;AAAA,EACjG,SAASA,QAAO;AAClB,CAAC,EAAE;AAAC;AAEG,IAAM,qBAAN,cAAiCA,QAAO,iBAAqC;AAAA,EAClF;AAAA,EACA;AAAA,IACE,SAASA,QAAO;AAAA;AAAA,IAEhB,iBAAiBA,QAAO,SAASA,QAAO,OAAO;AAAA,EACjD;AACF,EAAE;AAAC;AAEI,IAAM,kBAAN,cAA8BA,QAAO,iBAAkC,EAAE,mBAAmB;AAAA,EACjG,SAASA,QAAO;AAClB,CAAC,EAAE;AAAC;AAEG,IAAM,4BAAN,cAAwCA,QAAO,iBAA4C;AAAA,EAChG;AAAA,EACA,EAAE,SAASA,QAAO,OAAO;AAC3B,EAAE;AAAC;AAEI,IAAM,4BAAN,cAAwCA,QAAO,iBAA4C;AAAA,EAChG;AAAA,EACA,EAAE,OAAO,WAAW;AACtB,EAAE;AAAC;;;ACnLH,SAAS,UAAAC,eAAc;AAUhB,IAAM,iBAAiBC,QAAO,OAAO;AAAA,EAC1C,SAAS;AAAA,EACT,MAAMA,QAAO,SAASA,QAAO,MAAM;AAAA,EACnC,aAAaA,QAAO,SAASA,QAAO,MAAM;AAAA,EAC1C,aAAaA,QAAO,SAASA,QAAO,OAAO;AAAA,EAC3C,cAAcA,QAAO,SAASA,QAAO,OAAO;AAAA,EAC5C,mBAAmBA,QAAO,SAASA,QAAO,OAAOA,QAAO,QAAQA,QAAO,OAAO,CAAC;AAAA,EAC/E,iBAAiBA,QAAO,SAASA,QAAO,MAAM;AAAA,EAC9C,kBAAkBA,QAAO,SAASA,QAAO,MAAM;AAAA,EAC/C,uBAAuBA,QAAO,SAASA,QAAO,OAAOA,QAAO,QAAQA,QAAO,MAAM,CAAC;AACpF,CAAC;AAUM,IAAM,6BAA6BA,QAAO,OAAO;AAAA;AAAA,EAEtD,MAAMA,QAAO;AAAA;AAAA;AAAA,EAGb,YAAYA,QAAO,SAAS,CAAC,QAAQ,UAAU,KAAK,CAAC;AAAA;AAAA,EAErD,UAAUA,QAAO;AAAA;AAAA,EAEjB,MAAMA,QAAO;AAAA;AAAA,EAEb,MAAMA,QAAO;AACf,CAAC;","names":["Schema","Schema","Schema","Schema","Schema","Schema","Schema","Schema"]}
|
|
1
|
+
{"version":3,"sources":["../src/policies.ts","../src/elicitation.ts","../src/errors.ts","../src/oauth-client.ts","../src/types.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Tool policies — pattern matcher + policy resolution. Pure functions; the\n// executor stitches them into `tools.list`, `execute`, and the public\n// `executor.policies` CRUD surface. Plugins consume the same surface.\n//\n// v2: policies are owner-scoped (org | user) instead of scope-stacked. Each\n// owner contributes its first matching rule by local position; the final answer\n// is the most restrictive matched action across owners, so a user preference\n// cannot weaken an org guardrail (org = outer, user = inner).\n// ---------------------------------------------------------------------------\n\nimport { Match, Schema } from \"effect\";\n\nimport type { ToolPolicyAction, ToolPolicyRow } from \"./core-schema\";\nimport { Owner, PolicyId } from \"./ids\";\n\nexport interface ToolPolicy {\n readonly id: PolicyId;\n readonly owner: Owner;\n readonly pattern: string;\n readonly action: ToolPolicyAction;\n /** Fractional-indexing key. Lower lex order = higher precedence. */\n readonly position: string;\n readonly createdAt: Date;\n readonly updatedAt: Date;\n}\n\nexport interface CreateToolPolicyInput {\n readonly owner: Owner;\n readonly pattern: string;\n /** Optional explicit position. Defaults to a key above the current minimum\n * (top of the owner's list; highest precedence). */\n readonly action: ToolPolicyAction;\n readonly position?: string;\n}\n\nexport interface UpdateToolPolicyInput {\n readonly id: string;\n readonly owner: Owner;\n readonly pattern?: string;\n readonly action?: ToolPolicyAction;\n readonly position?: string;\n}\n\nexport interface RemoveToolPolicyInput {\n readonly id: string;\n readonly owner: Owner;\n}\n\n// ---------------------------------------------------------------------------\n// Match result.\n// ---------------------------------------------------------------------------\n\nexport interface PolicyMatch {\n readonly action: ToolPolicyAction;\n readonly pattern: string;\n readonly policyId: string;\n}\n\nexport type PolicySource = \"user\" | \"plugin-default\";\n\nexport interface EffectivePolicy {\n readonly action: ToolPolicyAction;\n readonly source: PolicySource;\n readonly pattern?: string;\n readonly policyId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Pattern matching. Grammar (matched against the full tool address\n// `<integration>.<owner>.<connection>.<tool>` or a shorter form the executor\n// passes in):\n// - universal: `*`\n// - exact: `vercel.dns.create`\n// - subtree (trailing `*`): `vercel.dns.*` — the literal prefix plus anything deeper\n// - plugin-wide: `vercel.*`\n// - mid-segment `*`: `vercel.*.*.dns.create` — each NON-trailing `*` matches\n// EXACTLY ONE segment (e.g. wildcard the owner/connection\n// segments to target a tool across every connection).\n// A `*` is always a complete segment: mid-pattern it consumes one segment,\n// trailing it is a subtree. Partial wildcards (`me*`) and a leading `*` (other\n// than the universal `*`) are rejected by `isValidPattern`.\n// ---------------------------------------------------------------------------\n\nexport const matchPattern = (pattern: string, toolId: string): boolean => {\n if (pattern === \"*\") return true;\n const patternSegments = pattern.split(\".\");\n const toolSegments = toolId.split(\".\");\n for (let i = 0; i < patternSegments.length; i++) {\n const seg = patternSegments[i]!;\n if (seg === \"*\") {\n // Trailing `*` is a subtree: the literal prefix already matched, so the\n // address matches at this position and anything deeper (or nothing).\n if (i === patternSegments.length - 1) return toolSegments.length >= i;\n // A non-trailing `*` consumes EXACTLY ONE segment; one must exist here.\n if (i >= toolSegments.length) return false;\n continue;\n }\n if (i >= toolSegments.length || toolSegments[i] !== seg) return false;\n }\n // Pattern exhausted with no trailing `*`: an exact match requires equal length.\n return patternSegments.length === toolSegments.length;\n};\n\nexport const isValidPattern = (pattern: string): boolean => {\n if (pattern.length === 0) return false;\n if (pattern === \"*\") return true;\n if (pattern.startsWith(\".\") || pattern.endsWith(\".\")) return false;\n if (pattern.includes(\"..\")) return false;\n if (pattern.startsWith(\"*\")) return false;\n const segments = pattern.split(\".\");\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i]!;\n if (seg.length === 0) return false;\n // A `*` segment must be the WHOLE segment — no partial wildcards (`me*`).\n // A `*` is valid mid-pattern (one segment) or trailing (subtree).\n if (seg.includes(\"*\") && seg !== \"*\") return false;\n }\n return true;\n};\n\n// ---------------------------------------------------------------------------\n// Resolution — each owner contributes its first matching rule by local\n// position; the most restrictive matched action across owners wins. Caller\n// passes an `ownerRank` so the resolver doesn't need to know which owner is\n// the outer guardrail.\n// ---------------------------------------------------------------------------\n\nexport const comparePolicyRow = (\n a: Pick<ToolPolicyRow, \"position\" | \"id\">,\n b: Pick<ToolPolicyRow, \"position\" | \"id\">,\n): number => {\n const pa = a.position;\n const pb = b.position;\n if (pa < pb) return -1;\n if (pa > pb) return 1;\n const ia = a.id;\n const ib = b.id;\n return ia < ib ? -1 : ia > ib ? 1 : 0;\n};\n\nconst actionRestrictionRank = (action: ToolPolicyAction): number =>\n Match.value(action).pipe(\n Match.when(\"block\", () => 3),\n Match.when(\"require_approval\", () => 2),\n Match.when(\"approve\", () => 1),\n Match.exhaustive,\n );\n\nconst moreRestrictive = <T extends { readonly action: ToolPolicyAction }>(\n current: T | undefined,\n candidate: T,\n): T => {\n if (!current) return candidate;\n const currentRank = actionRestrictionRank(current.action);\n const candidateRank = actionRestrictionRank(candidate.action);\n return candidateRank > currentRank ? candidate : current;\n};\n\nexport const resolveToolPolicy = (\n toolId: string,\n policies: readonly ToolPolicyRow[],\n ownerRank: (row: Pick<ToolPolicyRow, \"owner\">) => number,\n): PolicyMatch | undefined => {\n if (policies.length === 0) return undefined;\n const sorted = [...policies].sort((a, b) => {\n const sa = ownerRank(a);\n const sb = ownerRank(b);\n if (sa !== sb) return sa - sb;\n return comparePolicyRow(a, b);\n });\n const firstMatchByOwner = new Map<string, PolicyMatch>();\n for (const row of sorted) {\n if (firstMatchByOwner.has(row.owner)) continue;\n if (matchPattern(row.pattern, toolId)) {\n firstMatchByOwner.set(row.owner, {\n action: row.action as ToolPolicyAction,\n pattern: row.pattern,\n policyId: row.id,\n });\n }\n }\n let selected: PolicyMatch | undefined;\n for (const match of firstMatchByOwner.values()) {\n selected = moreRestrictive(selected, match);\n }\n return selected;\n};\n\n// ---------------------------------------------------------------------------\n// Layered resolution — user-authored rules + plugin default `requiresApproval`.\n// ---------------------------------------------------------------------------\n\nconst liftPlugin = (defaultRequiresApproval: boolean | undefined): EffectivePolicy =>\n defaultRequiresApproval\n ? { action: \"require_approval\", source: \"plugin-default\" }\n : { action: \"approve\", source: \"plugin-default\" };\n\nconst liftUser = (match: PolicyMatch): EffectivePolicy => ({\n action: match.action,\n source: \"user\",\n pattern: match.pattern,\n policyId: match.policyId,\n});\n\nexport const resolveEffectivePolicy = (\n toolId: string,\n policies: readonly ToolPolicyRow[],\n ownerRank: (row: Pick<ToolPolicyRow, \"owner\">) => number,\n defaultRequiresApproval?: boolean,\n): EffectivePolicy => {\n const match = resolveToolPolicy(toolId, policies, ownerRank);\n return match ? liftUser(match) : liftPlugin(defaultRequiresApproval);\n};\n\nexport const effectivePolicyFromSorted = (\n toolId: string,\n sortedPolicies: readonly (Pick<ToolPolicy, \"pattern\" | \"action\" | \"id\"> &\n Partial<Pick<ToolPolicy, \"owner\">>)[],\n defaultRequiresApproval?: boolean,\n): EffectivePolicy => {\n const firstMatchByOwner = new Map<string, EffectivePolicy>();\n for (const p of sortedPolicies) {\n const ownerKey = \"owner\" in p && p.owner ? String(p.owner) : \"__flat__\";\n if (firstMatchByOwner.has(ownerKey)) continue;\n if (matchPattern(p.pattern, toolId)) {\n firstMatchByOwner.set(ownerKey, {\n action: p.action,\n source: \"user\",\n pattern: p.pattern,\n policyId: p.id,\n });\n }\n }\n let selected: EffectivePolicy | undefined;\n for (const match of firstMatchByOwner.values()) {\n selected = moreRestrictive(selected, match);\n }\n return selected ?? liftPlugin(defaultRequiresApproval);\n};\n\n// ---------------------------------------------------------------------------\n// Row → public projection.\n// ---------------------------------------------------------------------------\n\nexport const rowToToolPolicy = (row: ToolPolicyRow): ToolPolicy => ({\n id: PolicyId.make(row.id),\n owner: row.owner as Owner,\n pattern: row.pattern,\n action: row.action as ToolPolicyAction,\n position: row.position,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n});\n\nexport const ToolPolicyActionSchema = Schema.Literals([\"approve\", \"require_approval\", \"block\"]);\n","import { Effect, Schema } from \"effect\";\n\nimport { ElicitationId, ToolAddress } from \"./ids\";\n\n/* A tool that needs user input mid-call suspends and the host's `onElicitation`\n * handler (executor-level, overridable per `execute`) answers. Tools that never\n * elicit never trigger it. Schema-tagged so requests/responses cross the wire. */\n\n/** Tool needs structured input from the user (render a form). */\nexport const FormElicitation = Schema.TaggedStruct(\"FormElicitation\", {\n message: Schema.String,\n /** JSON Schema describing the fields to collect. */\n requestedSchema: Schema.Record(Schema.String, Schema.Unknown),\n});\nexport type FormElicitation = typeof FormElicitation.Type;\n\n/** Tool needs the user to visit a URL (OAuth, approval page, etc.). */\nexport const UrlElicitation = Schema.TaggedStruct(\"UrlElicitation\", {\n message: Schema.String,\n url: Schema.String,\n /** Unique id so the host can correlate the callback. */\n elicitationId: ElicitationId,\n});\nexport type UrlElicitation = typeof UrlElicitation.Type;\n\nexport type ElicitationRequest = FormElicitation | UrlElicitation;\n\nexport const ElicitationAction = Schema.Literals([\"accept\", \"decline\", \"cancel\"]);\nexport type ElicitationAction = typeof ElicitationAction.Type;\n\nexport const ElicitationResponse = Schema.Struct({\n action: ElicitationAction,\n /** Present when `action` is \"accept\" — the data the user provided. */\n content: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n});\nexport type ElicitationResponse = typeof ElicitationResponse.Type;\n\n/** Handler input — the tool address being invoked, its args, and the request. */\nexport interface ElicitationContext {\n readonly address: ToolAddress;\n readonly args: unknown;\n readonly request: ElicitationRequest;\n}\n\n/** Host-provided handler the SDK calls when a tool suspends for input. */\nexport type ElicitationHandler = (ctx: ElicitationContext) => Effect.Effect<ElicitationResponse>;\n\n/** Executor-level elicitation policy: a handler, or `\"accept-all\"` to\n * auto-accept every request (tests / non-interactive hosts). */\nexport type OnElicitation = ElicitationHandler | \"accept-all\";\n\n/** Per-call options for `execute`. */\nexport interface InvokeOptions {\n /** Override the executor-level handler for this single call. */\n readonly onElicitation?: OnElicitation;\n}\n\n/** A tool was declined or cancelled during elicitation. */\nexport class ElicitationDeclinedError extends Schema.TaggedErrorClass<ElicitationDeclinedError>()(\n \"ElicitationDeclinedError\",\n {\n address: ToolAddress,\n action: Schema.Literals([\"decline\", \"cancel\"]),\n },\n) {\n // Derived message so telemetry (span status, logs) labels the failure\n // instead of rendering an Error with an empty message.\n override get message(): string {\n return `Tool approval ${this.action === \"cancel\" ? \"cancelled\" : \"declined\"}: ${this.address}`;\n }\n}\n","import { Schema } from \"effect\";\n\nimport { ElicitationDeclinedError } from \"./elicitation\";\nimport type { StorageFailure } from \"./fuma-runtime\";\nimport { ConnectionName, IntegrationSlug, Owner, ProviderKey, ToolAddress } from \"./ids\";\n\n/* The failure set the SDK surfaces. `execute`'s invoke failures are ported from\n * v1 but re-keyed by `address` (the full `tools.<integration>.<owner>.<connection>.<tool>`\n * handle) instead of an opaque tool id. Storage failures reuse fuma-runtime's\n * `StorageError`/`UniqueViolationError` (`StorageFailure`) — not redefined here. */\n\n// ---------------------------------------------------------------------------\n// Tool lifecycle\n// ---------------------------------------------------------------------------\n\n/* Tagged errors without an explicit `message` field define a `message` getter:\n * `Schema.TaggedErrorClass` instances are real Errors with `message: \"\"`, and\n * an empty message propagates everywhere errors are rendered — span\n * status.message in the tracer, Cause.pretty output, log lines — leaving the\n * failure unlabeled in telemetry. The getter is derived from the schema fields\n * (not an own property), so encoding/serialization is unaffected. */\n\nexport class ToolNotFoundError extends Schema.TaggedErrorClass<ToolNotFoundError>()(\n \"ToolNotFoundError\",\n {\n address: ToolAddress,\n suggestions: Schema.optional(Schema.Array(ToolAddress)),\n },\n) {\n override get message(): string {\n return `Tool not found: ${this.address}`;\n }\n}\n\nexport class ToolInvocationError extends Schema.TaggedErrorClass<ToolInvocationError>()(\n \"ToolInvocationError\",\n {\n address: ToolAddress,\n message: Schema.String,\n cause: Schema.optional(Schema.Unknown),\n },\n) {}\n\n/** Tool invocation was rejected because a workspace `tool_policy` rule with\n * `action: \"block\"` matched. `pattern` is the matched policy pattern. */\nexport class ToolBlockedError extends Schema.TaggedErrorClass<ToolBlockedError>()(\n \"ToolBlockedError\",\n {\n address: ToolAddress,\n pattern: Schema.String,\n },\n) {\n override get message(): string {\n return `Tool blocked by policy \"${this.pattern}\": ${this.address}`;\n }\n}\n\n/** Tool row exists but its owning plugin isn't loaded in this executor config. */\nexport class PluginNotLoadedError extends Schema.TaggedErrorClass<PluginNotLoadedError>()(\n \"PluginNotLoadedError\",\n {\n address: ToolAddress,\n pluginId: Schema.String,\n },\n) {\n override get message(): string {\n return `Plugin \"${this.pluginId}\" is not loaded for tool: ${this.address}`;\n }\n}\n\n/** Tool was found but its owning plugin has no `invokeTool` handler. */\nexport class NoHandlerError extends Schema.TaggedErrorClass<NoHandlerError>()(\"NoHandlerError\", {\n address: ToolAddress,\n pluginId: Schema.String,\n}) {\n override get message(): string {\n return `Plugin \"${this.pluginId}\" has no invokeTool handler for tool: ${this.address}`;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Integration / connection lifecycle\n// ---------------------------------------------------------------------------\n\nexport class IntegrationNotFoundError extends Schema.TaggedErrorClass<IntegrationNotFoundError>()(\n \"IntegrationNotFoundError\",\n { slug: IntegrationSlug },\n) {\n override get message(): string {\n return `Integration not found: ${this.slug}`;\n }\n}\n\n/** An \"add integration\" operation targeted a slug (namespace) that is already\n * registered. The core `integrations.register` primitive upserts by design\n * (for idempotent boot re-registration); add-operation layers gate on this to\n * prevent silently clobbering an existing integration's tools, connections,\n * and policies. */\nexport class IntegrationAlreadyExistsError extends Schema.TaggedErrorClass<IntegrationAlreadyExistsError>()(\n \"IntegrationAlreadyExistsError\",\n { slug: IntegrationSlug },\n { httpApiStatus: 409 },\n) {\n override get message(): string {\n return `Integration already exists: ${this.slug}`;\n }\n}\n\n/** `integrations.remove` was called on an integration declared statically by a\n * plugin at startup (`canRemove: false`). */\nexport class IntegrationRemovalNotAllowedError extends Schema.TaggedErrorClass<IntegrationRemovalNotAllowedError>()(\n \"IntegrationRemovalNotAllowedError\",\n { slug: IntegrationSlug },\n) {\n override get message(): string {\n return `Integration cannot be removed (declared statically by a plugin): ${this.slug}`;\n }\n}\n\nexport class ConnectionNotFoundError extends Schema.TaggedErrorClass<ConnectionNotFoundError>()(\n \"ConnectionNotFoundError\",\n {\n owner: Owner,\n integration: IntegrationSlug,\n name: ConnectionName,\n },\n) {\n override get message(): string {\n return `Connection not found: ${this.integration}.${this.owner}.${this.name}`;\n }\n}\n\n/** A connection create request was rejected before anything was written: the\n * input is structurally invalid (no credential inputs for a credentialed\n * template, mixed pasted/external origins, …) or targets owner `user` in a\n * context that has no user subject. The message says which — it is safe to\n * show to the caller. */\nexport class InvalidConnectionInputError extends Schema.TaggedErrorClass<InvalidConnectionInputError>()(\n \"InvalidConnectionInputError\",\n { message: Schema.String },\n) {}\n\n/** A connection references a credential provider key that isn't registered on\n * the executor. */\nexport class CredentialProviderNotRegisteredError extends Schema.TaggedErrorClass<CredentialProviderNotRegisteredError>()(\n \"CredentialProviderNotRegisteredError\",\n { provider: ProviderKey },\n) {\n override get message(): string {\n return `Credential provider not registered: ${this.provider}`;\n }\n}\n\n/** A connection's value could not be resolved — the provider returned nothing,\n * or an OAuth token refresh failed and the user must re-auth. */\nexport class CredentialResolutionError extends Schema.TaggedErrorClass<CredentialResolutionError>()(\n \"CredentialResolutionError\",\n {\n owner: Owner,\n integration: IntegrationSlug,\n name: ConnectionName,\n message: Schema.String,\n /** True when the stored grant is permanently invalid and the user must\n * sign in again (RFC 6749 §5.2 invalid_grant and friends). */\n reauthRequired: Schema.optional(Schema.Boolean),\n },\n) {}\n\n// ---------------------------------------------------------------------------\n// Union — the failure channel of `execute`.\n// ---------------------------------------------------------------------------\n\nexport type ExecuteError =\n | ToolNotFoundError\n | ToolInvocationError\n | ToolBlockedError\n | PluginNotLoadedError\n | NoHandlerError\n | ConnectionNotFoundError\n | CredentialProviderNotRegisteredError\n | CredentialResolutionError\n | ElicitationDeclinedError\n | StorageFailure;\n\n/** Convenience union spanning every typed error the SDK raises. */\nexport type ExecutorError =\n | ExecuteError\n | IntegrationNotFoundError\n | IntegrationRemovalNotAllowedError;\n","import type { Effect } from \"effect\";\nimport { Schema } from \"effect\";\n\nimport type { Connection } from \"./connection\";\nimport type { StorageFailure } from \"./fuma-runtime\";\nimport {\n type AuthTemplateSlug,\n type ConnectionName,\n type IntegrationSlug,\n OAuthClientSlug,\n OAuthState,\n type Owner,\n} from \"./ids\";\n\n/* The v2 OAuth surface contracts. OAuth is a credential mechanism, not an\n * integration type. A client is a registered app; running its flow mints a\n * Connection. The client is self-contained (carries its own endpoints) and\n * integration-independent, so the same app can back connections on whatever\n * integrations share that provider.\n *\n * The OAuth 2.1 *implementation* (PKCE, DCR, token exchange + refresh) lives in\n * `oauth-helpers` / `oauth-discovery` / `oauth-service`; these are the public\n * input/output shapes the executor's `oauth.*` namespace speaks. */\n\nexport type OAuthGrant = \"authorization_code\" | \"client_credentials\";\n\n/** Provider OAuth config an integration declares as one of its auth templates —\n * what to request. (The flow itself runs off the self-contained OAuthClient.)\n * Keyed `kind: \"oauth2\"` like every auth method across the plugins. */\nexport interface OAuthAuthentication {\n readonly slug: AuthTemplateSlug;\n readonly kind: \"oauth2\";\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n readonly scopes: readonly string[];\n}\n\n/** A registered OAuth app — pure app identity: clientId/secret + its endpoints.\n * Owner-scoped: a shared org app or a user's own BYO app. The app does NOT carry\n * scopes — what to request is the INTEGRATION's concern (`OAuthAuthentication.\n * scopes`, surfaced via the declared auth method), so the same app can back any\n * integration without pinning a scope set. */\nexport interface OAuthClient {\n readonly owner: Owner;\n readonly slug: OAuthClientSlug;\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n readonly grant: OAuthGrant;\n readonly clientId: string;\n /** The literal client secret. Stored out-of-band in the credential provider\n * (vault item id), never inline. Empty string for public / PKCE clients. */\n readonly clientSecret: string;\n /** RFC 8707 Resource Indicator (MCP). Carried so the refresh request can keep\n * the re-minted token bound to the same resource. Null/omitted otherwise. */\n readonly resource?: string | null;\n}\n\nexport type OAuthClientOrigin =\n | { readonly kind: \"manual\" }\n | {\n readonly kind: \"dynamic_client_registration\";\n readonly integration?: IntegrationSlug | null;\n };\n\nexport type CreateOAuthClientInput = OAuthClient & {\n readonly origin?: OAuthClientOrigin;\n};\n\n/** Metadata-only projection of a registered client for listing in the UI.\n * Deliberately omits `clientSecret` — the secret is never returned over the\n * read surface. `clientId` is included (it is not a secret; it is sent in the\n * authorize URL the user's browser visits). */\nexport interface OAuthClientSummary {\n readonly owner: Owner;\n readonly slug: OAuthClientSlug;\n readonly grant: OAuthGrant;\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n readonly resource?: string | null;\n readonly clientId: string;\n readonly origin: OAuthClientOrigin;\n}\n\n/** Flow-aware result of `oauth.start` — the status says what's next. */\nexport type ConnectResult =\n | { readonly status: \"connected\"; readonly connection: Connection }\n | {\n readonly status: \"redirect\";\n readonly authorizationUrl: string;\n readonly state: OAuthState;\n };\n\n/** Start a flow through a client to mint a connection for one integration.\n * `template` is the integration's oauth template the minted token is applied\n * through. */\nexport interface OAuthStartInput {\n readonly client: OAuthClientSlug;\n /** The owner that owns `client`. Supplied explicitly (the picker knows it), so\n * a Personal connection can be minted through a shared Workspace app without\n * any owner-derivation rule. A Workspace connection must use a Workspace app. */\n readonly clientOwner: Owner;\n /** The owner the minted CONNECTION is saved under (may differ from `clientOwner`). */\n readonly owner: Owner;\n readonly name: ConnectionName;\n readonly integration: IntegrationSlug;\n readonly template: AuthTemplateSlug;\n readonly identityLabel?: string | null;\n /** Browser-facing callback URL for this flow. Defaults to the executor's configured redirectUri. */\n readonly redirectUri?: string | null;\n}\n\nexport interface OAuthCompleteInput {\n readonly state: OAuthState;\n readonly code: string;\n /** Non-standard regional host the authorization server returns on the\n * callback (Datadog's `domain`/`site` params) so the code is redeemed at the\n * org's actual region rather than the statically advertised one. Used only\n * when it is a sibling subdomain of the client's configured token host. */\n readonly callbackDomain?: string | null;\n}\n\n/** Probe a base/issuer URL for OAuth 2.1 authorization-server metadata so the\n * onboarding UI can pre-fill a client's endpoints. */\nexport interface OAuthProbeInput {\n readonly url: string;\n}\n\nexport interface OAuthProbeResult {\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n /** RFC 8707 resource indicator discovered from protected-resource metadata.\n * Persist this on DCR clients so authorize/token/refresh requests stay bound\n * to the protected resource. */\n readonly resource?: string | null;\n readonly scopesSupported?: readonly string[];\n /** Whether the server advertises dynamic client registration (RFC 7591). */\n readonly registrationEndpoint?: string | null;\n /** RFC 8414 `token_endpoint_auth_methods_supported`. Surfaced so DCR can pick\n * a public (\"none\") client when the server allows it. */\n readonly tokenEndpointAuthMethodsSupported?: readonly string[];\n}\n\n/** Mint an OAuth client via RFC 7591 Dynamic Client Registration and persist it.\n * The user pastes NO client id/secret — the authorization server mints a\n * (public, PKCE) client which is stored as an owner-scoped `oauth_client`. */\nexport interface RegisterDynamicClientInput {\n readonly owner: Owner;\n readonly slug: OAuthClientSlug;\n /** RFC 7591 registration endpoint advertised by the authorization server. */\n readonly registrationEndpoint: string;\n readonly authorizationUrl: string;\n readonly tokenUrl: string;\n /** RFC 8707 Resource Indicator (MCP). Persisted on the minted client when known. */\n readonly resource?: string | null;\n readonly scopes: readonly string[];\n /** Auth methods the server advertises. When it allows `none` a public\n * (PKCE-only, no secret) client is registered; otherwise `client_secret_post`. */\n readonly tokenEndpointAuthMethodsSupported?: readonly string[];\n /** Human label for the registered app (RFC 7591 `client_name`). */\n readonly clientName?: string;\n /** Browser-facing callback URL to register. Defaults to the executor's configured redirectUri. */\n readonly redirectUri?: string | null;\n /** Integration that requested this dynamic client, when known. */\n readonly originIntegration?: IntegrationSlug | null;\n}\n\nexport class OAuthStartError extends Schema.TaggedErrorClass<OAuthStartError>()(\"OAuthStartError\", {\n message: Schema.String,\n}) {}\n\nexport class OAuthCompleteError extends Schema.TaggedErrorClass<OAuthCompleteError>()(\n \"OAuthCompleteError\",\n {\n message: Schema.String,\n /** True when the auth-code exchange failed in a way the user must restart. */\n restartRequired: Schema.optional(Schema.Boolean),\n },\n) {}\n\nexport class OAuthProbeError extends Schema.TaggedErrorClass<OAuthProbeError>()(\"OAuthProbeError\", {\n message: Schema.String,\n}) {}\n\nexport class OAuthRegisterDynamicError extends Schema.TaggedErrorClass<OAuthRegisterDynamicError>()(\n \"OAuthRegisterDynamicError\",\n { message: Schema.String },\n) {}\n\nexport class OAuthSessionNotFoundError extends Schema.TaggedErrorClass<OAuthSessionNotFoundError>()(\n \"OAuthSessionNotFoundError\",\n { state: OAuthState },\n) {}\n\n/** The OAuth surface the executor's `oauth.*` namespace and `ctx.oauth` expose.\n * Implemented by `makeOAuthService` (oauth-service.ts), wired by the executor\n * with the deps it needs to mint connections. */\nexport interface OAuthService {\n readonly createClient: (\n input: CreateOAuthClientInput,\n ) => Effect.Effect<OAuthClientSlug, StorageFailure>;\n /** Mint a client via RFC 7591 Dynamic Client Registration (no pre-shared\n * client id/secret) and persist it as an owner-scoped `oauth_client`. */\n readonly registerDynamicClient: (\n input: RegisterDynamicClientInput,\n ) => Effect.Effect<OAuthClientSlug, OAuthRegisterDynamicError | StorageFailure>;\n /** All registered clients visible to the caller (their org's shared clients +\n * their own user clients), as metadata-only summaries — never the secret. */\n readonly listClients: () => Effect.Effect<readonly OAuthClientSummary[], StorageFailure>;\n /** Permanently remove a registered OAuth app, keyed by (owner, slug). The\n * owner policy on `oauth_client` prevents removing another subject's user app.\n * Idempotent: removing an already-gone app succeeds. Connections that\n * referenced the slug keep their stored value and fail at the next token\n * refresh, prompting a reconnect — this op never cascades into connections. */\n readonly removeClient: (\n owner: Owner,\n slug: OAuthClientSlug,\n ) => Effect.Effect<void, StorageFailure>;\n readonly start: (\n input: OAuthStartInput,\n ) => Effect.Effect<ConnectResult, OAuthStartError | StorageFailure>;\n readonly complete: (\n input: OAuthCompleteInput,\n ) => Effect.Effect<Connection, OAuthCompleteError | OAuthSessionNotFoundError | StorageFailure>;\n readonly cancel: (state: OAuthState) => Effect.Effect<void, StorageFailure>;\n readonly probe: (\n input: OAuthProbeInput,\n ) => Effect.Effect<OAuthProbeResult, OAuthProbeError | StorageFailure>;\n}\n","// ---------------------------------------------------------------------------\n// Public projections beyond the core domain types. The integration / connection\n// / tool views live in their own domain files (`integration.ts`, `connection.ts`,\n// `tool.ts`); this file holds the schema-side views and the onboarding URL\n// autodetect result.\n// ---------------------------------------------------------------------------\n\nimport { Schema } from \"effect\";\n\nimport { ToolAddress } from \"./ids\";\n\n// ---------------------------------------------------------------------------\n// ToolSchemaView — the full schema-side view of a tool, returned by\n// `executor.tools.schema(address)`. Includes JSON schema roots plus shared\n// definitions for schema exploration, and optionally TypeScript preview strings.\n// ---------------------------------------------------------------------------\n\nexport const ToolSchemaView = Schema.Struct({\n address: ToolAddress,\n name: Schema.optional(Schema.String),\n description: Schema.optional(Schema.String),\n inputSchema: Schema.optional(Schema.Unknown),\n outputSchema: Schema.optional(Schema.Unknown),\n schemaDefinitions: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n inputTypeScript: Schema.optional(Schema.String),\n outputTypeScript: Schema.optional(Schema.String),\n typeScriptDefinitions: Schema.optional(Schema.Record(Schema.String, Schema.String)),\n});\nexport type ToolSchemaView = typeof ToolSchemaView.Type;\n\n// ---------------------------------------------------------------------------\n// Integration detection — optional capability on `PluginSpec.detect`. When a\n// user pastes a URL in the onboarding UI, `executor.integrations.detect(url)`\n// asks every plugin \"is this yours?\" and returns the best-confidence match so\n// the UI can auto-fill the onboarding form for the right plugin.\n// ---------------------------------------------------------------------------\n\nexport const IntegrationDetectionResult = Schema.Struct({\n /** Plugin id that recognized the URL (e.g. \"openapi\", \"graphql\"). */\n kind: Schema.String,\n /** Confidence tier — UI uses this to pick a winner when multiple plugins\n * claim a URL. */\n confidence: Schema.Literals([\"high\", \"medium\", \"low\"]),\n /** The (possibly normalized) endpoint the plugin will use. */\n endpoint: Schema.String,\n /** Human-readable name suggestion, typically derived from spec title or URL. */\n name: Schema.String,\n /** Slug suggestion — the plugin's recommendation for the integration slug. */\n slug: Schema.String,\n});\nexport type IntegrationDetectionResult = typeof IntegrationDetectionResult.Type;\n"],"mappings":"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","names":["Schema","Schema","Schema","Schema","Schema","Schema","Schema","Schema"]}
|