From 9e42e5cc4c9b42733aa59cfe8959c5b966298aa0 Mon Sep 17 00:00:00 2001 From: Dax Date: Tue, 8 Sep 2026 11:17:12 -0400 Subject: [PATCH] feat(core): refresh console provider config periodically (#47980) --- packages/core/src/plugin/provider/opencode.ts | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/core/src/plugin/provider/opencode.ts b/packages/core/src/plugin/provider/opencode.ts index e2917ac1664..c8d39ae5c48 100644 --- a/packages/core/src/plugin/provider/opencode.ts +++ b/packages/core/src/plugin/provider/opencode.ts @@ -1,4 +1,4 @@ -import { Duration, Effect, Schema, Semaphore, Stream } from "effect" +import { Duration, Effect, Equal, Schema, Semaphore, Stream } from "effect" import type { Scope } from "effect" import type { IntegrationOAuthMethodRegistration } from "@opencode/plugin/effect/integration" import { define } from "@opencode/plugin/effect/plugin" @@ -85,22 +85,24 @@ export const OpencodePlugin = define undefined)) : undefined - connected = connection !== undefined - providers = credential + const providers = credential ? yield* fetchProviders(http, credential).pipe( Effect.catch((cause) => Effect.logWarning("failed to load OpenCode provider config", { cause }).pipe(Effect.as(undefined)), ), ) : undefined + return { connected: connection !== undefined, providers } }) yield* ctx.integration.transform((editor) => { @@ -111,9 +113,9 @@ export const OpencodePlugin = define { - for (const [providerID, item] of Object.entries(providers ?? {})) { + for (const [providerID, item] of Object.entries(snapshot.providers ?? {})) { const source = catalog.provider.get(item.canonical ?? providerID) catalog.provider.update(providerID, (provider) => { if (source && source.provider !== provider) @@ -183,7 +185,7 @@ export const OpencodePlugin = define { if (!hasKey) { provider.activation = "enabled" @@ -199,12 +201,29 @@ export const OpencodePlugin = define loading.withPermit(load().pipe(Effect.andThen(ctx.catalog.reload()))) + const apply = Effect.fn("OpencodePlugin.apply")(function* (next: typeof snapshot) { + snapshot = next + yield* ctx.catalog.reload() + }) + const refresh = () => loading.withPermit(load().pipe(Effect.andThen(apply))) + yield* bus.subscribe(Credential.Event.Switched).pipe( Stream.filter((event) => event.data.integrationID === Integration.ID.make("opencode")), Stream.runForEach(refresh), Effect.forkScoped({ startImmediately: true }), ) + + // Console config can change independently of local credential activity, so re-fetch + // periodically and only rebuild the catalog when the loaded snapshot actually differs. + yield* Effect.sleep(Duration.minutes(10)).pipe( + Effect.andThen( + loading.withPermit( + load().pipe(Effect.flatMap((next) => (Equal.equals(snapshot, next) ? Effect.void : apply(next)))), + ), + ), + Effect.forever, + Effect.forkScoped, + ) }), })