History

fix url + mcp oauth

Rhys Sullivan committed 8 months ago

Commit a0347d61 parent 60b3c7111 files · +482 −19Browse files

Unified · Split

11 files
  1. apps/web/README.md +9 −0
  2. apps/web/app/.well-known/oauth-authorization-server/route.ts +43 −0
  3. apps/web/app/.well-known/oauth-protected-resource/route.ts +62 −0
  4. apps/web/app/v1/mcp/route.ts +66 −0
  5. apps/web/components/tools/tools-view.tsx +1 −12
  6. apps/web/lib/control-plane/server.ts +99 −1
  7. apps/web/lib/mcp/resource-auth.ts +181 −0
  8. apps/web/package.json +5 −0
  9. bun.lock +9 −4
  10. packages/management-api/src/openapi-extractor-wasm.ts +6 −1
  11. packages/runtime-deno-subprocess/src/index.ts +1 −1
apps/web/README.md+9−0
@@ -27,6 +27,7 @@ MCP install URL generation: · 26 unmodified lines
27 27
28- Derives from existing frontend origin and deployment metadata.28- Derives from existing frontend origin and deployment metadata.
29- In local dev with no DB URL, defaults to local PGlite-backed control-plane in this Next.js app.29- In local dev with no DB URL, defaults to local PGlite-backed control-plane in this Next.js app.
30- MCP endpoint is served from this app at `GET/POST/DELETE /v1/mcp`.
30 31
31WorkOS auth setup (optional but recommended):32WorkOS auth setup (optional but recommended):
32 33
@@ -36,3 +37,11 @@ WorkOS auth setup (optional but recommended): · 3 unmodified lines
36- `WORKOS_REDIRECT_URI` or `NEXT_PUBLIC_WORKOS_REDIRECT_URI` (for example `http://localhost:4312/callback`)37- `WORKOS_REDIRECT_URI` or `NEXT_PUBLIC_WORKOS_REDIRECT_URI` (for example `http://localhost:4312/callback`)
37 38
38When WorkOS is configured, the app requires sign-in and the server proxy forwards the authenticated WorkOS access token to control-plane as a bearer token (`Authorization: Bearer ...`).39When WorkOS is configured, the app requires sign-in and the server proxy forwards the authenticated WorkOS access token to control-plane as a bearer token (`Authorization: Bearer ...`).
40
41MCP OAuth setup (for external MCP clients):
42
43- `MCP_AUTHORIZATION_SERVER` (or `MCP_AUTHORIZATION_SERVER_URL`, `WORKOS_AUTHKIT_ISSUER`)
44- `/.well-known/oauth-protected-resource`
45- `/.well-known/oauth-authorization-server`
46
47Unauthenticated requests to `/v1/mcp` return `401` with `WWW-Authenticate` and `resource_metadata` for OAuth discovery.
apps/web/app/.well-known/oauth-authorization-server/route.ts+43−0
@@ -0,0 +1,43 @@
1import { getMcpAuthConfig } from "../../../lib/mcp/resource-auth";
2
3export const dynamic = "force-dynamic";
4export const runtime = "nodejs";
5
6export async function GET(): Promise<Response> {
7 const authConfig = getMcpAuthConfig();
8 if (!authConfig.enabled || !authConfig.authorizationServer) {
9 return Response.json(
10 { error: "MCP OAuth is not configured" },
11 { status: 404 },
12 );
13 }
14
15 const upstream = new URL(
16 "/.well-known/oauth-authorization-server",
17 authConfig.authorizationServer,
18 );
19
20 try {
21 const response = await fetch(upstream.toString(), {
22 headers: { accept: "application/json" },
23 });
24
25 const text = await response.text();
26 return new Response(text, {
27 status: response.status,
28 headers: {
29 "content-type": response.headers.get("content-type") ?? "application/json",
30 },
31 });
32 } catch (error) {
33 return Response.json(
34 {
35 error:
36 error instanceof Error
37 ? `Failed to fetch upstream auth metadata: ${error.message}`
38 : "Failed to fetch upstream auth metadata",
39 },
40 { status: 502 },
41 );
42 }
43}
apps/web/app/.well-known/oauth-protected-resource/route.ts+62−0
@@ -0,0 +1,62 @@
1import { externalOriginFromRequest } from "../../../lib/workos";
2import { getMcpAuthConfig } from "../../../lib/mcp/resource-auth";
3
4export const dynamic = "force-dynamic";
5export const runtime = "nodejs";
6
7const MCP_PATH = "/v1/mcp";
8
9const parseResourceHint = (
10 requestUrl: URL,
11 origin: string,
12): { resourceUrl: URL } | { error: string } => {
13 const resourceHint = requestUrl.searchParams.get("resource")?.trim();
14 if (!resourceHint) {
15 const fallback = new URL(MCP_PATH, origin);
16 for (const [key, value] of requestUrl.searchParams.entries()) {
17 if (key === "resource") continue;
18 fallback.searchParams.set(key, value);
19 }
20 return { resourceUrl: fallback };
21 }
22
23 let parsed: URL;
24 try {
25 parsed = new URL(resourceHint);
26 } catch {
27 return { error: "Invalid resource hint" };
28 }
29
30 if (parsed.origin !== origin) {
31 return { error: "resource hint origin must match this server" };
32 }
33
34 if (parsed.pathname !== MCP_PATH) {
35 return { error: `resource hint path must be ${MCP_PATH}` };
36 }
37
38 return { resourceUrl: parsed };
39};
40
41export async function GET(request: Request): Promise<Response> {
42 const authConfig = getMcpAuthConfig();
43 if (!authConfig.enabled || !authConfig.authorizationServer) {
44 return Response.json(
45 { error: "MCP OAuth is not configured" },
46 { status: 404 },
47 );
48 }
49
50 const origin = externalOriginFromRequest(request);
51 const requestUrl = new URL(request.url);
52 const parsedResource = parseResourceHint(requestUrl, origin);
53 if ("error" in parsedResource) {
54 return Response.json({ error: parsedResource.error }, { status: 400 });
55 }
56
57 return Response.json({
58 resource: parsedResource.resourceUrl.toString(),
59 authorization_servers: [authConfig.authorizationServer],
60 bearer_methods_supported: ["header"],
61 });
62}
apps/web/app/v1/mcp/route.ts+66−0
@@ -0,0 +1,66 @@
1import {
2 createWorkosPrincipal,
3 getControlPlaneRuntime,
4 provisionPrincipal,
5} from "../../../lib/control-plane/server";
6import {
7 getMcpAuthConfig,
8 unauthorizedMcpResponse,
9 verifyMcpBearerToken,
10} from "../../../lib/mcp/resource-auth";
11
12export const dynamic = "force-dynamic";
13export const runtime = "nodejs";
14
15const parseWorkspaceId = (request: Request): string | null => {
16 const value = new URL(request.url).searchParams.get("workspaceId")?.trim();
17 return value && value.length > 0 ? value : null;
18};
19
20const handler = async (request: Request): Promise<Response> => {
21 const authConfig = getMcpAuthConfig();
22 if (!authConfig.enabled) {
23 return Response.json(
24 {
25 error:
26 "MCP OAuth is not configured. Set MCP_AUTHORIZATION_SERVER (or WORKOS_AUTHKIT_ISSUER).",
27 },
28 { status: 503 },
29 );
30 }
31
32 const verified = await verifyMcpBearerToken(request, authConfig);
33 if (!verified) {
34 return unauthorizedMcpResponse(request, "No valid bearer token provided.");
35 }
36
37 const workspaceId = parseWorkspaceId(request);
38 if (!workspaceId) {
39 return Response.json(
40 { error: "workspaceId query parameter is required" },
41 { status: 400 },
42 );
43 }
44
45 const principal = createWorkosPrincipal({
46 subject: verified.subject,
47 email: verified.email,
48 displayName: verified.displayName,
49 });
50
51 if (workspaceId !== principal.workspaceId) {
52 return Response.json(
53 { error: "workspaceId does not match authenticated subject" },
54 { status: 403 },
55 );
56 }
57
58 const controlPlaneRuntime = await getControlPlaneRuntime();
59 await provisionPrincipal(controlPlaneRuntime, principal);
60
61 return controlPlaneRuntime.handleMcp(request, workspaceId);
62};
63
64export const GET = handler;
65export const POST = handler;
66export const DELETE = handler;
apps/web/components/tools/tools-view.tsx+1−12
@@ -65,13 +65,6 @@ type SourceGroup = { · 64 unmodified lines
65 toolCount: number;65 toolCount: number;
66};66};
67 67
68const isLocalHost = (hostname: string): boolean =>
69 hostname === "localhost"
70 || hostname === "127.0.0.1"
71 || hostname === "0.0.0.0"
72 || hostname === "::1"
73 || hostname === "[::1]";
74
75// ---------------------------------------------------------------------------68// ---------------------------------------------------------------------------
76// Search helpers (ported from old executor explorer-derived.ts)69// Search helpers (ported from old executor explorer-derived.ts)
77// ---------------------------------------------------------------------------70// ---------------------------------------------------------------------------
@@ -407,11 +400,7 @@ export function ToolsView(props: { · 329 unmodified lines
407 return `http://127.0.0.1:8788/v1/mcp?workspaceId=${encodeURIComponent(workspaceId)}`;400 return `http://127.0.0.1:8788/v1/mcp?workspaceId=${encodeURIComponent(workspaceId)}`;
408 }401 }
409 402
410 const baseOrigin = isLocalHost(window.location.hostname)403 const url = new URL("/v1/mcp", window.location.origin);
411 ? "http://127.0.0.1:8788"
412 : window.location.origin;
413
414 const url = new URL("/v1/mcp", baseOrigin);
415 url.searchParams.set("workspaceId", workspaceId);404 url.searchParams.set("workspaceId", workspaceId);
416 return url.toString();405 return url.toString();
417 }, [props.mcpBaseUrl, workspaceId]);406 }, [props.mcpBaseUrl, workspaceId]);
apps/web/lib/control-plane/server.ts+99−1
@@ -8,6 +8,18 @@ import { · 7 unmodified lines
8 makeSourceCatalogService,8 makeSourceCatalogService,
9 makeSourceManagerService,9 makeSourceManagerService,
10} from "@executor-v2/management-api";10} from "@executor-v2/management-api";
11import {
12 createRunExecutor,
13 createSourceToolRegistry,
14 defaultExecuteToolExposureMode,
15 makeGraphqlToolProvider,
16 makeMcpToolProvider,
17 makeOpenApiToolProvider,
18 makeRuntimeAdapterRegistry,
19 makeToolProviderRegistry,
20 parseExecuteToolExposureMode,
21 type ExecuteToolExposureMode,
22} from "@executor-v2/engine";
11import {23import {
12 makeSqlControlPlanePersistence,24 makeSqlControlPlanePersistence,
13 type SqlControlPlanePersistence,25 type SqlControlPlanePersistence,
@@ -22,14 +34,22 @@ import { · 8 unmodified lines
22 type OrganizationMembership,34 type OrganizationMembership,
23 type Workspace,35 type Workspace,
24} from "@executor-v2/schema";36} from "@executor-v2/schema";
37import { makeCloudflareWorkerLoaderRuntimeAdapter } from "@executor-v2/runtime-cloudflare-worker-loader";
38import { makeDenoSubprocessRuntimeAdapter } from "@executor-v2/runtime-deno-subprocess";
39import { makeLocalInProcessRuntimeAdapter } from "@executor-v2/runtime-local-inproc";
25import * as Effect from "effect/Effect";40import * as Effect from "effect/Effect";
26import * as Layer from "effect/Layer";41import * as Layer from "effect/Layer";
27 42
28import { PmActorLive } from "../../../pm/src/actor";43import { PmActorLive } from "../../../pm/src/actor";
29import { createPmApprovalsService } from "../../../pm/src/approvals-service";44import {
45 createPmApprovalsService,
46 createPmPersistentToolApprovalPolicy,
47} from "../../../pm/src/approvals-service";
30import { createPmCredentialsService } from "../../../pm/src/credentials-service";48import { createPmCredentialsService } from "../../../pm/src/credentials-service";
49import { createPmMcpHandler } from "../../../pm/src/mcp-handler";
31import { createPmOrganizationsService } from "../../../pm/src/organizations-service";50import { createPmOrganizationsService } from "../../../pm/src/organizations-service";
32import { createPmPoliciesService } from "../../../pm/src/policies-service";51import { createPmPoliciesService } from "../../../pm/src/policies-service";
52import { createPmExecuteRuntimeRun } from "../../../pm/src/runtime-execution-port";
33import { createPmStorageService } from "../../../pm/src/storage-service";53import { createPmStorageService } from "../../../pm/src/storage-service";
34import { createPmToolsService } from "../../../pm/src/tools-service";54import { createPmToolsService } from "../../../pm/src/tools-service";
35import { createPmWorkspacesService } from "../../../pm/src/workspaces-service";55import { createPmWorkspacesService } from "../../../pm/src/workspaces-service";
@@ -90,6 +110,7 @@ type ControlPlaneRuntime = { · 54 unmodified lines
90 toolArtifactStore: ToolArtifactStore;110 toolArtifactStore: ToolArtifactStore;
91 fetchOpenApiDocument: typeof fetchOpenApiDocument;111 fetchOpenApiDocument: typeof fetchOpenApiDocument;
92 handleControlPlane: (request: Request) => Promise<Response>;112 handleControlPlane: (request: Request) => Promise<Response>;
113 handleMcp: (request: Request, workspaceId: string) => Promise<Response>;
93 dispose: () => Promise<void>;114 dispose: () => Promise<void>;
94};115};
95 116
@@ -127,6 +148,22 @@ const resolveControlPlaneDataDir = (): string => · 31 unmodified lines
127const resolveStateRootDir = (): string =>148const resolveStateRootDir = (): string =>
128 defaultControlPlaneStateRootDir;149 defaultControlPlaneStateRootDir;
129 150
151const readConfiguredRuntimeKind = (value: string | undefined): string | undefined => {
152 const normalized = value?.trim();
153 return normalized && normalized.length > 0 ? normalized : undefined;
154};
155
156const readBooleanFlag = (value: string | undefined): boolean => {
157 const normalized = value?.trim().toLowerCase();
158 return normalized === "1" || normalized === "true" || normalized === "yes";
159};
160
161const readConfiguredToolExposureMode = (
162 value: string | undefined,
163): ExecuteToolExposureMode =>
164 parseExecuteToolExposureMode(value ?? undefined) ??
165 defaultExecuteToolExposureMode;
166
130const openApiSyncRetryDelayMs = 300;167const openApiSyncRetryDelayMs = 300;
131 168
132const formatCause = (cause: unknown): string => {169const formatCause = (cause: unknown): string => {
@@ -256,6 +293,35 @@ const createControlPlaneRuntime = async (): Promise<ControlPlaneRuntime> => { · 123 unmodified lines
256 293
257 const sourceStore = persistence.sourceStore;294 const sourceStore = persistence.sourceStore;
258 const toolArtifactStore = persistence.toolArtifactStore;295 const toolArtifactStore = persistence.toolArtifactStore;
296 const runtimeAdapterList = [
297 makeLocalInProcessRuntimeAdapter(),
298 makeDenoSubprocessRuntimeAdapter(),
299 makeCloudflareWorkerLoaderRuntimeAdapter(),
300 ];
301 const runtimeAdapters = makeRuntimeAdapterRegistry(runtimeAdapterList);
302 const defaultRuntimeKind =
303 readConfiguredRuntimeKind(process.env.PM_RUNTIME_KIND)
304 ?? runtimeAdapterList[0]?.kind
305 ?? "local-inproc";
306 const requireToolApprovals = readBooleanFlag(process.env.PM_REQUIRE_TOOL_APPROVALS);
307 const defaultToolExposureMode = readConfiguredToolExposureMode(
308 process.env.PM_TOOL_EXPOSURE_MODE,
309 );
310 const toolProviderRegistry = makeToolProviderRegistry([
311 makeOpenApiToolProvider(),
312 makeMcpToolProvider(),
313 makeGraphqlToolProvider(),
314 ]);
315 const persistentApprovalPolicy = createPmPersistentToolApprovalPolicy(
316 persistence.rows,
317 {
318 requireApprovals: requireToolApprovals,
319 },
320 );
321 const mcpHandlers = new Map<
322 string,
323 (request: Request) => Promise<Response>
324 >();
259 325
260 const sourceCatalog = makeSourceCatalogService(sourceStore);326 const sourceCatalog = makeSourceCatalogService(sourceStore);
261 const sourceManager = makeSourceManagerService(toolArtifactStore);327 const sourceManager = makeSourceManagerService(toolArtifactStore);
@@ -373,12 +439,44 @@ const createControlPlaneRuntime = async (): Promise<ControlPlaneRuntime> => { · 111 unmodified lines
373 PmActorLive(persistence.rows),439 PmActorLive(persistence.rows),
374 );440 );
375 441
442 const resolveMcpHandler = (workspaceId: string): ((request: Request) => Promise<Response>) => {
443 const existing = mcpHandlers.get(workspaceId);
444 if (existing) {
445 return existing;
446 }
447
448 const toolRegistry = createSourceToolRegistry({
449 workspaceId,
450 sourceStore,
451 toolArtifactStore,
452 toolProviderRegistry,
453 approvalPolicy: persistentApprovalPolicy,
454 });
455 const executeRuntimeRun = createPmExecuteRuntimeRun({
456 defaultRuntimeKind,
457 runtimeAdapters,
458 toolRegistry,
459 });
460 const runExecutor = createRunExecutor(executeRuntimeRun);
461 const next = createPmMcpHandler(runExecutor.executeRun, {
462 toolRegistry,
463 defaultToolExposureMode,
464 });
465
466 mcpHandlers.set(workspaceId, next);
467 return next;
468 };
469
376 return {470 return {
377 persistence,471 persistence,
378 sourceStore,472 sourceStore,
379 toolArtifactStore,473 toolArtifactStore,
380 fetchOpenApiDocument,474 fetchOpenApiDocument,
381 handleControlPlane: controlPlaneWebHandler.handler,475 handleControlPlane: controlPlaneWebHandler.handler,
476 handleMcp: async (request, workspaceId) => {
477 const handler = resolveMcpHandler(workspaceId);
478 return handler(request);
479 },
382 dispose: async () => {480 dispose: async () => {
383 await controlPlaneWebHandler.dispose();481 await controlPlaneWebHandler.dispose();
384 await persistence.close();482 await persistence.close();
apps/web/lib/mcp/resource-auth.ts+181−0
@@ -0,0 +1,181 @@
1import { createRemoteJWKSet, jwtVerify } from "jose";
2
3import { externalOriginFromRequest } from "../workos";
4
5type McpAuthConfig = {
6 enabled: boolean;
7 authorizationServer: string | null;
8 jwks: ReturnType<typeof createRemoteJWKSet> | null;
9};
10
11export type VerifiedMcpToken = {
12 subject: string;
13 email: string | null;
14 displayName: string | null;
15};
16
17const trim = (value: string | undefined): string | undefined => {
18 const candidate = value?.trim();
19 return candidate && candidate.length > 0 ? candidate : undefined;
20};
21
22const toAuthorizationServerUrl = (value: string): string | null => {
23 const candidate =
24 value.startsWith("http://") || value.startsWith("https://")
25 ? value
26 : `https://${value}`;
27
28 try {
29 return new URL(candidate).origin;
30 } catch {
31 return null;
32 }
33};
34
35const resolveAuthorizationServer = (): string | null => {
36 const candidates = [
37 process.env.MCP_AUTHORIZATION_SERVER,
38 process.env.MCP_AUTHORIZATION_SERVER_URL,
39 process.env.WORKOS_AUTHKIT_ISSUER,
40 process.env.WORKOS_AUTHKIT_DOMAIN,
41 ];
42
43 for (const candidate of candidates) {
44 const normalized = trim(candidate);
45 if (!normalized) {
46 continue;
47 }
48
49 const parsed = toAuthorizationServerUrl(normalized);
50 if (parsed) {
51 return parsed;
52 }
53 }
54
55 return null;
56};
57
58let cachedAuthConfig: McpAuthConfig | null = null;
59
60export const getMcpAuthConfig = (): McpAuthConfig => {
61 const authorizationServer = resolveAuthorizationServer();
62
63 if (
64 cachedAuthConfig
65 && cachedAuthConfig.authorizationServer === authorizationServer
66 ) {
67 return cachedAuthConfig;
68 }
69
70 if (!authorizationServer) {
71 cachedAuthConfig = {
72 enabled: false,
73 authorizationServer: null,
74 jwks: null,
75 };
76 return cachedAuthConfig;
77 }
78
79 cachedAuthConfig = {
80 enabled: true,
81 authorizationServer,
82 jwks: createRemoteJWKSet(new URL("/oauth2/jwks", authorizationServer)),
83 };
84
85 return cachedAuthConfig;
86};
87
88const parseBearerToken = (request: Request): string | null => {
89 const header = request.headers.get("authorization");
90 if (!header || !header.startsWith("Bearer ")) {
91 return null;
92 }
93
94 const token = header.slice("Bearer ".length).trim();
95 return token.length > 0 ? token : null;
96};
97
98export const resourceMetadataUrl = (request: Request): string => {
99 const url = new URL(request.url);
100 const origin = externalOriginFromRequest(request);
101 const metadata = new URL(
102 "/.well-known/oauth-protected-resource",
103 origin,
104 );
105 metadata.search = url.search;
106
107 const resource = new URL(url.pathname, origin);
108 resource.search = url.search;
109 metadata.searchParams.set("resource", resource.toString());
110
111 return metadata.toString();
112};
113
114export const unauthorizedMcpResponse = (
115 request: Request,
116 message: string,
117): Response => {
118 const challenge = [
119 'Bearer error="unauthorized"',
120 'error_description="Authorization needed"',
121 `resource_metadata="${resourceMetadataUrl(request)}"`,
122 ].join(", ");
123
124 return Response.json(
125 { error: message },
126 {
127 status: 401,
128 headers: {
129 "WWW-Authenticate": challenge,
130 },
131 },
132 );
133};
134
135const readDisplayName = (payload: Record<string, unknown>): string | null => {
136 const direct = payload.name;
137 if (typeof direct === "string" && direct.trim().length > 0) {
138 return direct.trim();
139 }
140
141 const given = typeof payload.given_name === "string" ? payload.given_name.trim() : "";
142 const family = typeof payload.family_name === "string" ? payload.family_name.trim() : "";
143 const combined = [given, family].filter((part) => part.length > 0).join(" ");
144
145 return combined.length > 0 ? combined : null;
146};
147
148export const verifyMcpBearerToken = async (
149 request: Request,
150 config: McpAuthConfig,
151): Promise<VerifiedMcpToken | null> => {
152 if (!config.enabled || !config.authorizationServer || !config.jwks) {
153 return null;
154 }
155
156 const token = parseBearerToken(request);
157 if (!token) {
158 return null;
159 }
160
161 try {
162 const { payload } = await jwtVerify(token, config.jwks, {
163 issuer: config.authorizationServer,
164 });
165
166 if (typeof payload.sub !== "string" || payload.sub.length === 0) {
167 return null;
168 }
169
170 const email = typeof payload.email === "string" ? payload.email : null;
171 const displayName = readDisplayName(payload);
172
173 return {
174 subject: payload.sub,
175 email,
176 displayName,
177 };
178 } catch {
179 return null;
180 }
181};
apps/web/package.json+5−0
@@ -11,9 +11,13 @@ · 10 unmodified lines
11 "dependencies": {11 "dependencies": {
12 "@effect-atom/atom": "^0.4.11",12 "@effect-atom/atom": "^0.4.11",
13 "@effect-atom/atom-react": "^0.4.6",13 "@effect-atom/atom-react": "^0.4.6",
14 "@executor-v2/engine": "workspace:*",
14 "@executor-v2/management-api": "workspace:*",15 "@executor-v2/management-api": "workspace:*",
15 "@executor-v2/persistence-ports": "workspace:*",16 "@executor-v2/persistence-ports": "workspace:*",
16 "@executor-v2/persistence-sql": "workspace:*",17 "@executor-v2/persistence-sql": "workspace:*",
18 "@executor-v2/runtime-cloudflare-worker-loader": "workspace:*",
19 "@executor-v2/runtime-deno-subprocess": "workspace:*",
20 "@executor-v2/runtime-local-inproc": "workspace:*",
17 "@executor-v2/schema": "workspace:*",21 "@executor-v2/schema": "workspace:*",
18 "@modelcontextprotocol/sdk": "^1.27.1",22 "@modelcontextprotocol/sdk": "^1.27.1",
19 "@streamdown/code": "^1.0.3",23 "@streamdown/code": "^1.0.3",
@@ -21,6 +25,7 @@ · 1 unmodified line
21 "class-variance-authority": "^0.7.1",25 "class-variance-authority": "^0.7.1",
22 "clsx": "^2.1.1",26 "clsx": "^2.1.1",
23 "effect": "catalog:",27 "effect": "catalog:",
28 "jose": "^6.1.3",
24 "next": "^15.4.6",29 "next": "^15.4.6",
25 "next-themes": "^0.4.6",30 "next-themes": "^0.4.6",
26 "react": "^19.1.1",31 "react": "^19.1.1",
bun.lock+9−4
@@ -47,9 +47,13 @@ · 46 unmodified lines
47 "dependencies": {47 "dependencies": {
48 "@effect-atom/atom": "^0.4.11",48 "@effect-atom/atom": "^0.4.11",
49 "@effect-atom/atom-react": "^0.4.6",49 "@effect-atom/atom-react": "^0.4.6",
50 "@executor-v2/engine": "workspace:*",
50 "@executor-v2/management-api": "workspace:*",51 "@executor-v2/management-api": "workspace:*",
51 "@executor-v2/persistence-ports": "workspace:*",52 "@executor-v2/persistence-ports": "workspace:*",
52 "@executor-v2/persistence-sql": "workspace:*",53 "@executor-v2/persistence-sql": "workspace:*",
54 "@executor-v2/runtime-cloudflare-worker-loader": "workspace:*",
55 "@executor-v2/runtime-deno-subprocess": "workspace:*",
56 "@executor-v2/runtime-local-inproc": "workspace:*",
53 "@executor-v2/schema": "workspace:*",57 "@executor-v2/schema": "workspace:*",
54 "@modelcontextprotocol/sdk": "^1.27.1",58 "@modelcontextprotocol/sdk": "^1.27.1",
55 "@streamdown/code": "^1.0.3",59 "@streamdown/code": "^1.0.3",
@@ -57,6 +61,7 @@ · 1 unmodified line
57 "class-variance-authority": "^0.7.1",61 "class-variance-authority": "^0.7.1",
58 "clsx": "^2.1.1",62 "clsx": "^2.1.1",
59 "effect": "catalog:",63 "effect": "catalog:",
64 "jose": "^6.1.3",
60 "next": "^15.4.6",65 "next": "^15.4.6",
61 "next-themes": "^0.4.6",66 "next-themes": "^0.4.6",
62 "react": "^19.1.1",67 "react": "^19.1.1",
@@ -1472,7 +1477,7 @@ · 1409 unmodified lines
1472 1477
1473 "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],1478 "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
1474 1479
1475 "jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],1480 "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
1476 1481
1477 "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],1482 "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
1478 1483
@@ -2348,8 +2353,6 @@ · 869 unmodified lines
2348 2353
2349 "@mapbox/node-pre-gyp/tar": ["tar@7.5.9", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg=="],2354 "@mapbox/node-pre-gyp/tar": ["tar@7.5.9", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg=="],
2350 2355
2351 "@modelcontextprotocol/sdk/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
2352
2353 "@neondatabase/serverless/@types/node": ["@types/node@22.19.13", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw=="],2356 "@neondatabase/serverless/@types/node": ["@types/node@22.19.13", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw=="],
2354 2357
2355 "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],2358 "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
@@ -2430,7 +2433,7 @@ · 74 unmodified lines
2430 2433
2431 "@vercel/static-config/ajv": ["ajv@8.6.3", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw=="],2434 "@vercel/static-config/ajv": ["ajv@8.6.3", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw=="],
2432 2435
2433 "@workos-inc/node/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],2436 "@workos-inc/authkit-nextjs/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
2434 2437
2435 "accepts/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],2438 "accepts/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
2436 2439
@@ -2522,6 +2525,8 @@ · 85 unmodified lines
2522 2525
2523 "unstorage/lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="],2526 "unstorage/lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="],
2524 2527
2528 "vercel/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
2529
2525 "vscode-json-languageservice/jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],2530 "vscode-json-languageservice/jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
2526 2531
2527 "widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],2532 "widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
packages/management-api/src/openapi-extractor-wasm.ts+6−1
@@ -12,8 +12,13 @@ const readWasmBytes = async (): Promise<Uint8Array> => { · 11 unmodified lines
12 const candidates: string[] = [];12 const candidates: string[] = [];
13 13
14 try {14 try {
15 const wasmModuleUrl = new URL(
16 "./openapi-extractor-wasm/openapi_extractor_bg.wasm",
17 import.meta.url,
18 ).toString();
19
15 candidates.push(20 candidates.push(
16 fileURLToPath(new URL("./openapi-extractor-wasm/openapi_extractor_bg.wasm", import.meta.url)),21 fileURLToPath(wasmModuleUrl),
17 );22 );
18 } catch {23 } catch {
19 // Next.js serverless bundling can provide non-URL import.meta.url values.24 // Next.js serverless bundling can provide non-URL import.meta.url values.
packages/runtime-deno-subprocess/src/index.ts+1−1
@@ -131,7 +131,7 @@ const defaultDenoExecutable = (): string => { · 130 unmodified lines
131};131};
132 132
133const workerScriptPath = fileURLToPath(133const workerScriptPath = fileURLToPath(
134 new URL("./deno-subprocess-worker.mjs", import.meta.url),134 new URL("./deno-subprocess-worker.mjs", import.meta.url).toString(),
135);135);
136 136
137const writeMessage = (137const writeMessage = (