| export * as PluginHost from "./host" |
|
|
| import type { PluginContext as Interface } from "@opencode-ai/plugin/v2/effect" |
| import { Effect, Schema } from "effect" |
| import { AgentV2 } from "../agent" |
| import { AISDK } from "../aisdk" |
| import { Catalog } from "../catalog" |
| import { CommandV2 } from "../command" |
| import { Credential } from "../credential" |
| import { Integration } from "../integration" |
| import { ModelV2 } from "../model" |
| import { PluginV2 } from "../plugin" |
| import { ProviderV2 } from "../provider" |
| import { Reference } from "../reference" |
| import type { DeepMutable } from "../schema" |
| import { SkillV2 } from "../skill" |
|
|
| const mutable = <T>(value: T) => value as DeepMutable<T> |
|
|
| export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Interface) { |
| const agents = yield* AgentV2.Service |
| const aisdk = yield* AISDK.Service |
| const catalog = yield* Catalog.Service |
| const commands = yield* CommandV2.Service |
| const integration = yield* Integration.Service |
| const reference = yield* Reference.Service |
| const skill = yield* SkillV2.Service |
|
|
| return { |
| options: {}, |
| agent: { |
| reload: agents.reload, |
| transform: (callback) => |
| agents.transform((draft) => |
| callback({ |
| list: () => mutable(draft.list()), |
| get: (id) => mutable(draft.get(AgentV2.ID.make(id))), |
| default: (id) => draft.default(id === undefined ? undefined : AgentV2.ID.make(id)), |
| update: (id, update) => draft.update(AgentV2.ID.make(id), update), |
| remove: (id) => draft.remove(AgentV2.ID.make(id)), |
| }), |
| ), |
| }, |
| aisdk: { |
| sdk: (callback) => |
| aisdk.hook.sdk((event) => { |
| const output = { |
| model: mutable(event.model), |
| package: event.package, |
| options: event.options, |
| sdk: event.sdk, |
| } |
| const result = callback(output) |
| return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe( |
| Effect.tap(() => Effect.sync(() => (event.sdk = output.sdk))), |
| ) |
| }), |
| language: (callback) => |
| aisdk.hook.language((event) => { |
| const output = { |
| model: mutable(event.model), |
| sdk: event.sdk, |
| options: event.options, |
| language: event.language, |
| } |
| const result = callback(output) |
| return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe( |
| Effect.tap(() => Effect.sync(() => (event.language = output.language))), |
| ) |
| }), |
| }, |
| catalog: { |
| reload: catalog.reload, |
| transform: (callback) => |
| catalog.transform((draft) => |
| callback({ |
| provider: { |
| list: () => mutable(draft.provider.list()), |
| get: (id) => mutable(draft.provider.get(ProviderV2.ID.make(id))), |
| update: (id, update) => draft.provider.update(ProviderV2.ID.make(id), update), |
| remove: (id) => draft.provider.remove(ProviderV2.ID.make(id)), |
| }, |
| model: { |
| get: (providerID, modelID) => |
| mutable(draft.model.get(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID))), |
| update: (providerID, modelID, update) => |
| draft.model.update(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID), update), |
| remove: (providerID, modelID) => |
| draft.model.remove(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID)), |
| default: { |
| get: draft.model.default.get, |
| set: (providerID, modelID) => |
| draft.model.default.set(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID)), |
| }, |
| }, |
| }), |
| ), |
| }, |
| command: { |
| reload: commands.reload, |
| transform: commands.transform, |
| }, |
| integration: { |
| reload: integration.reload, |
| connection: { |
| active: (id) => integration.connection.active(Integration.ID.make(id)), |
| resolve: (connection) => |
| integration.connection.resolve( |
| connection.type === "credential" ? { ...connection, id: Credential.ID.make(connection.id) } : connection, |
| ), |
| }, |
| transform: (callback) => |
| integration.transform((draft) => |
| callback({ |
| list: () => mutable(draft.list()), |
| get: (id) => mutable(draft.get(Integration.ID.make(id))), |
| update: (id, update) => draft.update(Integration.ID.make(id), update), |
| remove: (id) => draft.remove(Integration.ID.make(id)), |
| method: { |
| list: (id) => mutable(draft.method.list(Integration.ID.make(id))), |
| update: (input) => { |
| if ("authorize" in input) { |
| const methodID = Integration.MethodID.make(input.method.id) |
| const refresh = input.refresh |
| draft.method.update({ |
| integrationID: Integration.ID.make(input.integrationID), |
| method: { ...input.method, id: methodID }, |
| authorize: (inputs) => |
| input.authorize(inputs).pipe( |
| Effect.map((authorization) => { |
| if (authorization.mode === "auto") { |
| return { |
| ...authorization, |
| callback: authorization.callback.pipe( |
| Effect.map((credential) => |
| Credential.OAuth.make({ |
| ...credential, |
| methodID: Integration.MethodID.make(credential.methodID), |
| }), |
| ), |
| ), |
| } |
| } |
| return { |
| ...authorization, |
| callback: (code: string) => |
| authorization.callback(code).pipe( |
| Effect.map((credential) => |
| Credential.OAuth.make({ |
| ...credential, |
| methodID: Integration.MethodID.make(credential.methodID), |
| }), |
| ), |
| ), |
| } |
| }), |
| ), |
| ...(refresh |
| ? { |
| refresh: (value: Credential.OAuth) => |
| refresh(value).pipe( |
| Effect.map((next) => |
| Credential.OAuth.make({ |
| ...next, |
| methodID: Integration.MethodID.make(next.methodID), |
| }), |
| ), |
| ), |
| } |
| : {}), |
| ...(input.label ? { label: input.label } : {}), |
| }) |
| return |
| } |
| if (input.method.type === "env") { |
| draft.method.update({ |
| integrationID: Integration.ID.make(input.integrationID), |
| method: { type: "env", names: input.method.names }, |
| }) |
| return |
| } |
| draft.method.update({ |
| integrationID: Integration.ID.make(input.integrationID), |
| method: { type: "key", label: input.method.label }, |
| }) |
| }, |
| remove: (id, method) => |
| draft.method.remove(Integration.ID.make(id), Schema.decodeUnknownSync(Integration.Method)(method)), |
| }, |
| }), |
| ), |
| }, |
| plugin: { |
| add: (input) => plugin.add(PluginV2.ID.make(input.id), input.effect), |
| remove: (id) => plugin.remove(PluginV2.ID.make(id)), |
| }, |
| reference: { |
| reload: reference.reload, |
| transform: (callback) => |
| reference.transform((draft) => |
| callback({ |
| add: (name, source) => draft.add(name, Schema.decodeUnknownSync(Reference.Source)(source)), |
| remove: draft.remove, |
| list: draft.list, |
| }), |
| ), |
| }, |
| skill: { |
| reload: skill.reload, |
| transform: (callback) => |
| skill.transform((draft) => |
| callback({ |
| source: (source) => draft.source(Schema.decodeUnknownSync(SkillV2.Source)(source)), |
| list: draft.list, |
| }), |
| ), |
| }, |
| } satisfies Interface |
| }) |
|
|