Route hosted plugin outbound requests through a shared Effect HttpClient layer that blocks local/private/metadata targets and re-checks redirects. Thread the executor-provided HTTP client through OpenAPI, GraphQL, MCP probing, and OAuth discovery, keep local harnesses on the normal fetch client, and split the restored security coverage into the relevant existing suites. Fix cloud migration scripts to invoke the local Drizzle binary under op run.
Showing the changes to packages/plugins/openapi/src (1 of 19 files). Show all
Unified · Split
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | import { Effect, Option, Predicate, Schema } from "effect"; |
| 2 | 2 | import type { Layer } from "effect"; |
| 3 | −import { FetchHttpClient, HttpClient } from "effect/unstable/http"; | |
| 3 | +import { HttpClient } from "effect/unstable/http"; | |
| 4 | 4 | |
| 5 | 5 | import { OpenApiGroup } from "../api/group"; |
| 6 | 6 | import { OpenApiExtensionService, OpenApiHandlers } from "../api/handlers"; |
| @@ -754,8 +754,6 @@ const toOpenApiSourceConfig = ( · 747 unmodified lines | ||
| 754 | 754 | const isHttpUrl = (s: string): boolean => s.startsWith("http://") || s.startsWith("https://"); |
| 755 | 755 | |
| 756 | 756 | export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { |
| 757 | − const httpClientLayer = options?.httpClientLayer ?? FetchHttpClient.layer; | |
| 758 | − | |
| 759 | 757 | type RebuildInput = { |
| 760 | 758 | readonly specText: string; |
| 761 | 759 | readonly scope: string; |
| @@ -902,6 +900,7 @@ export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { · 140 unmodified lines | ||
| 902 | 900 | // than surface a 500 through the unwhitelisted error channel. |
| 903 | 901 | const refreshSourceInternal = (ctx: PluginCtx<OpenapiStore>, sourceId: string, scope: string) => |
| 904 | 902 | Effect.gen(function* () { |
| 903 | + const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer; | |
| 905 | 904 | const existing = yield* ctx.storage.getSource(sourceId, scope); |
| 906 | 905 | if (!existing) return; |
| 907 | 906 | const effective = yield* resolveEffectiveSourceConfig(ctx, existing); |
| @@ -938,6 +937,7 @@ export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { · 30 unmodified lines | ||
| 938 | 937 | storage: (deps): OpenapiStore => makeDefaultOpenapiStore(deps), |
| 939 | 938 | |
| 940 | 939 | extension: (ctx) => { |
| 940 | + const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer; | |
| 941 | 941 | const addSpecInternal = (config: OpenApiSpecConfig) => |
| 942 | 942 | Effect.gen(function* () { |
| 943 | 943 | // Resolve URL → text and parse BEFORE opening a transaction. |
| @@ -1173,6 +1173,7 @@ export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { · 229 unmodified lines | ||
| 1173 | 1173 | |
| 1174 | 1174 | invokeTool: ({ ctx, toolRow, args }) => |
| 1175 | 1175 | Effect.gen(function* () { |
| 1176 | + const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer; | |
| 1176 | 1177 | // toolRow.scope_id is the resolved owning scope of the tool |
| 1177 | 1178 | // (innermost-wins from the executor's stack). The matching |
| 1178 | 1179 | // openapi_operation + openapi_source rows live at the same |
| @@ -1313,8 +1314,9 @@ export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { · 134 unmodified lines | ||
| 1313 | 1314 | // means stale UI state, which is worth surfacing to the caller. |
| 1314 | 1315 | refreshSource: ({ ctx, sourceId, scope }) => refreshSourceInternal(ctx, sourceId, scope), |
| 1315 | 1316 | |
| 1316 | − detect: ({ url }) => | |
| 1317 | + detect: ({ ctx, url }) => | |
| 1317 | 1318 | Effect.gen(function* () { |
| 1319 | + const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer; | |
| 1318 | 1320 | const trimmed = url.trim(); |
| 1319 | 1321 | if (!trimmed) return null; |
| 1320 | 1322 | const parsed = yield* Effect.try({ |