feat(config): support well-known remote_config (#26054)

This commit is contained in:
Dax 2026-05-06 11:12:23 -04:00 committed by GitHub
parent 63a175b50d
commit d9c18381a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 132 additions and 2 deletions

View file

@ -70,6 +70,40 @@ function normalizeLoadedConfig(data: unknown, source: string) {
return copy
}
async function substituteWellKnownRemoteConfig(input: {
value: unknown
dir: string
source: string
}) {
if (!isRecord(input.value) || typeof input.value.url !== "string") return
const url = await ConfigVariable.substitute({
text: input.value.url,
type: "virtual",
dir: input.dir,
source: input.source,
})
const headers = isRecord(input.value.headers)
? Object.fromEntries(
await Promise.all(
Object.entries(input.value.headers)
.filter((entry): entry is [string, string] => typeof entry[1] === "string")
.map(async ([key, value]) => [
key,
await ConfigVariable.substitute({
text: value,
type: "virtual",
dir: input.dir,
source: input.source,
}),
]),
),
)
: undefined
return { url, headers }
}
async function resolveLoadedPlugins<T extends { plugin?: ConfigPlugin.Spec[] }>(config: T, filepath: string) {
if (!config.plugin) return config
for (let i = 0; i < config.plugin.length; i++) {
@ -494,8 +528,27 @@ export const layer = Layer.effect(
if (!response.ok) {
throw new Error(`failed to fetch remote config from ${url}: ${response.status}`)
}
const wellknown = (yield* Effect.promise(() => response.json())) as { config?: Record<string, unknown> }
const remoteConfig = wellknown.config ?? {}
const wellknown = (yield* Effect.promise(() => response.json())) as {
config?: Record<string, unknown>
remote_config?: unknown
}
const remote = yield* Effect.promise(() =>
substituteWellKnownRemoteConfig({
value: wellknown.remote_config,
dir: url,
source: `${url}/.well-known/opencode`,
}),
)
const fetchedConfig = remote
? ((yield* Effect.promise(async () => {
log.debug("fetching remote config", { url: remote.url })
const response = await fetch(remote.url, { headers: remote.headers })
if (!response.ok) throw new Error(`failed to fetch remote config from ${remote.url}: ${response.status}`)
const data = await response.json()
return isRecord(data) && isRecord(data.config) ? data.config : data
})) as Record<string, unknown>)
: {}
const remoteConfig = mergeConfig(wellknown.config ?? {}, fetchedConfig as Info)
if (!remoteConfig.$schema) remoteConfig.$schema = "https://opencode.ai/config.json"
const source = `${url}/.well-known/opencode`
const next = yield* loadConfig(JSON.stringify(remoteConfig), {

View file

@ -1972,6 +1972,83 @@ test("wellknown URL with trailing slash is normalized", async () => {
}
})
test("wellknown remote_config supports templated env vars in headers", async () => {
const originalFetch = globalThis.fetch
const originalToken = process.env.TEST_TOKEN
let wellknownFetchedUrl: string | undefined
let remoteFetchedUrl: string | undefined
let remoteHeaders: HeadersInit | undefined
globalThis.fetch = mock((url: string | URL | Request, init?: RequestInit) => {
const urlStr = url instanceof Request ? url.url : url instanceof URL ? url.href : url
if (urlStr.includes(".well-known/opencode")) {
wellknownFetchedUrl = urlStr
return Promise.resolve(
new Response(
JSON.stringify({
remote_config: {
url: "https://config.example.com/opencode.json",
headers: {
Authorization: "Bearer {env:TEST_TOKEN}",
},
},
}),
{ status: 200 },
),
)
}
if (urlStr.includes("config.example.com")) {
remoteFetchedUrl = urlStr
remoteHeaders = init?.headers
return Promise.resolve(
new Response(
JSON.stringify({
mcp: { confluence: { type: "remote", url: "https://confluence.example.com/mcp", enabled: true } },
}),
{ status: 200 },
),
)
}
return originalFetch(url, init)
}) as unknown as typeof fetch
const fakeAuth = Layer.mock(Auth.Service)({
all: () =>
Effect.succeed({
"https://example.com": new Auth.WellKnown({ type: "wellknown", key: "TEST_TOKEN", token: "test-token" }),
}),
})
const layer = Config.layer.pipe(
Layer.provide(testFlock),
Layer.provide(AppFileSystem.defaultLayer),
Layer.provide(Env.defaultLayer),
Layer.provide(fakeAuth),
Layer.provide(emptyAccount),
Layer.provideMerge(infra),
Layer.provide(noopNpm),
)
try {
await provideTmpdirInstance(
() =>
Config.Service.use((svc) =>
Effect.gen(function* () {
const config = yield* svc.get()
expect(wellknownFetchedUrl).toBe("https://example.com/.well-known/opencode")
expect(remoteFetchedUrl).toBe("https://config.example.com/opencode.json")
expect(remoteHeaders).toEqual({ Authorization: "Bearer test-token" })
expect(config.mcp?.confluence?.enabled).toBe(true)
}),
),
{ git: true },
).pipe(Effect.scoped, Effect.provide(layer), Effect.runPromise)
} finally {
globalThis.fetch = originalFetch
if (originalToken === undefined) delete process.env.TEST_TOKEN
else process.env.TEST_TOKEN = originalToken
}
})
describe("resolvePluginSpec", () => {
test("keeps package specs unchanged", async () => {
await using tmp = await tmpdir()