|
|
@@ -12,19 +12,23 @@ const cost = (input: number, output = 0) => [{ input, output, cache: { read: 0,
|
|
|
const locationLayer = Layer.succeed(Location.Service, Location.Service.of({ directory: "test" }))
|
|
|
|
|
|
describe("OpencodePlugin", () => {
|
|
|
- it.effect("uses a public key and cancels paid models without credentials", () =>
|
|
|
+ it.effect("uses a public key and disables paid models without credentials", () =>
|
|
|
withEnv({ OPENCODE_API_KEY: undefined }, () =>
|
|
|
Effect.gen(function* () {
|
|
|
const plugin = yield* PluginV2.Service
|
|
|
+ const catalog = yield* Catalog.Service
|
|
|
yield* plugin.add(OpencodePlugin)
|
|
|
- const updated = yield* plugin.trigger("provider.update", {}, { provider: provider("opencode"), cancel: false })
|
|
|
- const paid = yield* plugin.trigger(
|
|
|
- "model.update",
|
|
|
- {},
|
|
|
- { model: model("opencode", "paid", { cost: cost(1) }), cancel: false },
|
|
|
- )
|
|
|
- expect(updated.provider.options.aisdk.provider.apiKey).toBe("public")
|
|
|
- expect(paid.cancel).toBe(true)
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ const item = provider("opencode")
|
|
|
+ catalog.provider.update(item.id, () => {})
|
|
|
+ const paid = model("opencode", "paid", { cost: cost(1) })
|
|
|
+ catalog.model.update(item.id, paid.id, (draft) => {
|
|
|
+ draft.cost = [...paid.cost]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBe("public")
|
|
|
+ expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(false)
|
|
|
}),
|
|
|
),
|
|
|
)
|
|
|
@@ -33,14 +37,19 @@ describe("OpencodePlugin", () => {
|
|
|
withEnv({ OPENCODE_API_KEY: undefined }, () =>
|
|
|
Effect.gen(function* () {
|
|
|
const plugin = yield* PluginV2.Service
|
|
|
+ const catalog = yield* Catalog.Service
|
|
|
yield* plugin.add(OpencodePlugin)
|
|
|
- yield* plugin.trigger("provider.update", {}, { provider: provider("opencode"), cancel: false })
|
|
|
- const free = yield* plugin.trigger(
|
|
|
- "model.update",
|
|
|
- {},
|
|
|
- { model: model("opencode", "free", { cost: cost(0) }), cancel: false },
|
|
|
- )
|
|
|
- expect(free.cancel).toBe(false)
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ const item = provider("opencode")
|
|
|
+ catalog.provider.update(item.id, () => {})
|
|
|
+ const free = model("opencode", "free", { cost: cost(0) })
|
|
|
+ catalog.model.update(item.id, free.id, (draft) => {
|
|
|
+ draft.cost = [...free.cost]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBe("public")
|
|
|
+ expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("free"))).enabled).toBe(true)
|
|
|
}),
|
|
|
),
|
|
|
)
|
|
|
@@ -49,14 +58,19 @@ describe("OpencodePlugin", () => {
|
|
|
withEnv({ OPENCODE_API_KEY: undefined }, () =>
|
|
|
Effect.gen(function* () {
|
|
|
const plugin = yield* PluginV2.Service
|
|
|
+ const catalog = yield* Catalog.Service
|
|
|
yield* plugin.add(OpencodePlugin)
|
|
|
- yield* plugin.trigger("provider.update", {}, { provider: provider("opencode"), cancel: false })
|
|
|
- const outputOnly = yield* plugin.trigger(
|
|
|
- "model.update",
|
|
|
- {},
|
|
|
- { model: model("opencode", "output-only", { cost: cost(0, 1) }), cancel: false },
|
|
|
- )
|
|
|
- expect(outputOnly.cancel).toBe(false)
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ const item = provider("opencode")
|
|
|
+ catalog.provider.update(item.id, () => {})
|
|
|
+ const outputOnly = model("opencode", "output-only", { cost: cost(0, 1) })
|
|
|
+ catalog.model.update(item.id, outputOnly.id, (draft) => {
|
|
|
+ draft.cost = [...outputOnly.cost]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBe("public")
|
|
|
+ expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("output-only"))).enabled).toBe(true)
|
|
|
}),
|
|
|
),
|
|
|
)
|
|
|
@@ -65,15 +79,19 @@ describe("OpencodePlugin", () => {
|
|
|
withEnv({ OPENCODE_API_KEY: "secret" }, () =>
|
|
|
Effect.gen(function* () {
|
|
|
const plugin = yield* PluginV2.Service
|
|
|
+ const catalog = yield* Catalog.Service
|
|
|
yield* plugin.add(OpencodePlugin)
|
|
|
- const updated = yield* plugin.trigger("provider.update", {}, { provider: provider("opencode"), cancel: false })
|
|
|
- const paid = yield* plugin.trigger(
|
|
|
- "model.update",
|
|
|
- {},
|
|
|
- { model: model("opencode", "paid", { cost: cost(1) }), cancel: false },
|
|
|
- )
|
|
|
- expect(updated.provider.options.aisdk.provider.apiKey).toBeUndefined()
|
|
|
- expect(paid.cancel).toBe(false)
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ const item = provider("opencode")
|
|
|
+ catalog.provider.update(item.id, () => {})
|
|
|
+ const paid = model("opencode", "paid", { cost: cost(1) })
|
|
|
+ catalog.model.update(item.id, paid.id, (draft) => {
|
|
|
+ draft.cost = [...paid.cost]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBeUndefined()
|
|
|
+ expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
|
|
}),
|
|
|
),
|
|
|
)
|
|
|
@@ -82,19 +100,21 @@ describe("OpencodePlugin", () => {
|
|
|
withEnv({ OPENCODE_API_KEY: undefined, CUSTOM_OPENCODE_API_KEY: "secret" }, () =>
|
|
|
Effect.gen(function* () {
|
|
|
const plugin = yield* PluginV2.Service
|
|
|
+ const catalog = yield* Catalog.Service
|
|
|
yield* plugin.add(OpencodePlugin)
|
|
|
- const updated = yield* plugin.trigger(
|
|
|
- "provider.update",
|
|
|
- {},
|
|
|
- { provider: provider("opencode", { env: ["CUSTOM_OPENCODE_API_KEY"] }), cancel: false },
|
|
|
- )
|
|
|
- const paid = yield* plugin.trigger(
|
|
|
- "model.update",
|
|
|
- {},
|
|
|
- { model: model("opencode", "paid", { cost: cost(1) }), cancel: false },
|
|
|
- )
|
|
|
- expect(updated.provider.options.aisdk.provider.apiKey).toBeUndefined()
|
|
|
- expect(paid.cancel).toBe(false)
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ const item = provider("opencode", { env: ["CUSTOM_OPENCODE_API_KEY"] })
|
|
|
+ catalog.provider.update(item.id, (draft) => {
|
|
|
+ draft.env = [...item.env]
|
|
|
+ })
|
|
|
+ const paid = model("opencode", "paid", { cost: cost(1) })
|
|
|
+ catalog.model.update(item.id, paid.id, (draft) => {
|
|
|
+ draft.cost = [...paid.cost]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBeUndefined()
|
|
|
+ expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
|
|
}),
|
|
|
),
|
|
|
)
|
|
|
@@ -103,31 +123,30 @@ describe("OpencodePlugin", () => {
|
|
|
withEnv({ OPENCODE_API_KEY: undefined }, () =>
|
|
|
Effect.gen(function* () {
|
|
|
const plugin = yield* PluginV2.Service
|
|
|
+ const catalog = yield* Catalog.Service
|
|
|
yield* plugin.add(OpencodePlugin)
|
|
|
- const updated = yield* plugin.trigger(
|
|
|
- "provider.update",
|
|
|
- {},
|
|
|
- {
|
|
|
- provider: provider("opencode", {
|
|
|
- options: {
|
|
|
- headers: {},
|
|
|
- body: {},
|
|
|
- aisdk: {
|
|
|
- provider: { apiKey: "configured" },
|
|
|
- request: {},
|
|
|
- },
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ const item = provider("opencode", {
|
|
|
+ options: {
|
|
|
+ headers: {},
|
|
|
+ body: {},
|
|
|
+ aisdk: {
|
|
|
+ provider: { apiKey: "configured" },
|
|
|
+ request: {},
|
|
|
},
|
|
|
- }),
|
|
|
- cancel: false,
|
|
|
- },
|
|
|
- )
|
|
|
- const paid = yield* plugin.trigger(
|
|
|
- "model.update",
|
|
|
- {},
|
|
|
- { model: model("opencode", "paid", { cost: cost(1) }), cancel: false },
|
|
|
- )
|
|
|
- expect(updated.provider.options.aisdk.provider.apiKey).toBe("configured")
|
|
|
- expect(paid.cancel).toBe(false)
|
|
|
+ },
|
|
|
+ })
|
|
|
+ catalog.provider.update(item.id, (draft) => {
|
|
|
+ draft.options = item.options
|
|
|
+ })
|
|
|
+ const paid = model("opencode", "paid", { cost: cost(1) })
|
|
|
+ catalog.model.update(item.id, paid.id, (draft) => {
|
|
|
+ draft.cost = [...paid.cost]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBe("configured")
|
|
|
+ expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
|
|
}),
|
|
|
),
|
|
|
)
|
|
|
@@ -136,19 +155,21 @@ describe("OpencodePlugin", () => {
|
|
|
withEnv({ OPENCODE_API_KEY: undefined }, () =>
|
|
|
Effect.gen(function* () {
|
|
|
const plugin = yield* PluginV2.Service
|
|
|
+ const catalog = yield* Catalog.Service
|
|
|
yield* plugin.add(OpencodePlugin)
|
|
|
- const updated = yield* plugin.trigger(
|
|
|
- "provider.update",
|
|
|
- {},
|
|
|
- { provider: provider("opencode", { enabled: { via: "auth", service: "opencode" } }), cancel: false },
|
|
|
- )
|
|
|
- const paid = yield* plugin.trigger(
|
|
|
- "model.update",
|
|
|
- {},
|
|
|
- { model: model("opencode", "paid", { cost: cost(1) }), cancel: false },
|
|
|
- )
|
|
|
- expect(updated.provider.options.aisdk.provider.apiKey).toBeUndefined()
|
|
|
- expect(paid.cancel).toBe(false)
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ const item = provider("opencode", { enabled: { via: "account", service: "opencode" } })
|
|
|
+ catalog.provider.update(item.id, (draft) => {
|
|
|
+ draft.enabled = item.enabled
|
|
|
+ })
|
|
|
+ const paid = model("opencode", "paid", { cost: cost(1) })
|
|
|
+ catalog.model.update(item.id, paid.id, (draft) => {
|
|
|
+ draft.cost = [...paid.cost]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBeUndefined()
|
|
|
+ expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
|
|
}),
|
|
|
),
|
|
|
)
|
|
|
@@ -157,15 +178,19 @@ describe("OpencodePlugin", () => {
|
|
|
withEnv({ OPENCODE_API_KEY: undefined }, () =>
|
|
|
Effect.gen(function* () {
|
|
|
const plugin = yield* PluginV2.Service
|
|
|
+ const catalog = yield* Catalog.Service
|
|
|
yield* plugin.add(OpencodePlugin)
|
|
|
- const updated = yield* plugin.trigger("provider.update", {}, { provider: provider("openai"), cancel: false })
|
|
|
- const paid = yield* plugin.trigger(
|
|
|
- "model.update",
|
|
|
- {},
|
|
|
- { model: model("openai", "paid", { cost: cost(1) }), cancel: false },
|
|
|
- )
|
|
|
- expect(updated.provider.options.aisdk.provider.apiKey).toBeUndefined()
|
|
|
- expect(paid.cancel).toBe(false)
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ const item = provider("openai")
|
|
|
+ catalog.provider.update(item.id, () => {})
|
|
|
+ const paid = model("openai", "paid", { cost: cost(1) })
|
|
|
+ catalog.model.update(item.id, paid.id, (draft) => {
|
|
|
+ draft.cost = [...paid.cost]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ expect((yield* catalog.provider.get(ProviderV2.ID.openai)).options.aisdk.provider.apiKey).toBeUndefined()
|
|
|
+ expect((yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
|
|
}),
|
|
|
),
|
|
|
)
|
|
|
@@ -175,18 +200,21 @@ describe("OpencodePlugin", () => {
|
|
|
const catalog = yield* Catalog.Service
|
|
|
const providerID = ProviderV2.ID.opencode
|
|
|
|
|
|
- yield* catalog.provider.update(providerID, () => {})
|
|
|
- yield* catalog.model.update(providerID, ModelV2.ID.make("cheap-mini"), (model) => {
|
|
|
- model.capabilities.input = ["text"]
|
|
|
- model.capabilities.output = ["text"]
|
|
|
- model.cost = cost(1, 1)
|
|
|
- model.time.released = DateTime.makeUnsafe(Date.now())
|
|
|
- })
|
|
|
- yield* catalog.model.update(providerID, ModelV2.ID.make("gpt-5-nano"), (model) => {
|
|
|
- model.capabilities.input = ["text"]
|
|
|
- model.capabilities.output = ["text"]
|
|
|
- model.cost = cost(10, 10)
|
|
|
- model.time.released = DateTime.makeUnsafe(Date.now())
|
|
|
+ const load = yield* catalog.loader()
|
|
|
+ yield* load((catalog) => {
|
|
|
+ catalog.provider.update(providerID, () => {})
|
|
|
+ catalog.model.update(providerID, ModelV2.ID.make("cheap-mini"), (model) => {
|
|
|
+ model.capabilities.input = ["text"]
|
|
|
+ model.capabilities.output = ["text"]
|
|
|
+ model.cost = [...cost(1, 1)]
|
|
|
+ model.time.released = DateTime.makeUnsafe(Date.now())
|
|
|
+ })
|
|
|
+ catalog.model.update(providerID, ModelV2.ID.make("gpt-5-nano"), (model) => {
|
|
|
+ model.capabilities.input = ["text"]
|
|
|
+ model.capabilities.output = ["text"]
|
|
|
+ model.cost = [...cost(10, 10)]
|
|
|
+ model.time.released = DateTime.makeUnsafe(Date.now())
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
const selected = yield* catalog.model.small(providerID)
|