From a2cb4909daac81a297e80c3a5059b9bcff8f6b3e Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Mon, 13 Apr 2026 21:24:20 -0400 Subject: [PATCH] refactor(plugin): remove async facade exports (#22367) --- packages/opencode/src/agent/agent.ts | 6 +- packages/opencode/src/cli/cmd/providers.ts | 12 +++- packages/opencode/src/plugin/index.ts | 18 ------ .../test/plugin/loader-shared.test.ts | 9 ++- packages/opencode/test/plugin/trigger.test.ts | 61 ++++++++++--------- .../test/plugin/workspace-adaptor.test.ts | 21 ++++--- .../opencode/test/provider/provider.test.ts | 13 ++-- 7 files changed, 72 insertions(+), 68 deletions(-) diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index cebcaa048f..562c90cc02 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -73,6 +73,7 @@ export namespace Agent { Effect.gen(function* () { const config = yield* Config.Service const auth = yield* Auth.Service + const plugin = yield* Plugin.Service const skill = yield* Skill.Service const provider = yield* Provider.Service @@ -335,9 +336,7 @@ export namespace Agent { const language = yield* provider.getLanguage(resolved) const system = [PROMPT_GENERATE] - yield* Effect.promise(() => - Plugin.trigger("experimental.chat.system.transform", { model: resolved }, { system }), - ) + yield* plugin.trigger("experimental.chat.system.transform", { model: resolved }, { system }) const existing = yield* InstanceState.useEffect(state, (s) => s.list()) // TODO: clean this up so provider specific logic doesnt bleed over @@ -398,6 +397,7 @@ export namespace Agent { ) export const defaultLayer = layer.pipe( + Layer.provide(Plugin.defaultLayer), Layer.provide(Provider.defaultLayer), Layer.provide(Auth.defaultLayer), Layer.provide(Config.defaultLayer), diff --git a/packages/opencode/src/cli/cmd/providers.ts b/packages/opencode/src/cli/cmd/providers.ts index 8d6159c9a5..5d5cd1ff88 100644 --- a/packages/opencode/src/cli/cmd/providers.ts +++ b/packages/opencode/src/cli/cmd/providers.ts @@ -340,6 +340,12 @@ export const ProvidersLoginCommand = cmd({ } return filtered }) + const hooks = await AppRuntime.runPromise( + Effect.gen(function* () { + const plugin = yield* Plugin.Service + return yield* plugin.list() + }), + ) const priority: Record = { opencode: 0, @@ -351,7 +357,7 @@ export const ProvidersLoginCommand = cmd({ vercel: 6, } const pluginProviders = resolvePluginProviders({ - hooks: await Plugin.list(), + hooks, existingProviders: providers, disabled, enabled, @@ -408,7 +414,7 @@ export const ProvidersLoginCommand = cmd({ provider = selected as string } - const plugin = await Plugin.list().then((x) => x.findLast((x) => x.auth?.provider === provider)) + const plugin = hooks.findLast((x) => x.auth?.provider === provider) if (plugin && plugin.auth) { const handled = await handlePluginAuth({ auth: plugin.auth }, provider, args.method) if (handled) return @@ -422,7 +428,7 @@ export const ProvidersLoginCommand = cmd({ if (prompts.isCancel(custom)) throw new UI.CancelledError() provider = custom.replace(/^@ai-sdk\//, "") - const customPlugin = await Plugin.list().then((x) => x.findLast((x) => x.auth?.provider === provider)) + const customPlugin = hooks.findLast((x) => x.auth?.provider === provider) if (customPlugin && customPlugin.auth) { const handled = await handlePluginAuth({ auth: customPlugin.auth }, provider, args.method) if (handled) return diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index 60942915a3..0dc53d997d 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -20,7 +20,6 @@ import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cl import { Effect, Layer, Context, Stream } from "effect" import { EffectLogger } from "@/effect/logger" import { InstanceState } from "@/effect/instance-state" -import { makeRuntime } from "@/effect/run-service" import { errorMessage } from "@/util/error" import { PluginLoader } from "./loader" import { parsePluginSpecifier, readPluginId, readV1Plugin, resolvePluginId } from "./shared" @@ -290,21 +289,4 @@ export namespace Plugin { ) export const defaultLayer = layer.pipe(Layer.provide(Bus.layer), Layer.provide(Config.defaultLayer)) - const { runPromise } = makeRuntime(Service, defaultLayer) - - export async function trigger< - Name extends TriggerName, - Input = Parameters[Name]>[0], - Output = Parameters[Name]>[1], - >(name: Name, input: Input, output: Output): Promise { - return runPromise((svc) => svc.trigger(name, input, output)) - } - - export async function list(): Promise { - return runPromise((svc) => svc.list()) - } - - export async function init() { - return runPromise((svc) => svc.init()) - } } diff --git a/packages/opencode/test/plugin/loader-shared.test.ts b/packages/opencode/test/plugin/loader-shared.test.ts index 32b6c601d1..5f1e2b1686 100644 --- a/packages/opencode/test/plugin/loader-shared.test.ts +++ b/packages/opencode/test/plugin/loader-shared.test.ts @@ -1,4 +1,5 @@ import { afterAll, afterEach, describe, expect, spyOn, test } from "bun:test" +import { Effect } from "effect" import fs from "fs/promises" import path from "path" import { pathToFileURL } from "url" @@ -29,9 +30,11 @@ afterEach(async () => { async function load(dir: string) { return Instance.provide({ directory: dir, - fn: async () => { - await Plugin.list() - }, + fn: async () => + Effect.gen(function* () { + const plugin = yield* Plugin.Service + yield* plugin.list() + }).pipe(Effect.provide(Plugin.defaultLayer), Effect.runPromise), }) } diff --git a/packages/opencode/test/plugin/trigger.test.ts b/packages/opencode/test/plugin/trigger.test.ts index d89d930c69..972ba61b14 100644 --- a/packages/opencode/test/plugin/trigger.test.ts +++ b/packages/opencode/test/plugin/trigger.test.ts @@ -1,4 +1,5 @@ import { afterAll, afterEach, describe, expect, test } from "bun:test" +import { Effect } from "effect" import path from "path" import { pathToFileURL } from "url" import { tmpdir } from "../fixture/fixture" @@ -56,20 +57,22 @@ describe("plugin.trigger", () => { const out = await Instance.provide({ directory: tmp.path, - fn: async () => { - const out = { system: [] as string[] } - await Plugin.trigger( - "experimental.chat.system.transform", - { - model: { - providerID: "anthropic", - modelID: "claude-sonnet-4-6", - } as any, - }, - out, - ) - return out - }, + fn: async () => + Effect.gen(function* () { + const plugin = yield* Plugin.Service + const out = { system: [] as string[] } + yield* plugin.trigger( + "experimental.chat.system.transform", + { + model: { + providerID: "anthropic", + modelID: "claude-sonnet-4-6", + } as any, + }, + out, + ) + return out + }).pipe(Effect.provide(Plugin.defaultLayer), Effect.runPromise), }) expect(out.system).toEqual(["sync"]) @@ -90,20 +93,22 @@ describe("plugin.trigger", () => { const out = await Instance.provide({ directory: tmp.path, - fn: async () => { - const out = { system: [] as string[] } - await Plugin.trigger( - "experimental.chat.system.transform", - { - model: { - providerID: "anthropic", - modelID: "claude-sonnet-4-6", - } as any, - }, - out, - ) - return out - }, + fn: async () => + Effect.gen(function* () { + const plugin = yield* Plugin.Service + const out = { system: [] as string[] } + yield* plugin.trigger( + "experimental.chat.system.transform", + { + model: { + providerID: "anthropic", + modelID: "claude-sonnet-4-6", + } as any, + }, + out, + ) + return out + }).pipe(Effect.provide(Plugin.defaultLayer), Effect.runPromise), }) expect(out.system).toEqual(["async"]) diff --git a/packages/opencode/test/plugin/workspace-adaptor.test.ts b/packages/opencode/test/plugin/workspace-adaptor.test.ts index cc098b124b..a5f56df5e9 100644 --- a/packages/opencode/test/plugin/workspace-adaptor.test.ts +++ b/packages/opencode/test/plugin/workspace-adaptor.test.ts @@ -1,4 +1,5 @@ import { afterAll, afterEach, describe, expect, test } from "bun:test" +import { Effect } from "effect" import path from "path" import { pathToFileURL } from "url" import { tmpdir } from "../fixture/fixture" @@ -72,15 +73,17 @@ describe("plugin.workspace", () => { const info = await Instance.provide({ directory: tmp.path, - fn: async () => { - await Plugin.init() - return Workspace.create({ - type: tmp.extra.type, - branch: null, - extra: { key: "value" }, - projectID: Instance.project.id, - }) - }, + fn: async () => + Effect.gen(function* () { + const plugin = yield* Plugin.Service + yield* plugin.init() + return Workspace.create({ + type: tmp.extra.type, + branch: null, + extra: { key: "value" }, + projectID: Instance.project.id, + }) + }).pipe(Effect.provide(Plugin.defaultLayer), Effect.runPromise), }) expect(info.type).toBe(tmp.extra.type) diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index 73e77be5fd..ac990bc0fb 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -2436,10 +2436,15 @@ test("plugin config providers persist after instance dispose", async () => { const first = await Instance.provide({ directory: tmp.path, - fn: async () => { - await Plugin.init() - return list() - }, + fn: async () => + AppRuntime.runPromise( + Effect.gen(function* () { + const plugin = yield* Plugin.Service + const provider = yield* Provider.Service + yield* plugin.init() + return yield* provider.list() + }), + ), }) expect(first[ProviderID.make("demo")]).toBeDefined() expect(first[ProviderID.make("demo")].models[ModelID.make("chat")]).toBeDefined()