fix(httpapi): expose v2 catalog errors (#28498)

This commit is contained in:
Shoubhit Dash
2026-05-21 00:53:35 +05:30
committed by GitHub
parent 7690481fc1
commit 4308dd75fb
8 changed files with 82 additions and 14 deletions

View File

@@ -62,6 +62,8 @@ type Trace = {
write(type: string, data?: unknown): void
}
const StreamClosed = undefined as never
type StreamInput = {
sdk: OpencodeClient
directory?: string
@@ -418,12 +420,12 @@ function createLayer(input: StreamInput) {
),
(events) =>
Effect.sync(() => {
void events.stream.return(undefined).catch(() => {})
void events.stream.return(StreamClosed).catch(() => {})
}),
),
)
closeStream = () => {
void events.stream.return(undefined).catch(() => {})
void events.stream.return(StreamClosed).catch(() => {})
}
input.trace?.write("recv.subscribe", {
sessionID: input.sessionID,

View File

@@ -1,6 +1,7 @@
import { ModelV2 } from "@opencode-ai/core/model"
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { ServiceUnavailableError } from "../../errors"
import { V2Authorization } from "../../middleware/authorization"
import { LocationQuery, locationQueryOpenApi, V2LocationMiddleware } from "./location"
@@ -9,6 +10,7 @@ export const ModelGroup = HttpApiGroup.make("v2.model")
HttpApiEndpoint.get("models", "/api/model", {
query: LocationQuery,
success: Schema.Array(ModelV2.Info),
error: ServiceUnavailableError,
})
.annotateMerge(locationQueryOpenApi)
.annotateMerge(

View File

@@ -1,7 +1,7 @@
import { ProviderV2 } from "@opencode-ai/core/provider"
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { ApiNotFoundError } from "../../errors"
import { ProviderNotFoundError, ServiceUnavailableError } from "../../errors"
import { V2Authorization } from "../../middleware/authorization"
import { LocationQuery, locationQueryOpenApi, V2LocationMiddleware } from "./location"
@@ -10,6 +10,7 @@ export const ProviderGroup = HttpApiGroup.make("v2.provider")
HttpApiEndpoint.get("providers", "/api/provider", {
query: LocationQuery,
success: Schema.Array(ProviderV2.Info),
error: ServiceUnavailableError,
})
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
@@ -25,7 +26,7 @@ export const ProviderGroup = HttpApiGroup.make("v2.provider")
params: { providerID: ProviderV2.ID },
query: LocationQuery,
success: ProviderV2.Info,
error: ApiNotFoundError,
error: [ProviderNotFoundError, ServiceUnavailableError],
})
.annotateMerge(locationQueryOpenApi)
.annotateMerge(

View File

@@ -3,6 +3,12 @@ import { PluginBoot } from "@opencode-ai/core/plugin/boot"
import { Effect } from "effect"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api"
import { ServiceUnavailableError } from "../../errors"
const catalogUnavailable = new ServiceUnavailableError({
message: "Model catalog is unavailable",
service: "catalog",
})
export const modelHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.model", (handlers) =>
Effect.gen(function* () {
@@ -11,7 +17,7 @@ export const modelHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.model", (
Effect.fn(function* () {
const catalog = yield* Catalog.Service
const pluginBoot = yield* PluginBoot.Service
yield* pluginBoot.wait()
yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable)))
return yield* catalog.model.available()
}),
)

View File

@@ -3,7 +3,12 @@ import { PluginBoot } from "@opencode-ai/core/plugin/boot"
import { Effect } from "effect"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api"
import { notFound } from "../../errors"
import { ProviderNotFoundError, ServiceUnavailableError } from "../../errors"
const catalogUnavailable = new ServiceUnavailableError({
message: "Provider catalog is unavailable",
service: "catalog",
})
export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provider", (handlers) =>
Effect.gen(function* () {
@@ -13,7 +18,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provid
Effect.fn(function* () {
const catalog = yield* Catalog.Service
const pluginBoot = yield* PluginBoot.Service
yield* pluginBoot.wait()
yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable)))
return yield* catalog.provider.available()
}),
)
@@ -22,10 +27,21 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provid
Effect.fn(function* (ctx) {
const catalog = yield* Catalog.Service
const pluginBoot = yield* PluginBoot.Service
yield* pluginBoot.wait()
yield* pluginBoot.wait().pipe(Effect.catchDefect(() => Effect.fail(catalogUnavailable)))
return yield* catalog.provider
.get(ctx.params.providerID)
.pipe(Effect.catchTag("CatalogV2.ProviderNotFound", () => Effect.fail(notFound("Provider not found"))))
.pipe(
Effect.catchTag(
"CatalogV2.ProviderNotFound",
(error) =>
Effect.fail(
new ProviderNotFoundError({
providerID: error.providerID,
message: `Provider not found: ${error.providerID}`,
}),
),
),
)
}),
)
}),