feat(plugin): select event subscriptions

This commit is contained in:
Dax Raad
2026-08-15 18:55:44 -04:00
parent 6a7d6c5adc
commit 58deb52dcd
15 changed files with 141 additions and 26 deletions
+13 -1
View File
@@ -1,3 +1,15 @@
import type { EventApi } from "@opencode-ai/client/effect/api"
import type { OpenCodeEvent } from "@opencode-ai/client/effect"
import type { Stream } from "effect"
export interface EventDomain extends Pick<EventApi<unknown>, "subscribe"> {}
export type PluginEvent = Exclude<OpenCodeEvent, { readonly type: "server.connected" }>
export type PluginEventType = PluginEvent["type"]
export interface EventSubscribe {
(): Stream.Stream<PluginEvent, unknown>
(type: PluginEventType): Stream.Stream<PluginEvent, unknown>
}
export interface EventDomain extends Omit<EventApi<unknown>, "subscribe"> {
readonly subscribe: EventSubscribe
}
+7 -4
View File
@@ -2,6 +2,7 @@ import { Tool } from "@opencode-ai/schema/tool"
import { Effect, Schema, SchemaAST, Scope, Stream } from "effect"
import { HttpApiEndpoint, HttpApiSchema } from "effect/unstable/httpapi"
import { define } from "../effect/plugin.js"
import type { PluginEventType } from "./event.js"
import type { Context, Plugin } from "./plugin.js"
import type { Info } from "./tool.js"
@@ -149,13 +150,15 @@ export function fromPromise(plugin: Plugin) {
reload: () => run(host.command.reload()),
},
event: {
subscribe: () =>
Stream.toAsyncIterable(
host.event.subscribe().pipe(
subscribe: (type?: PluginEventType) => {
const events = type === undefined ? host.event.subscribe() : host.event.subscribe(type)
return Stream.toAsyncIterable(
events.pipe(
Stream.mapEffect((event) => Schema.encodeUnknownEffect(OpenCodeEvent)(event)),
Stream.map((event) => event as unknown as PromiseEvent),
),
),
)
},
},
integration: {
list: adaptApiMethod(IntegrationEndpoints["integration.list"], host.integration.list),
+12 -1
View File
@@ -1,3 +1,14 @@
import type { OpenCodeEvent } from "@opencode-ai/client"
import type { EventApi } from "@opencode-ai/client/promise/api"
export interface EventDomain extends Pick<EventApi, "subscribe"> {}
export type PluginEvent = Exclude<OpenCodeEvent, { readonly type: "server.connected" }>
export type PluginEventType = PluginEvent["type"]
export interface EventSubscribe {
(): AsyncIterable<PluginEvent>
(type: PluginEventType): AsyncIterable<PluginEvent>
}
export interface EventDomain extends Omit<EventApi, "subscribe"> {
readonly subscribe: EventSubscribe
}
+26
View File
@@ -0,0 +1,26 @@
import { expect, test } from "bun:test"
import type { Context as EffectContext } from "../src/effect/plugin.js"
import type { Context as PromiseContext } from "../src/promise/plugin.js"
function effectSubscriptions(ctx: EffectContext) {
ctx.event.subscribe()
ctx.event.subscribe("config.updated")
// @ts-expect-error server.connected is a network-only marker
ctx.event.subscribe("server.connected")
// @ts-expect-error plugin subscriptions select at most one event type
ctx.event.subscribe(["config.updated"])
}
function promiseSubscriptions(ctx: PromiseContext) {
ctx.event.subscribe()
ctx.event.subscribe("config.updated")
// @ts-expect-error server.connected is a network-only marker
ctx.event.subscribe("server.connected")
// @ts-expect-error plugin subscriptions select at most one event type
ctx.event.subscribe(["config.updated"])
}
test("event subscription types support wildcard and one public event", () => {
expect(effectSubscriptions).toBeFunction()
expect(promiseSubscriptions).toBeFunction()
})