fix(core): load OpenCode provider config asynchronously
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { Duration, Effect, Schema, Stream } from "effect"
|
import { Duration, Effect, Schema, Semaphore, Stream } from "effect"
|
||||||
import type { Scope } from "effect"
|
import type { Scope } from "effect"
|
||||||
import type { IntegrationOAuthMethodRegistration } from "@opencode-ai/plugin/v2/effect/integration"
|
import type { IntegrationOAuthMethodRegistration } from "@opencode-ai/plugin/v2/effect/integration"
|
||||||
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
|
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
|
||||||
@@ -79,6 +79,7 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
|
|||||||
effect: Effect.fn(function* (ctx) {
|
effect: Effect.fn(function* (ctx) {
|
||||||
const events = yield* EventV2.Service
|
const events = yield* EventV2.Service
|
||||||
const http = yield* HttpClient.HttpClient
|
const http = yield* HttpClient.HttpClient
|
||||||
|
const loading = Semaphore.makeUnsafe(1)
|
||||||
let connected = false
|
let connected = false
|
||||||
let providers: typeof ConfigV1.Info.Type.provider | undefined
|
let providers: typeof ConfigV1.Info.Type.provider | undefined
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
|
|||||||
draft.method.update({ integrationID: "opencode", method: { type: "key", label: "API key (service account)" } })
|
draft.method.update({ integrationID: "opencode", method: { type: "key", label: "API key (service account)" } })
|
||||||
})
|
})
|
||||||
|
|
||||||
yield* load()
|
connected = (yield* ctx.integration.connection.active("opencode")) !== undefined
|
||||||
yield* ctx.catalog.transform((catalog) => {
|
yield* ctx.catalog.transform((catalog) => {
|
||||||
for (const [providerID, item] of Object.entries(providers ?? {})) {
|
for (const [providerID, item] of Object.entries(providers ?? {})) {
|
||||||
catalog.provider.update(providerID, (provider) => {
|
catalog.provider.update(providerID, (provider) => {
|
||||||
@@ -176,11 +177,13 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const refresh = () => loading.withPermit(load().pipe(Effect.andThen(ctx.catalog.reload())))
|
||||||
yield* events.subscribe(Integration.Event.ConnectionUpdated).pipe(
|
yield* events.subscribe(Integration.Event.ConnectionUpdated).pipe(
|
||||||
Stream.filter((event) => event.data.integrationID === Integration.ID.make("opencode")),
|
Stream.filter((event) => event.data.integrationID === Integration.ID.make("opencode")),
|
||||||
Stream.runForEach(() => load().pipe(Effect.andThen(ctx.catalog.reload()))),
|
Stream.runForEach(refresh),
|
||||||
Effect.forkScoped({ startImmediately: true }),
|
Effect.forkScoped({ startImmediately: true }),
|
||||||
)
|
)
|
||||||
|
yield* refresh().pipe(Effect.forkScoped)
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,20 @@ function required<T>(value: T | undefined): T {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function eventually<A>(
|
||||||
|
effect: Effect.Effect<A>,
|
||||||
|
predicate: (value: A) => boolean,
|
||||||
|
remaining = 1000,
|
||||||
|
): Effect.Effect<A, Error> {
|
||||||
|
return Effect.gen(function* () {
|
||||||
|
const value = yield* effect
|
||||||
|
if (predicate(value)) return value
|
||||||
|
if (remaining === 0) return yield* Effect.fail(new Error("Timed out waiting for value"))
|
||||||
|
yield* Effect.promise(() => Bun.sleep(1))
|
||||||
|
return yield* eventually(effect, predicate, remaining - 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () => Effect.Effect<A, E, R>) {
|
function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () => Effect.Effect<A, E, R>) {
|
||||||
return Effect.acquireUseRelease(
|
return Effect.acquireUseRelease(
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
@@ -67,11 +81,14 @@ describe("OpencodePlugin", () => {
|
|||||||
Effect.acquireUseRelease(
|
Effect.acquireUseRelease(
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
const authorization: Array<string | null> = []
|
const authorization: Array<string | null> = []
|
||||||
|
const gate = Promise.withResolvers<void>()
|
||||||
return {
|
return {
|
||||||
authorization,
|
authorization,
|
||||||
|
release: gate.resolve,
|
||||||
server: Bun.serve({
|
server: Bun.serve({
|
||||||
port: 0,
|
port: 0,
|
||||||
fetch: (request) => {
|
fetch: async (request) => {
|
||||||
|
await gate.promise
|
||||||
authorization.push(request.headers.get("authorization"))
|
authorization.push(request.headers.get("authorization"))
|
||||||
const origin = new URL(request.url).origin
|
const origin = new URL(request.url).origin
|
||||||
return Response.json({
|
return Response.json({
|
||||||
@@ -110,7 +127,7 @@ describe("OpencodePlugin", () => {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
({ authorization, server }) =>
|
({ authorization, release, server }) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const credentials = yield* Credential.Service
|
const credentials = yield* Credential.Service
|
||||||
const catalog = yield* Catalog.Service
|
const catalog = yield* Catalog.Service
|
||||||
@@ -128,8 +145,15 @@ describe("OpencodePlugin", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
yield* addPlugin()
|
yield* addPlugin()
|
||||||
|
expect(authorization).toEqual([])
|
||||||
|
release()
|
||||||
|
|
||||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("remote")))
|
const provider = required(
|
||||||
|
yield* eventually(
|
||||||
|
catalog.provider.get(ProviderV2.ID.make("remote")),
|
||||||
|
(item) => item?.integrationID === Integration.ID.make("opencode"),
|
||||||
|
),
|
||||||
|
)
|
||||||
expect(provider).toMatchObject({
|
expect(provider).toMatchObject({
|
||||||
name: "Remote",
|
name: "Remote",
|
||||||
integrationID: "opencode",
|
integrationID: "opencode",
|
||||||
|
|||||||
Reference in New Issue
Block a user