fix(core): fall back to Cloudflare environment IDs when not configured (#51963)

This commit is contained in:
Aiden Cline
2026-09-28 19:28:03 -05:00
committed by GitHub
parent 3ab5c1433c
commit 3babae35c0
4 changed files with 101 additions and 35 deletions
@@ -1,55 +1,62 @@
import { Effect } from "effect"
import { isArrayNonEmpty } from "effect/Array"
import { define } from "@opencode/plugin/effect/plugin"
import { Form } from "@opencode/schema/form"
import { Provider } from "../../provider.js"
import { iife } from "../../util/iife.js"
import { configuredSettings } from "./configured.js"
const providerID = Provider.ID.make("cloudflare-ai-gateway")
const accountIdField = Form.StringField.make({
type: "string",
key: "accountId",
title: "Enter your Cloudflare Account ID",
placeholder: "e.g. 1234567890abcdef1234567890abcdef",
required: true,
})
const gatewayIdField = Form.StringField.make({
type: "string",
key: "gatewayId",
title: "Enter your Cloudflare AI Gateway ID",
placeholder: "e.g. my-gateway",
required: true,
})
export const CloudflareAIGatewayPlugin = define({
id: "opencode.provider.cloudflare.ai.gateway",
effect: Effect.fn(function* (ctx) {
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID
const gatewayId = process.env.CLOUDFLARE_GATEWAY_ID
const configured = yield* configuredSettings(providerID)
const form = iife(() => {
if (typeof configured?.baseURL === "string") return
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID || stringOption(configured ?? {}, "accountId")
const gatewayId =
process.env.CLOUDFLARE_GATEWAY_ID ||
stringOption(configured ?? {}, "gatewayId") ||
stringOption(configured ?? {}, "gateway")
if (accountId && gatewayId) return
const accountIdForm = Form.StringField.make({
type: "string",
key: "accountId",
title: "Enter your Cloudflare Account ID",
placeholder: "e.g. 1234567890abcdef1234567890abcdef",
required: true,
})
const gatewayIdForm = Form.StringField.make({
type: "string",
key: "gatewayId",
title: "Enter your Cloudflare AI Gateway ID",
placeholder: "e.g. my-gateway",
required: true,
})
if (accountId) return Form.Fields.make([gatewayIdForm])
if (gatewayId) return Form.Fields.make([accountIdForm])
return Form.Fields.make([accountIdForm, gatewayIdForm])
})
const fields =
typeof configured?.baseURL === "string"
? []
: [
...(accountId || typeof configured?.accountId === "string" ? [] : [accountIdField]),
...(gatewayId || typeof configured?.gatewayId === "string" ? [] : [gatewayIdField]),
]
yield* ctx.integration.transform((editor) => {
editor.method.update({
integrationID: providerID,
method: {
type: "key",
label: "Gateway API token",
form,
form: isArrayNonEmpty(fields) ? Form.Fields.make(fields) : undefined,
},
})
})
yield* ctx.provider.transform((evt) => {
const item = evt.get(providerID)
if (!item || (!accountId && !gatewayId)) return
evt.update(item.provider.id, (provider) => {
if (typeof provider.settings?.baseURL === "string") return
provider.settings = {
...(accountId ? { accountId } : {}),
...(gatewayId ? { gatewayId } : {}),
...provider.settings,
}
})
})
}),
})
function stringOption(options: Record<string, unknown>, key: string) {
return typeof options[key] === "string" ? options[key] : undefined
}
@@ -50,7 +50,7 @@ export const CloudflareWorkersAIPlugin = define({
})
function resolveAccountId(options: Record<string, unknown>) {
return process.env.CLOUDFLARE_ACCOUNT_ID ?? stringOption(options, "accountId")
return stringOption(options, "accountId") ?? process.env.CLOUDFLARE_ACCOUNT_ID
}
function workersEndpoint(accountId: string) {
@@ -4,11 +4,19 @@ import { Integration } from "@opencode/core/integration"
import { Plugin } from "@opencode/core/plugin"
import { PluginHost } from "@opencode/core/plugin/host"
import { CloudflareAIGatewayPlugin } from "@opencode/core/plugin/provider/cloudflare-ai-gateway"
import { Provider } from "@opencode/core/provider"
import { withEnv } from "../fixture/env"
import { testEffect } from "../lib/effect"
import { PluginTestLayer } from "./fixture"
const it = testEffect(PluginTestLayer)
const providerID = Provider.ID.make("cloudflare-ai-gateway")
const seed = Effect.fn(function* (fn: (provider: Provider.MutableInfo) => void) {
const catalog = yield* Provider.Service
yield* catalog.transform((catalog) => catalog.update(providerID, fn))
return catalog
})
const addPlugin = Effect.fn(function* () {
const plugin = yield* Plugin.Service
@@ -46,4 +54,55 @@ describe("CloudflareAIGatewayPlugin", () => {
}),
),
)
it.effect("requests only the gateway ID when the environment provides the account ID", () =>
withEnv({ CLOUDFLARE_ACCOUNT_ID: "account", CLOUDFLARE_GATEWAY_ID: undefined }, () =>
Effect.gen(function* () {
yield* addPlugin()
const integrations = yield* Integration.Service
expect((yield* integrations.get(Integration.ID.make("cloudflare-ai-gateway")))?.methods).toContainEqual({
type: "key",
label: "Gateway API token",
form: [expect.objectContaining({ type: "string", key: "gatewayId", required: true })],
})
}),
),
)
it.effect("gives the provider its environment account and gateway IDs", () =>
withEnv({ CLOUDFLARE_ACCOUNT_ID: "account", CLOUDFLARE_GATEWAY_ID: "gateway" }, () =>
Effect.gen(function* () {
const catalog = yield* seed(() => {})
yield* addPlugin()
expect((yield* catalog.get(providerID))?.settings).toEqual({ accountId: "account", gatewayId: "gateway" })
}),
),
)
it.effect("prefers configured IDs over environment IDs", () =>
withEnv({ CLOUDFLARE_ACCOUNT_ID: "env-account", CLOUDFLARE_GATEWAY_ID: "env-gateway" }, () =>
Effect.gen(function* () {
const catalog = yield* seed((provider) => {
provider.settings = { accountId: "configured-account", gatewayId: "configured-gateway" }
})
yield* addPlugin()
expect((yield* catalog.get(providerID))?.settings).toEqual({
accountId: "configured-account",
gatewayId: "configured-gateway",
})
}),
),
)
it.effect("leaves a configured endpoint URL untouched", () =>
withEnv({ CLOUDFLARE_ACCOUNT_ID: "account", CLOUDFLARE_GATEWAY_ID: "gateway" }, () =>
Effect.gen(function* () {
const catalog = yield* seed((provider) => {
provider.settings = { baseURL: "https://proxy.example/v1" }
})
yield* addPlugin()
expect((yield* catalog.get(providerID))?.settings).toEqual({ baseURL: "https://proxy.example/v1" })
}),
),
)
})
@@ -119,7 +119,7 @@ describe("CloudflareWorkersAIPlugin", () => {
),
)
it.effect("uses env account ID over configured account ID", () =>
it.effect("uses configured account ID over env account ID", () =>
withEnv({ CLOUDFLARE_ACCOUNT_ID: "env-acct" }, () =>
Effect.gen(function* () {
const catalog = yield* seed((provider) => {
@@ -129,7 +129,7 @@ describe("CloudflareWorkersAIPlugin", () => {
yield* addPlugin()
expect(required(yield* catalog.get(providerID))).toMatchObject({
package: "aisdk:test-provider",
settings: { baseURL: "https://api.cloudflare.com/client/v4/accounts/env-acct/ai/v1" },
settings: { baseURL: "https://api.cloudflare.com/client/v4/accounts/configured-acct/ai/v1" },
})
}),
),