feat(sdk): expose live event stream (#34098)

This commit is contained in:
Kit Langton 2026-06-26 21:16:33 +02:00 committed by GitHub
commit 42e6b7db32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 395 additions and 56 deletions

View file

@ -1,6 +1,6 @@
import { makeDefaultApi } from "@opencode-ai/protocol/api"
import { InvalidRequestError, SessionNotFoundError } from "@opencode-ai/protocol/errors"
import { HttpApiMiddleware } from "effect/unstable/httpapi"
import { HttpApi, HttpApiMiddleware } from "effect/unstable/httpapi"
class LocationMiddleware extends HttpApiMiddleware.Service<LocationMiddleware>()(
"@opencode-ai/client/LocationMiddleware",
@ -17,3 +17,5 @@ const Api = makeDefaultApi({
})
export const SessionGroup = Api.groups["server.session"]
export const EventGroup = Api.groups["server.event"]
export const ClientApi = HttpApi.make("opencode-client").add(SessionGroup).add(EventGroup)

View file

@ -10,3 +10,4 @@ export { Session } from "@opencode-ai/schema/session"
export { SessionInput } from "@opencode-ai/schema/session-input"
export { SessionMessage } from "@opencode-ai/schema/session-message"
export { Prompt } from "@opencode-ai/schema/prompt"
export type { OpenCodeEvent } from "@opencode-ai/protocol/groups/event"

View file

@ -2,13 +2,11 @@
import { Effect, Stream, Schema } from "effect"
import { Sse } from "effect/unstable/encoding"
import { HttpClientError } from "effect/unstable/http"
import { HttpApi, HttpApiClient } from "effect/unstable/httpapi"
import { SessionGroup } from "../contract"
import { HttpApiClient } from "effect/unstable/httpapi"
import { ClientApi } from "../contract"
import { ClientError } from "./client-error"
const Api = HttpApi.make("generated").add(SessionGroup)
type RawClient = HttpApiClient.ForApi<typeof Api>
type RawClient = HttpApiClient.ForApi<typeof ClientApi>
const mapClientError = <E>(error: E) =>
HttpClientError.isHttpClientError(error) || Schema.isSchemaError(error) || Sse.Retry.is(error)
@ -210,7 +208,20 @@ const adaptGroup0 = (raw: RawClient["server.session"]) => ({
message: Endpoint0_16(raw),
})
const adaptClient = (raw: RawClient) => ({ sessions: adaptGroup0(raw["server.session"]) })
const Endpoint1_0 = (raw: RawClient["server.event"]) => () =>
Stream.unwrap(
raw["event.subscribe"]({}).pipe(
Effect.mapError(mapClientError),
Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError))),
),
)
const adaptGroup1 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint1_0(raw) })
const adaptClient = (raw: RawClient) => ({
sessions: adaptGroup0(raw["server.session"]),
events: adaptGroup1(raw["server.event"]),
})
export const make = (options?: { readonly baseUrl?: URL | string }) =>
HttpApiClient.make(Api, options).pipe(Effect.map(adaptClient))
HttpApiClient.make(ClientApi, options).pipe(Effect.map(adaptClient))

View file

@ -32,6 +32,7 @@ import type {
SessionsInterruptOutput,
SessionsMessageInput,
SessionsMessageOutput,
EventsSubscribeOutput,
} from "./types"
import { ClientError } from "./client-error"
@ -373,6 +374,13 @@ export function make(options: ClientOptions) {
requestOptions,
).then((value) => value.data),
},
events: {
subscribe: (requestOptions?: RequestOptions): AsyncIterable<EventsSubscribeOutput> =>
sse<EventsSubscribeOutput>(
{ method: "GET", path: `/api/event`, successStatus: 200, declaredStatuses: [401, 400], empty: false },
requestOptions,
),
},
}
}

View file

@ -1,3 +1,5 @@
import type { OpenCodeEventEncoded } from "@opencode-ai/protocol/groups/event"
export type JsonValue =
| null
| boolean
@ -1666,3 +1668,5 @@ export type SessionsMessageOutput = {
readonly time: { readonly created: number }
}
}["data"]
export type EventsSubscribeOutput = OpenCodeEventEncoded

View file

@ -1 +1,2 @@
export * from "./generated/index"
export type { EventsSubscribeOutput as OpenCodeEvent } from "./generated/types"