| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
- import { createSimpleContext } from "@opencode-ai/ui/context"
- import { createGlobalEmitter } from "@solid-primitives/event-bus"
- import { batch, onCleanup } from "solid-js"
- import { usePlatform } from "./platform"
- import { useServer } from "./server"
- export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleContext({
- name: "GlobalSDK",
- init: () => {
- const server = useServer()
- const platform = usePlatform()
- const abort = new AbortController()
- const eventSdk = createOpencodeClient({
- baseUrl: server.url,
- signal: abort.signal,
- fetch: platform.fetch,
- })
- const emitter = createGlobalEmitter<{
- [key: string]: Event
- }>()
- type Queued = { directory: string; payload: Event }
- let queue: Array<Queued | undefined> = []
- let buffer: Array<Queued | undefined> = []
- const coalesced = new Map<string, number>()
- let timer: ReturnType<typeof setTimeout> | undefined
- let last = 0
- const key = (directory: string, payload: Event) => {
- if (payload.type === "session.status") return `session.status:${directory}:${payload.properties.sessionID}`
- if (payload.type === "lsp.updated") return `lsp.updated:${directory}`
- if (payload.type === "message.part.updated") {
- const part = payload.properties.part
- return `message.part.updated:${directory}:${part.messageID}:${part.id}`
- }
- }
- const flush = () => {
- if (timer) clearTimeout(timer)
- timer = undefined
- if (queue.length === 0) return
- const events = queue
- queue = buffer
- buffer = events
- queue.length = 0
- coalesced.clear()
- last = Date.now()
- batch(() => {
- for (const event of events) {
- if (!event) continue
- emitter.emit(event.directory, event.payload)
- }
- })
- buffer.length = 0
- }
- const schedule = () => {
- if (timer) return
- const elapsed = Date.now() - last
- timer = setTimeout(flush, Math.max(0, 16 - elapsed))
- }
- void (async () => {
- const events = await eventSdk.global.event()
- let yielded = Date.now()
- for await (const event of events.stream) {
- const directory = event.directory ?? "global"
- const payload = event.payload
- const k = key(directory, payload)
- if (k) {
- const i = coalesced.get(k)
- if (i !== undefined) {
- queue[i] = undefined
- }
- coalesced.set(k, queue.length)
- }
- queue.push({ directory, payload })
- schedule()
- if (Date.now() - yielded < 8) continue
- yielded = Date.now()
- await new Promise<void>((resolve) => setTimeout(resolve, 0))
- }
- })()
- .finally(flush)
- .catch(() => undefined)
- onCleanup(() => {
- abort.abort()
- flush()
- })
- const sdk = createOpencodeClient({
- baseUrl: server.url,
- fetch: platform.fetch,
- throwOnError: true,
- })
- return { url: server.url, client: sdk, event: emitter }
- },
- })
|