refactor(core): move models.dev into core (#27347)

This commit is contained in:
Dax 2026-05-13 20:58:24 -04:00 committed by GitHub
commit 16c457e712
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 345 additions and 153 deletions

View file

@ -5,6 +5,7 @@ import { produce, type Draft } from "immer"
import { ModelV2 } from "./model" import { ModelV2 } from "./model"
import { PluginV2 } from "./plugin" import { PluginV2 } from "./plugin"
import { ProviderV2 } from "./provider" import { ProviderV2 } from "./provider"
import { Instance } from "./instance"
type ProviderRecord = { type ProviderRecord = {
provider: ProviderV2.Info provider: ProviderV2.Info
@ -56,6 +57,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/v2
export const layer = Layer.effect( export const layer = Layer.effect(
Service, Service,
Effect.gen(function* () { Effect.gen(function* () {
yield* Instance.Service
let records = HashMap.empty<ProviderV2.ID, ProviderRecord>() let records = HashMap.empty<ProviderV2.ID, ProviderRecord>()
let defaultModel: { providerID: ProviderV2.ID; modelID: ModelV2.ID } | undefined let defaultModel: { providerID: ProviderV2.ID; modelID: ModelV2.ID } | undefined
const plugin = yield* PluginV2.Service const plugin = yield* PluginV2.Service

View file

@ -0,0 +1,12 @@
import { Layer, LayerMap } from "effect"
import { Instance } from "./instance"
import { Catalog } from "./catalog"
import { PluginBoot } from "./plugin/boot"
export class InstanceServiceMap extends LayerMap.Service<InstanceServiceMap>()("@opencode/example/InstanceServiceMap", {
lookup: (ref: Instance.Ref) => {
const instance = Layer.succeed(Instance.Service, Instance.Service.of(ref))
return Layer.mergeAll(Catalog.defaultLayer, PluginBoot.defaultLayer).pipe(Layer.provide(instance))
},
idleTimeToLive: "5 minutes",
}) {}

View file

@ -0,0 +1,10 @@
import { Context } from "effect"
export * as Instance from "./instance"
export type Ref = {
readonly directory: string
readonly workspaceID?: string
}
export class Service extends Context.Service<Service, Ref>()("@opencode/Instance") {}

View file

@ -0,0 +1,2 @@
// Auto-generated by build.ts - do not edit
export declare const snapshot: Record<string, unknown>

File diff suppressed because one or more lines are too long

View file

@ -1,15 +1,17 @@
import { Global } from "@opencode-ai/core/global"
import path from "path" import path from "path"
import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effect" import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effect"
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http" import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"
import { Installation } from "../installation" import { Global } from "./global"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "./flag/flag"
import { Flock } from "@opencode-ai/core/util/flock" import { Flock } from "./util/flock"
import { Hash } from "@opencode-ai/core/util/hash" import { Hash } from "./util/hash"
import { AppFileSystem } from "@opencode-ai/core/filesystem" import { AppFileSystem } from "./filesystem"
import { withTransientReadRetry } from "@/util/effect-http-client" import { InstallationChannel, InstallationVersion } from "./installation/version"
import { CatalogModelStatus } from "./model-status"
import { RuntimeFlags } from "@/effect/runtime-flags" export const CatalogModelStatus = Schema.Literals(["alpha", "beta", "deprecated"])
export type CatalogModelStatus = typeof CatalogModelStatus.Type
const USER_AGENT = `opencode/${InstallationChannel}/${InstallationVersion}/${Flag.OPENCODE_CLIENT}`
const CostTier = Schema.Struct({ const CostTier = Schema.Struct({
input: Schema.Finite, input: Schema.Finite,
@ -110,14 +112,21 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/ModelsDev") {} export class Service extends Context.Service<Service, Interface>()("@opencode/ModelsDev") {}
type Requirements = AppFileSystem.Service | HttpClient.HttpClient | RuntimeFlags.Service type Requirements = AppFileSystem.Service | HttpClient.HttpClient
export const layer: Layer.Layer<Service, never, Requirements> = Layer.effect( export const layer: Layer.Layer<Service, never, Requirements> = Layer.effect(
Service, Service,
Effect.gen(function* () { Effect.gen(function* () {
const fs = yield* AppFileSystem.Service const fs = yield* AppFileSystem.Service
const http = HttpClient.filterStatusOk(withTransientReadRetry(yield* HttpClient.HttpClient)) const http = HttpClient.filterStatusOk(
const flags = yield* RuntimeFlags.Service (yield* HttpClient.HttpClient).pipe(
HttpClient.retryTransient({
retryOn: "errors-and-responses",
times: 2,
schedule: Schedule.exponential(200).pipe(Schedule.jittered),
}),
),
)
const source = Flag.OPENCODE_MODELS_URL || "https://models.dev" const source = Flag.OPENCODE_MODELS_URL || "https://models.dev"
const filepath = path.join( const filepath = path.join(
@ -136,7 +145,7 @@ export const layer: Layer.Layer<Service, never, Requirements> = Layer.effect(
const fetchApi = Effect.fn("ModelsDev.fetchApi")(function* () { const fetchApi = Effect.fn("ModelsDev.fetchApi")(function* () {
return yield* HttpClientRequest.get(`${source}/api.json`).pipe( return yield* HttpClientRequest.get(`${source}/api.json`).pipe(
HttpClientRequest.setHeader("User-Agent", Installation.userAgent(flags.client)), HttpClientRequest.setHeader("User-Agent", USER_AGENT),
http.execute, http.execute,
Effect.flatMap((res) => res.text), Effect.flatMap((res) => res.text),
Effect.timeout("10 seconds"), Effect.timeout("10 seconds"),
@ -212,7 +221,6 @@ export const layer: Layer.Layer<Service, never, Requirements> = Layer.effect(
export const defaultLayer: Layer.Layer<Service> = layer.pipe( export const defaultLayer: Layer.Layer<Service> = layer.pipe(
Layer.provide(FetchHttpClient.layer), Layer.provide(FetchHttpClient.layer),
Layer.provide(AppFileSystem.defaultLayer), Layer.provide(AppFileSystem.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
) )
export * as ModelsDev from "./models" export * as ModelsDev from "./models"

View file

@ -0,0 +1,66 @@
export * as PluginBoot from "./boot"
import { Context, Deferred, Effect, Layer } from "effect"
import { AuthV2 } from "../auth"
import { Catalog } from "../catalog"
import { Npm } from "../npm"
import { PluginV2 } from "../plugin"
import { AuthPlugin } from "./auth"
import { EnvPlugin } from "./env"
import { ModelsDevPlugin } from "./models-dev"
import { ProviderPlugins } from "./provider"
type Plugin = {
id: PluginV2.ID
effect: Effect.Effect<PluginV2.HookFunctions | void, never, Catalog.Service | AuthV2.Service | Npm.Service>
}
export interface Interface {
readonly wait: () => Effect.Effect<void>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/PluginBoot") {}
export const layer: Layer.Layer<Service, never, Catalog.Service | PluginV2.Service | AuthV2.Service | Npm.Service> = Layer.effect(
Service,
Effect.gen(function* () {
const catalog = yield* Catalog.Service
const plugin = yield* PluginV2.Service
const auth = yield* AuthV2.Service
const npm = yield* Npm.Service
const done = yield* Deferred.make<void>()
const add = Effect.fn("PluginBoot.add")(function* (input: Plugin) {
yield* plugin.add({
id: input.id,
effect: input.effect.pipe(
Effect.provideService(Catalog.Service, catalog),
Effect.provideService(AuthV2.Service, auth),
Effect.provideService(Npm.Service, npm),
),
})
})
const boot = Effect.gen(function* () {
yield* add(EnvPlugin)
yield* add(AuthPlugin)
for (const item of ProviderPlugins) {
yield* add(item)
}
yield* add(ModelsDevPlugin)
}).pipe(Effect.withSpan("PluginBoot.boot"))
yield* boot.pipe(Effect.exit, Effect.flatMap((exit) => Deferred.done(done, exit)), Effect.forkScoped)
return Service.of({
wait: () => Deferred.await(done),
})
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Catalog.defaultLayer),
Layer.provide(PluginV2.defaultLayer),
Layer.provide(Layer.orDie(AuthV2.defaultLayer)),
Layer.provide(Npm.defaultLayer),
)

View file

@ -1,9 +1,9 @@
import { DateTime, Effect } from "effect" import { DateTime, Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog" import { Catalog } from "../catalog"
import { ModelV2 } from "@opencode-ai/core/model" import { ModelV2 } from "../model"
import { ProviderV2 } from "@opencode-ai/core/provider" import { ModelsDev } from "../models"
import { ModelsDev } from "@/provider/models" import { PluginV2 } from "../plugin"
import { PluginV2 } from "@opencode-ai/core/plugin" import { ProviderV2 } from "../provider"
function released(date: string) { function released(date: string) {
const time = Date.parse(date) const time = Date.parse(date)

View file

@ -1,12 +1,14 @@
import { describe, expect } from "bun:test" import { describe, expect } from "bun:test"
import { DateTime, Effect, Layer, Option } from "effect" import { DateTime, Effect, Layer, Option } from "effect"
import { Catalog } from "@opencode-ai/core/catalog" import { Catalog } from "@opencode-ai/core/catalog"
import { Instance } from "@opencode-ai/core/instance"
import { ModelV2 } from "@opencode-ai/core/model" import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { ProviderV2 } from "@opencode-ai/core/provider" import { ProviderV2 } from "@opencode-ai/core/provider"
import { testEffect } from "../lib/effect" import { testEffect } from "./lib/effect"
const it = testEffect(Catalog.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer))) const instanceLayer = Layer.succeed(Instance.Service, Instance.Service.of({ directory: "test" }))
const it = testEffect(Catalog.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer), Layer.provide(instanceLayer)))
describe("CatalogV2", () => { describe("CatalogV2", () => {
it.effect("normalizes provider baseURL into endpoint url", () => it.effect("normalizes provider baseURL into endpoint url", () =>

View file

@ -4,11 +4,10 @@ import { HttpClient, HttpClientResponse } from "effect/unstable/http"
import { AppFileSystem } from "@opencode-ai/core/filesystem" import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"
import { Global } from "@opencode-ai/core/global" import { Global } from "@opencode-ai/core/global"
import { ModelsDev } from "../../src/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { it } from "../lib/effect" import { it } from "./lib/effect"
import { rm, writeFile, utimes, mkdir } from "fs/promises" import { rm, writeFile, utimes, mkdir } from "fs/promises"
import path from "path" import path from "path"
import { RuntimeFlags } from "@/effect/runtime-flags"
// test/preload.ts pins OPENCODE_MODELS_PATH to a fixture so other tests can // test/preload.ts pins OPENCODE_MODELS_PATH to a fixture so other tests can
// resolve providers without network. These tests need to drive the on-disk // resolve providers without network. These tests need to drive the on-disk
@ -93,7 +92,6 @@ const buildLayer = (state: Ref.Ref<MockState>) =>
Layer.fresh(ModelsDev.layer).pipe( Layer.fresh(ModelsDev.layer).pipe(
Layer.provide(Layer.succeed(HttpClient.HttpClient, makeMockClient(state))), Layer.provide(Layer.succeed(HttpClient.HttpClient, makeMockClient(state))),
Layer.provide(AppFileSystem.defaultLayer), Layer.provide(AppFileSystem.defaultLayer),
Layer.provide(RuntimeFlags.layer({ client: "test-client" })),
) )
const writeCache = (data: object, mtimeMs?: number) => const writeCache = (data: object, mtimeMs?: number) =>
@ -138,14 +136,14 @@ describe("ModelsDev Service", () => {
}), }),
) )
it.live("get() returns {} when disk empty and fetch disabled", () => it.live("get() returns bundled snapshot when disk empty and fetch disabled", () =>
Effect.gen(function* () { Effect.gen(function* () {
const state = yield* Ref.make(initialState) const state = yield* Ref.make(initialState)
const result = yield* provided( const result = yield* provided(
state, state,
ModelsDev.Service.use((s) => s.get()), ModelsDev.Service.use((s) => s.get()),
) )
expect(result).toEqual({}) expect(Object.keys(result).length).toBeGreaterThan(0)
const final = yield* Ref.get(state) const final = yield* Ref.get(state)
expect(final.calls).toEqual([]) expect(final.calls).toEqual([])
}), }),
@ -207,7 +205,7 @@ describe("ModelsDev Service", () => {
const final = yield* Ref.get(state) const final = yield* Ref.get(state)
expect(final.calls.length).toBe(1) expect(final.calls.length).toBe(1)
expect(final.calls[0].url).toContain("/api.json") expect(final.calls[0].url).toContain("/api.json")
expect(final.calls[0].userAgent).toContain("/test-client") expect(final.calls[0].userAgent).toContain("/cli")
}), }),
) )
@ -257,7 +255,7 @@ describe("ModelsDev Service", () => {
}), }),
) )
expect(result).toEqual(fixture) expect(result).toEqual(fixture)
// withTransientReadRetry retries 5xx, so calls may be > 1. // retryTransient retries 5xx, so calls may be > 1.
const final = yield* Ref.get(state) const final = yield* Ref.get(state)
expect(final.calls.length).toBeGreaterThanOrEqual(1) expect(final.calls.length).toBeGreaterThanOrEqual(1)
}), }),

View file

@ -4,7 +4,7 @@ import { AuthV2 } from "@opencode-ai/core/auth"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { AuthPlugin } from "@opencode-ai/core/plugin/auth" import { AuthPlugin } from "@opencode-ai/core/plugin/auth"
import { AzurePlugin } from "@opencode-ai/core/plugin/provider/azure" import { AzurePlugin } from "@opencode-ai/core/plugin/provider/azure"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
import { fakeSelectorSdk, it, model, npmLayer, provider, withEnv } from "./provider-helper" import { fakeSelectorSdk, it, model, npmLayer, provider, withEnv } from "./provider-helper"
const itWithAuth = testEffect(Layer.mergeAll(PluginV2.defaultLayer, AuthV2.defaultLayer, npmLayer)) const itWithAuth = testEffect(Layer.mergeAll(PluginV2.defaultLayer, AuthV2.defaultLayer, npmLayer))

View file

@ -5,7 +5,7 @@ import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { AuthPlugin } from "@opencode-ai/core/plugin/auth" import { AuthPlugin } from "@opencode-ai/core/plugin/auth"
import { CloudflareWorkersAIPlugin } from "@opencode-ai/core/plugin/provider/cloudflare-workers-ai" import { CloudflareWorkersAIPlugin } from "@opencode-ai/core/plugin/provider/cloudflare-workers-ai"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
import { fakeSelectorSdk, it, model, npmLayer, provider, withEnv } from "./provider-helper" import { fakeSelectorSdk, it, model, npmLayer, provider, withEnv } from "./provider-helper"
const itWithAuth = testEffect(Layer.mergeAll(PluginV2.defaultLayer, AuthV2.defaultLayer, npmLayer)) const itWithAuth = testEffect(Layer.mergeAll(PluginV2.defaultLayer, AuthV2.defaultLayer, npmLayer))

View file

@ -3,7 +3,7 @@ import { Effect, Layer } from "effect"
import { AISDK } from "@opencode-ai/core/aisdk" import { AISDK } from "@opencode-ai/core/aisdk"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { DeepInfraPlugin } from "@opencode-ai/core/plugin/provider/deepinfra" import { DeepInfraPlugin } from "@opencode-ai/core/plugin/provider/deepinfra"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
import { it, model } from "./provider-helper" import { it, model } from "./provider-helper"
const itAISDK = testEffect(Layer.provideMerge(AISDK.layer, PluginV2.defaultLayer)) const itAISDK = testEffect(Layer.provideMerge(AISDK.layer, PluginV2.defaultLayer))

View file

@ -9,7 +9,7 @@ import { AISDK } from "@opencode-ai/core/aisdk"
import { ModelV2 } from "@opencode-ai/core/model" import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { DynamicProviderPlugin } from "@opencode-ai/core/plugin/provider/dynamic" import { DynamicProviderPlugin } from "@opencode-ai/core/plugin/provider/dynamic"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
import { fixtureProvider, it, model, npmLayer } from "./provider-helper" import { fixtureProvider, it, model, npmLayer } from "./provider-helper"
const fixtureProviderPath = fileURLToPath(fixtureProvider) const fixtureProviderPath = fileURLToPath(fixtureProvider)

View file

@ -3,7 +3,7 @@ import { Effect } from "effect"
import { ModelV2 } from "@opencode-ai/core/model" import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { GithubCopilotPlugin } from "@opencode-ai/core/plugin/provider/github-copilot" import { GithubCopilotPlugin } from "@opencode-ai/core/plugin/provider/github-copilot"
import { fakeSelectorSdk, it, model } from "../v2/plugin/provider-helper" import { fakeSelectorSdk, it, model } from "./provider-helper"
describe("GithubCopilotPlugin", () => { describe("GithubCopilotPlugin", () => {
it.effect("creates the bundled Copilot SDK for the GitHub Copilot package", () => it.effect("creates the bundled Copilot SDK for the GitHub Copilot package", () =>

View file

@ -4,7 +4,7 @@ import { AuthV2 } from "@opencode-ai/core/auth"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { AuthPlugin } from "@opencode-ai/core/plugin/auth" import { AuthPlugin } from "@opencode-ai/core/plugin/auth"
import { GitLabPlugin } from "@opencode-ai/core/plugin/provider/gitlab" import { GitLabPlugin } from "@opencode-ai/core/plugin/provider/gitlab"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
import { it, model, npmLayer, provider, withEnv } from "./provider-helper" import { it, model, npmLayer, provider, withEnv } from "./provider-helper"
const gitlabSDKOptions: Record<string, unknown>[] = [] const gitlabSDKOptions: Record<string, unknown>[] = []

View file

@ -4,7 +4,7 @@ import { AISDK } from "@opencode-ai/core/aisdk"
import { ModelV2 } from "@opencode-ai/core/model" import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { GooglePlugin } from "@opencode-ai/core/plugin/provider/google" import { GooglePlugin } from "@opencode-ai/core/plugin/provider/google"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
import { it, model } from "./provider-helper" import { it, model } from "./provider-helper"
const itWithAISDK = testEffect(AISDK.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer))) const itWithAISDK = testEffect(AISDK.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer)))

View file

@ -6,7 +6,7 @@ import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { GroqPlugin } from "@opencode-ai/core/plugin/provider/groq" import { GroqPlugin } from "@opencode-ai/core/plugin/provider/groq"
import { it, model } from "./provider-helper" import { it, model } from "./provider-helper"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
const aisdkIt = testEffect(AISDK.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer))) const aisdkIt = testEffect(AISDK.layer.pipe(Layer.provideMerge(PluginV2.defaultLayer)))

View file

@ -5,7 +5,7 @@ import { Effect, Layer, Option } from "effect"
import { ModelV2 } from "@opencode-ai/core/model" import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { ProviderV2 } from "@opencode-ai/core/provider" import { ProviderV2 } from "@opencode-ai/core/provider"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
export const fixtureProvider = new URL("./fixtures/provider-factory.ts", import.meta.url).href export const fixtureProvider = new URL("./fixtures/provider-factory.ts", import.meta.url).href

View file

@ -1,6 +1,7 @@
import { describe, expect } from "bun:test" import { describe, expect } from "bun:test"
import { DateTime, Effect, Option } from "effect" import { DateTime, Effect, Layer, Option } from "effect"
import { Catalog } from "@opencode-ai/core/catalog" import { Catalog } from "@opencode-ai/core/catalog"
import { Instance } from "@opencode-ai/core/instance"
import { ModelV2 } from "@opencode-ai/core/model" import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { OpencodePlugin } from "@opencode-ai/core/plugin/provider/opencode" import { OpencodePlugin } from "@opencode-ai/core/plugin/provider/opencode"
@ -8,6 +9,7 @@ import { ProviderV2 } from "@opencode-ai/core/provider"
import { it, model, provider, withEnv } from "./provider-helper" import { it, model, provider, withEnv } from "./provider-helper"
const cost = (input: number, output = 0) => [{ input, output, cache: { read: 0, write: 0 } }] const cost = (input: number, output = 0) => [{ input, output, cache: { read: 0, write: 0 } }]
const instanceLayer = Layer.succeed(Instance.Service, Instance.Service.of({ directory: "test" }))
describe("OpencodePlugin", () => { describe("OpencodePlugin", () => {
it.effect("uses a public key and cancels paid models without credentials", () => it.effect("uses a public key and cancels paid models without credentials", () =>
@ -190,6 +192,6 @@ describe("OpencodePlugin", () => {
const selected = yield* catalog.model.small(providerID) const selected = yield* catalog.model.small(providerID)
expect(Option.getOrUndefined(selected)?.id).toBe(ModelV2.ID.make("gpt-5-nano")) expect(Option.getOrUndefined(selected)?.id).toBe(ModelV2.ID.make("gpt-5-nano"))
}).pipe(Effect.provide(Catalog.defaultLayer)), }).pipe(Effect.provide(Catalog.defaultLayer.pipe(Layer.provide(instanceLayer)))),
) )
}) })

View file

@ -4,7 +4,7 @@ import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin" import { PluginV2 } from "@opencode-ai/core/plugin"
import { XAIPlugin } from "@opencode-ai/core/plugin/provider/xai" import { XAIPlugin } from "@opencode-ai/core/plugin/provider/xai"
import { ProviderV2 } from "@opencode-ai/core/provider" import { ProviderV2 } from "@opencode-ai/core/provider"
import { testEffect } from "../../lib/effect" import { testEffect } from "../lib/effect"
import { fakeSelectorSdk } from "./provider-helper" import { fakeSelectorSdk } from "./provider-helper"
const it = testEffect(PluginV2.defaultLayer) const it = testEffect(PluginV2.defaultLayer)

View file

@ -13,11 +13,11 @@ const modelsData = process.env.MODELS_DEV_API_JSON
? await Bun.file(process.env.MODELS_DEV_API_JSON).text() ? await Bun.file(process.env.MODELS_DEV_API_JSON).text()
: await fetch(`${modelsUrl}/api.json`).then((x) => x.text()) : await fetch(`${modelsUrl}/api.json`).then((x) => x.text())
await Bun.write( await Bun.write(
path.join(dir, "src/provider/models-snapshot.js"), path.join(dir, "../core/src/models-snapshot.js"),
`// @ts-nocheck\n// Auto-generated by build.ts - do not edit\nexport const snapshot = ${modelsData}\n`, `// @ts-nocheck\n// Auto-generated by build.ts - do not edit\nexport const snapshot = ${modelsData}\n`,
) )
await Bun.write( await Bun.write(
path.join(dir, "src/provider/models-snapshot.d.ts"), path.join(dir, "../core/src/models-snapshot.d.ts"),
`// Auto-generated by build.ts - do not edit\nexport declare const snapshot: Record<string, unknown>\n`, `// Auto-generated by build.ts - do not edit\nexport declare const snapshot: Record<string, unknown>\n`,
) )
console.log("Generated models-snapshot.js") console.log("Generated models-snapshot.js")

View file

@ -1,22 +1,23 @@
import { EOL } from "os" import { EOL } from "os"
import { Effect, Layer, Option } from "effect" import { Effect, Layer, Option } from "effect"
import { Catalog } from "@opencode-ai/core/catalog" import { Catalog } from "@opencode-ai/core/catalog"
import { InstanceServiceMap } from "@opencode-ai/core/instance-layer"
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
import { effectCmd } from "../../effect-cmd" import { effectCmd } from "../../effect-cmd"
import { PluginBoot } from "@/v2/plugin-boot"
const layer = Catalog.defaultLayer.pipe(Layer.provide(PluginBoot.defaultLayer)) const Runtime = Layer.mergeAll(InstanceServiceMap.layer)
export const V2Command = effectCmd({ export const V2Command = effectCmd({
command: "v2", command: "v2",
describe: "debug v2 catalog and built-in plugins", describe: "debug v2 catalog and built-in plugins",
instance: false, instance: false,
handler: Effect.fn("Cli.debug.v2")(function* () { handler: Effect.fn("Cli.debug.v2")(
const result = yield* Effect.gen(function* () { function* () {
yield* PluginBoot.Service.use((service) => service.wait())
const catalog = yield* Catalog.Service const catalog = yield* Catalog.Service
const providers = (yield* catalog.provider.available()).sort((a, b) => a.id.localeCompare(b.id)) const providers = (yield* catalog.provider.available()).sort((a, b) => a.id.localeCompare(b.id))
const all = (yield* catalog.provider.all()).sort((a, b) => a.id.localeCompare(b.id)) const all = (yield* catalog.provider.all()).sort((a, b) => a.id.localeCompare(b.id))
return { const result = {
providers, providers,
default: catalog.model default: catalog.model
.default() .default()
@ -33,8 +34,13 @@ export const V2Command = effectCmd({
), ),
), ),
} }
}).pipe(Effect.provide(layer), Effect.orDie) process.stdout.write(JSON.stringify(result, null, 2) + EOL)
},
process.stdout.write(JSON.stringify(result, null, 2) + EOL) Effect.provide(
}), InstanceServiceMap.get({
directory: process.cwd(),
}),
),
Effect.provide(Runtime),
),
}) })

View file

@ -19,7 +19,7 @@ import type {
import { UI } from "../ui" import { UI } from "../ui"
import { cmd } from "./cmd" import { cmd } from "./cmd"
import { effectCmd } from "../effect-cmd" import { effectCmd } from "../effect-cmd"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { InstanceRef } from "@/effect/instance-ref" import { InstanceRef } from "@/effect/instance-ref"
import { SessionShare } from "@/share/session" import { SessionShare } from "@/share/session"
import { Session } from "@/session/session" import { Session } from "@/session/session"

View file

@ -2,7 +2,7 @@ import { EOL } from "os"
import { Effect } from "effect" import { Effect } from "effect"
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
import { ProviderID } from "../../provider/schema" import { ProviderID } from "../../provider/schema"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { effectCmd, fail } from "../effect-cmd" import { effectCmd, fail } from "../effect-cmd"
import { UI } from "../ui" import { UI } from "../ui"

View file

@ -3,7 +3,7 @@ import { cmd } from "./cmd"
import { CliError, effectCmd, fail } from "../effect-cmd" import { CliError, effectCmd, fail } from "../effect-cmd"
import { UI } from "../ui" import { UI } from "../ui"
import * as Prompt from "../effect/prompt" import * as Prompt from "../effect/prompt"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { map, pipe, sortBy, values } from "remeda" import { map, pipe, sortBy, values } from "remeda"
import path from "path" import path from "path"

View file

@ -14,7 +14,7 @@ import { FileWatcher } from "@/file/watcher"
import { Storage } from "@/storage/storage" import { Storage } from "@/storage/storage"
import { Snapshot } from "@/snapshot" import { Snapshot } from "@/snapshot"
import { Plugin } from "@/plugin" import { Plugin } from "@/plugin"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
import { ProviderAuth } from "@/provider/auth" import { ProviderAuth } from "@/provider/auth"
import { Agent } from "@/agent/agent" import { Agent } from "@/agent/agent"

View file

@ -1,7 +1,6 @@
import { Schema } from "effect" import { Schema } from "effect"
export const CatalogModelStatus = Schema.Literals(["alpha", "beta", "deprecated"]) export { CatalogModelStatus } from "@opencode-ai/core/models"
export type CatalogModelStatus = typeof CatalogModelStatus.Type
export const ModelStatus = Schema.Literals(["alpha", "beta", "deprecated", "active"]) export const ModelStatus = Schema.Literals(["alpha", "beta", "deprecated", "active"])
export type ModelStatus = typeof ModelStatus.Type export type ModelStatus = typeof ModelStatus.Type

View file

@ -8,7 +8,7 @@ import { Npm } from "@opencode-ai/core/npm"
import { Hash } from "@opencode-ai/core/util/hash" import { Hash } from "@opencode-ai/core/util/hash"
import { Plugin } from "../plugin" import { Plugin } from "../plugin"
import { type LanguageModelV3 } from "@ai-sdk/provider" import { type LanguageModelV3 } from "@ai-sdk/provider"
import * as ModelsDev from "./models" import * as ModelsDev from "@opencode-ai/core/models"
import { Auth } from "../auth" import { Auth } from "../auth"
import { Env } from "../env" import { Env } from "../env"
import { InstallationVersion } from "@opencode-ai/core/installation/version" import { InstallationVersion } from "@opencode-ai/core/installation/version"

View file

@ -2,7 +2,7 @@ import type { ModelMessage, ToolResultPart } from "ai"
import { mergeDeep, unique } from "remeda" import { mergeDeep, unique } from "remeda"
import type { JSONSchema7 } from "@ai-sdk/provider" import type { JSONSchema7 } from "@ai-sdk/provider"
import type * as Provider from "./provider" import type * as Provider from "./provider"
import type * as ModelsDev from "./models" import type * as ModelsDev from "@opencode-ai/core/models"
import { iife } from "@/util/iife" import { iife } from "@/util/iife"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"

View file

@ -0,0 +1,59 @@
import { Catalog } from "@opencode-ai/core/catalog"
import { Instance } from "@opencode-ai/core/instance"
import { InstanceServiceMap } from "@opencode-ai/core/instance-layer"
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
import { Effect, Layer, Schema } from "effect"
import { HttpServerRequest } from "effect/unstable/http"
import { HttpApiMiddleware, OpenApi } from "effect/unstable/httpapi"
export const InstanceQuery = Schema.Struct({
instance: Schema.optional(
Schema.Struct({
directory: Schema.optional(Schema.String),
workspace: Schema.optional(Schema.String),
}),
),
}).annotate({ identifier: "V2InstanceQuery" })
export const instanceQueryOpenApi = OpenApi.annotations({
transform: (operation) => {
const parameters = operation.parameters
if (!Array.isArray(parameters)) return operation
return {
...operation,
parameters: parameters.map((parameter) =>
parameter?.name === "instance" && parameter?.in === "query"
? { ...parameter, style: "deepObject", explode: true }
: parameter,
),
}
},
})
export class V2InstanceMiddleware extends HttpApiMiddleware.Service<
V2InstanceMiddleware,
{
provides: Catalog.Service | PluginBoot.Service
}
>()("@opencode/ExperimentalHttpApiV2Instance") {}
function ref(request: HttpServerRequest.HttpServerRequest): Instance.Ref {
const query = new URL(request.url, "http://localhost").searchParams
return {
directory: query.get("instance[directory]") || request.headers["x-opencode-directory"] || process.cwd(),
workspaceID: query.get("instance[workspace]") || request.headers["x-opencode-workspace"],
}
}
export const layer = Layer.effect(
V2InstanceMiddleware,
Effect.gen(function* () {
const instances = yield* InstanceServiceMap
return V2InstanceMiddleware.of((effect) =>
Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest
return yield* effect.pipe(Effect.provide(instances.get(ref(request))))
}),
)
}),
).pipe(Layer.provide(InstanceServiceMap.layer))

View file

@ -2,12 +2,14 @@ import { ModelV2 } from "@opencode-ai/core/model"
import { Schema } from "effect" import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { Authorization } from "../../middleware/authorization" import { Authorization } from "../../middleware/authorization"
import { InstanceQuery, instanceQueryOpenApi, V2InstanceMiddleware } from "./instance"
export const ModelGroup = HttpApiGroup.make("v2.model") export const ModelGroup = HttpApiGroup.make("v2.model")
.add( .add(
HttpApiEndpoint.get("models", "/api/model", { HttpApiEndpoint.get("models", "/api/model", {
query: InstanceQuery,
success: Schema.Array(ModelV2.Info), success: Schema.Array(ModelV2.Info),
}).annotateMerge( }).annotateMerge(instanceQueryOpenApi).annotateMerge(
OpenApi.annotations({ OpenApi.annotations({
identifier: "v2.model.list", identifier: "v2.model.list",
summary: "List v2 models", summary: "List v2 models",
@ -21,4 +23,5 @@ export const ModelGroup = HttpApiGroup.make("v2.model")
description: "Experimental v2 model routes.", description: "Experimental v2 model routes.",
}), }),
) )
.middleware(V2InstanceMiddleware)
.middleware(Authorization) .middleware(Authorization)

View file

@ -3,12 +3,14 @@ import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { ApiNotFoundError } from "../../errors" import { ApiNotFoundError } from "../../errors"
import { Authorization } from "../../middleware/authorization" import { Authorization } from "../../middleware/authorization"
import { InstanceQuery, instanceQueryOpenApi, V2InstanceMiddleware } from "./instance"
export const ProviderGroup = HttpApiGroup.make("v2.provider") export const ProviderGroup = HttpApiGroup.make("v2.provider")
.add( .add(
HttpApiEndpoint.get("providers", "/api/provider", { HttpApiEndpoint.get("providers", "/api/provider", {
query: InstanceQuery,
success: Schema.Array(ProviderV2.Info), success: Schema.Array(ProviderV2.Info),
}).annotateMerge( }).annotateMerge(instanceQueryOpenApi).annotateMerge(
OpenApi.annotations({ OpenApi.annotations({
identifier: "v2.provider.list", identifier: "v2.provider.list",
summary: "List v2 providers", summary: "List v2 providers",
@ -19,9 +21,10 @@ export const ProviderGroup = HttpApiGroup.make("v2.provider")
.add( .add(
HttpApiEndpoint.get("provider", "/api/provider/:providerID", { HttpApiEndpoint.get("provider", "/api/provider/:providerID", {
params: { providerID: ProviderV2.ID }, params: { providerID: ProviderV2.ID },
query: InstanceQuery,
success: ProviderV2.Info, success: ProviderV2.Info,
error: ApiNotFoundError, error: ApiNotFoundError,
}).annotateMerge( }).annotateMerge(instanceQueryOpenApi).annotateMerge(
OpenApi.annotations({ OpenApi.annotations({
identifier: "v2.provider.get", identifier: "v2.provider.get",
summary: "Get v2 provider", summary: "Get v2 provider",
@ -35,4 +38,5 @@ export const ProviderGroup = HttpApiGroup.make("v2.provider")
description: "Experimental v2 provider routes.", description: "Experimental v2 provider routes.",
}), }),
) )
.middleware(V2InstanceMiddleware)
.middleware(Authorization) .middleware(Authorization)

View file

@ -1,6 +1,6 @@
import { ProviderAuth } from "@/provider/auth" import { ProviderAuth } from "@/provider/auth"
import { Config } from "@/config/config" import { Config } from "@/config/config"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
import { ProviderID } from "@/provider/schema" import { ProviderID } from "@/provider/schema"
import { mapValues } from "remeda" import { mapValues } from "remeda"

View file

@ -1,12 +1,12 @@
import { Catalog } from "@opencode-ai/core/catalog"
import { SessionV2 } from "@/v2/session" import { SessionV2 } from "@/v2/session"
import { Layer } from "effect" import { Layer } from "effect"
import { layer as v2InstanceLayer } from "../groups/v2/instance"
import { messageHandlers } from "./v2/message" import { messageHandlers } from "./v2/message"
import { modelHandlers } from "./v2/model" import { modelHandlers } from "./v2/model"
import { providerHandlers } from "./v2/provider" import { providerHandlers } from "./v2/provider"
import { sessionHandlers } from "./v2/session" import { sessionHandlers } from "./v2/session"
export const v2Handlers = Layer.mergeAll(sessionHandlers, messageHandlers, modelHandlers, providerHandlers).pipe( export const v2Handlers = Layer.mergeAll(sessionHandlers, messageHandlers, modelHandlers, providerHandlers).pipe(
Layer.provide(Catalog.defaultLayer), Layer.provide(v2InstanceLayer),
Layer.provide(SessionV2.defaultLayer), Layer.provide(SessionV2.defaultLayer),
) )

View file

@ -1,12 +1,19 @@
import { Catalog } from "@opencode-ai/core/catalog" import { Catalog } from "@opencode-ai/core/catalog"
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
import { Effect } from "effect" import { Effect } from "effect"
import { HttpApiBuilder } from "effect/unstable/httpapi" import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api" import { InstanceHttpApi } from "../../api"
export const modelHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.model", (handlers) => export const modelHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.model", (handlers) =>
Effect.gen(function* () { Effect.gen(function* () {
const catalog = yield* Catalog.Service return handlers.handle(
"models",
return handlers.handle("models", () => catalog.model.available()) Effect.fn(function* () {
const catalog = yield* Catalog.Service
const pluginBoot = yield* PluginBoot.Service
yield* pluginBoot.wait()
return yield* catalog.model.available()
}),
)
}), }),
) )

View file

@ -1,4 +1,5 @@
import { Catalog } from "@opencode-ai/core/catalog" import { Catalog } from "@opencode-ai/core/catalog"
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
import { Effect } from "effect" import { Effect } from "effect"
import { HttpApiBuilder } from "effect/unstable/httpapi" import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api" import { InstanceHttpApi } from "../../api"
@ -6,13 +7,22 @@ import { notFound } from "../../errors"
export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provider", (handlers) => export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.provider", (handlers) =>
Effect.gen(function* () { Effect.gen(function* () {
const catalog = yield* Catalog.Service
return handlers return handlers
.handle("providers", () => catalog.provider.available()) .handle(
"providers",
Effect.fn(function* () {
const catalog = yield* Catalog.Service
const pluginBoot = yield* PluginBoot.Service
yield* pluginBoot.wait()
return yield* catalog.provider.available()
}),
)
.handle( .handle(
"provider", "provider",
Effect.fn(function* (ctx) { Effect.fn(function* (ctx) {
const catalog = yield* Catalog.Service
const pluginBoot = yield* PluginBoot.Service
yield* pluginBoot.wait()
return yield* catalog.provider return yield* catalog.provider
.get(ctx.params.providerID) .get(ctx.params.providerID)
.pipe(Effect.catchTag("CatalogV2.ProviderNotFound", () => Effect.fail(notFound("Provider not found")))) .pipe(Effect.catchTag("CatalogV2.ProviderNotFound", () => Effect.fail(notFound("Provider not found"))))

View file

@ -29,7 +29,7 @@ import { InstanceLayer } from "@/project/instance-layer"
import { Plugin } from "@/plugin" import { Plugin } from "@/plugin"
import { Project } from "@/project/project" import { Project } from "@/project/project"
import { ProviderAuth } from "@/provider/auth" import { ProviderAuth } from "@/provider/auth"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
import { Pty } from "@/pty" import { Pty } from "@/pty"
import { PtyTicket } from "@/pty/ticket" import { PtyTicket } from "@/pty/ticket"

View file

@ -1,50 +0,0 @@
export * as PluginBoot from "./plugin-boot"
import { Npm } from "@opencode-ai/core/npm"
import { Effect, Layer } from "effect"
import { AuthV2 } from "@opencode-ai/core/auth"
import { Catalog } from "@opencode-ai/core/catalog"
import { PluginV2 } from "@opencode-ai/core/plugin"
import { AuthPlugin } from "@opencode-ai/core/plugin/auth"
import { EnvPlugin } from "@opencode-ai/core/plugin/env"
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
import { ModelsDevPlugin } from "./plugin/models-dev"
type Plugin = {
id: PluginV2.ID
effect: Effect.Effect<PluginV2.HookFunctions | void, never, Catalog.Service | AuthV2.Service | Npm.Service>
}
export const layer = Layer.effectDiscard(
Effect.gen(function* () {
const catalog = yield* Catalog.Service
const plugin = yield* PluginV2.Service
const auth = yield* AuthV2.Service
const npm = yield* Npm.Service
const add = Effect.fn("PluginBoot.add")(function* (input: Plugin) {
yield* plugin.add({
id: input.id,
effect: input.effect.pipe(
Effect.provideService(Catalog.Service, catalog),
Effect.provideService(AuthV2.Service, auth),
Effect.provideService(Npm.Service, npm),
),
})
})
yield* add(EnvPlugin)
yield* add(AuthPlugin)
for (const item of ProviderPlugins) {
yield* add(item)
}
yield* add(ModelsDevPlugin)
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Catalog.defaultLayer),
Layer.provide(PluginV2.defaultLayer),
Layer.provide(Layer.orDie(AuthV2.defaultLayer)),
Layer.provide(Npm.defaultLayer),
)

View file

@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test"
import { Schema } from "effect" import { Schema } from "effect"
import { ConfigProvider } from "@/config/provider" import { ConfigProvider } from "@/config/provider"
import { CatalogModelStatus, ModelStatus } from "@/provider/model-status" import { CatalogModelStatus, ModelStatus } from "@/provider/model-status"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
describe("provider model status schemas", () => { describe("provider model status schemas", () => {

View file

@ -7,7 +7,7 @@ import { Global } from "@opencode-ai/core/global"
import { Instance } from "../../src/project/instance" import { Instance } from "../../src/project/instance"
import { WithInstance } from "../../src/project/with-instance" import { WithInstance } from "../../src/project/with-instance"
import { Plugin } from "../../src/plugin/index" import { Plugin } from "../../src/plugin/index"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
import { ProviderID, ModelID } from "../../src/provider/schema" import { ProviderID, ModelID } from "../../src/provider/schema"
import { Filesystem } from "@/util/filesystem" import { Filesystem } from "@/util/filesystem"

View file

@ -9,7 +9,7 @@ import { Instance } from "../../src/project/instance"
import { WithInstance } from "../../src/project/with-instance" import { WithInstance } from "../../src/project/with-instance"
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
import { ProviderTransform } from "@/provider/transform" import { ProviderTransform } from "@/provider/transform"
import { ModelsDev } from "@/provider/models" import { ModelsDev } from "@opencode-ai/core/models"
import { ProviderID, ModelID } from "../../src/provider/schema" import { ProviderID, ModelID } from "../../src/provider/schema"
import { Filesystem } from "@/util/filesystem" import { Filesystem } from "@/util/filesystem"
import { tmpdir } from "../fixture/fixture" import { tmpdir } from "../fixture/fixture"

View file

@ -4385,10 +4385,20 @@ export class Model extends HeyApiClient {
* *
* Retrieve available v2 models ordered by release date. * Retrieve available v2 models ordered by release date.
*/ */
public list<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) { public list<ThrowOnError extends boolean = false>(
parameters?: {
instance?: {
directory?: string
workspace?: string
}
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "instance" }] }])
return (options?.client ?? this.client).get<V2ModelListResponses, unknown, ThrowOnError>({ return (options?.client ?? this.client).get<V2ModelListResponses, unknown, ThrowOnError>({
url: "/api/model", url: "/api/model",
...options, ...options,
...params,
}) })
} }
} }
@ -4399,10 +4409,20 @@ export class Provider2 extends HeyApiClient {
* *
* Retrieve active v2 AI providers so clients can show provider availability and configuration. * Retrieve active v2 AI providers so clients can show provider availability and configuration.
*/ */
public list<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) { public list<ThrowOnError extends boolean = false>(
parameters?: {
instance?: {
directory?: string
workspace?: string
}
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "instance" }] }])
return (options?.client ?? this.client).get<V2ProviderListResponses, unknown, ThrowOnError>({ return (options?.client ?? this.client).get<V2ProviderListResponses, unknown, ThrowOnError>({
url: "/api/provider", url: "/api/provider",
...options, ...options,
...params,
}) })
} }
@ -4414,10 +4434,24 @@ export class Provider2 extends HeyApiClient {
public get<ThrowOnError extends boolean = false>( public get<ThrowOnError extends boolean = false>(
parameters: { parameters: {
providerID: string providerID: string
instance?: {
directory?: string
workspace?: string
}
}, },
options?: Options<never, ThrowOnError>, options?: Options<never, ThrowOnError>,
) { ) {
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "providerID" }] }]) const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "providerID" },
{ in: "query", key: "instance" },
],
},
],
)
return (options?.client ?? this.client).get<V2ProviderGetResponses, V2ProviderGetErrors, ThrowOnError>({ return (options?.client ?? this.client).get<V2ProviderGetResponses, V2ProviderGetErrors, ThrowOnError>({
url: "/api/provider/{providerID}", url: "/api/provider/{providerID}",
...options, ...options,

View file

@ -15,8 +15,6 @@ export type Event =
| EventPermissionReplied | EventPermissionReplied
| EventSessionDiff | EventSessionDiff
| EventSessionError | EventSessionError
| EventInstallationUpdated
| EventInstallationUpdateAvailable
| EventQuestionAsked | EventQuestionAsked
| EventQuestionReplied | EventQuestionReplied
| EventQuestionRejected | EventQuestionRejected
@ -42,6 +40,8 @@ export type Event =
| EventPtyUpdated | EventPtyUpdated
| EventPtyExited | EventPtyExited
| EventPtyDeleted | EventPtyDeleted
| EventInstallationUpdated
| EventInstallationUpdateAvailable
| EventMessageUpdated | EventMessageUpdated
| EventMessageRemoved | EventMessageRemoved
| EventMessagePartUpdated | EventMessagePartUpdated
@ -799,8 +799,6 @@ export type GlobalEvent = {
| EventPermissionReplied | EventPermissionReplied
| EventSessionDiff | EventSessionDiff
| EventSessionError | EventSessionError
| EventInstallationUpdated
| EventInstallationUpdateAvailable
| EventQuestionAsked | EventQuestionAsked
| EventQuestionReplied | EventQuestionReplied
| EventQuestionRejected | EventQuestionRejected
@ -826,6 +824,8 @@ export type GlobalEvent = {
| EventPtyUpdated | EventPtyUpdated
| EventPtyExited | EventPtyExited
| EventPtyDeleted | EventPtyDeleted
| EventInstallationUpdated
| EventInstallationUpdateAvailable
| EventMessageUpdated | EventMessageUpdated
| EventMessageRemoved | EventMessageRemoved
| EventMessagePartUpdated | EventMessagePartUpdated
@ -2496,22 +2496,6 @@ export type EventSessionError = {
} }
} }
export type EventInstallationUpdated = {
id: string
type: "installation.updated"
properties: {
version: string
}
}
export type EventInstallationUpdateAvailable = {
id: string
type: "installation.update-available"
properties: {
version: string
}
}
export type EventQuestionAsked = { export type EventQuestionAsked = {
id: string id: string
type: "question.asked" type: "question.asked"
@ -2681,6 +2665,22 @@ export type EventPtyDeleted = {
} }
} }
export type EventInstallationUpdated = {
id: string
type: "installation.updated"
properties: {
version: string
}
}
export type EventInstallationUpdateAvailable = {
id: string
type: "installation.update-available"
properties: {
version: string
}
}
export type EventMessageUpdated = { export type EventMessageUpdated = {
id: string id: string
type: "message.updated" type: "message.updated"
@ -6673,7 +6673,12 @@ export type V2SessionMessagesResponse2 = V2SessionMessagesResponses[keyof V2Sess
export type V2ModelListData = { export type V2ModelListData = {
body?: never body?: never
path?: never path?: never
query?: never query?: {
instance?: {
directory?: string
workspace?: string
}
}
url: "/api/model" url: "/api/model"
} }
@ -6689,7 +6694,12 @@ export type V2ModelListResponse = V2ModelListResponses[keyof V2ModelListResponse
export type V2ProviderListData = { export type V2ProviderListData = {
body?: never body?: never
path?: never path?: never
query?: never query?: {
instance?: {
directory?: string
workspace?: string
}
}
url: "/api/provider" url: "/api/provider"
} }
@ -6707,7 +6717,12 @@ export type V2ProviderGetData = {
path: { path: {
providerID: string providerID: string
} }
query?: never query?: {
instance?: {
directory?: string
workspace?: string
}
}
url: "/api/provider/{providerID}" url: "/api/provider/{providerID}"
} }