91 lines
3.3 KiB
TypeScript
91 lines
3.3 KiB
TypeScript
export * as ConfigExternalPlugin from "./external"
|
|
|
|
import type { Plugin as EffectPlugin } from "@opencode-ai/plugin/v2/effect"
|
|
import type { Plugin as PromisePlugin } from "@opencode-ai/plugin/v2/promise"
|
|
import { Effect, Schema } from "effect"
|
|
import path from "path"
|
|
import { fileURLToPath, pathToFileURL } from "url"
|
|
import { Config } from "../../config"
|
|
import { FSUtil } from "../../fs-util"
|
|
import { Location } from "../../location"
|
|
import { Npm } from "../../npm"
|
|
import { define } from "../../plugin/internal"
|
|
import { PluginPromise } from "../../plugin/promise"
|
|
|
|
const PluginModule = Schema.Struct({
|
|
default: Schema.Union([
|
|
Schema.Struct({
|
|
id: Schema.String,
|
|
effect: Schema.declare<EffectPlugin["effect"]>(
|
|
(input): input is EffectPlugin["effect"] => typeof input === "function",
|
|
),
|
|
}),
|
|
Schema.Struct({
|
|
id: Schema.String,
|
|
setup: Schema.declare<PromisePlugin["setup"]>(
|
|
(input): input is PromisePlugin["setup"] => typeof input === "function",
|
|
),
|
|
}),
|
|
]),
|
|
})
|
|
|
|
export const Plugin = define({
|
|
id: "config-plugin",
|
|
effect: Effect.fn(function* (ctx) {
|
|
const config = yield* Config.Service
|
|
const fs = yield* FSUtil.Service
|
|
const location = yield* Location.Service
|
|
const npm = yield* Npm.Service
|
|
yield* Effect.gen(function* () {
|
|
const configured: { package: string; options?: Record<string, any> }[] = []
|
|
|
|
for (const entry of yield* config.entries()) {
|
|
if (entry.type === "document") {
|
|
const directory = entry.path ? path.dirname(entry.path) : location.directory
|
|
for (const item of entry.info.plugins ?? []) {
|
|
const ref = typeof item === "string" ? { package: item } : item
|
|
const packageName = (() => {
|
|
if (ref.package.startsWith("file://")) return fileURLToPath(ref.package)
|
|
if (ref.package.startsWith("./") || ref.package.startsWith("../")) {
|
|
return path.resolve(directory, ref.package)
|
|
}
|
|
return ref.package
|
|
})()
|
|
configured.push({ package: packageName, options: ref.options })
|
|
}
|
|
}
|
|
|
|
if (entry.type === "directory") {
|
|
const files = yield* fs
|
|
.glob("{plugin,plugins}/*.{ts,js}", {
|
|
cwd: entry.path,
|
|
absolute: true,
|
|
include: "file",
|
|
dot: true,
|
|
symlink: true,
|
|
})
|
|
.pipe(Effect.orElseSucceed(() => []))
|
|
files.sort()
|
|
for (const file of files) configured.push({ package: file })
|
|
}
|
|
}
|
|
|
|
for (const ref of configured) {
|
|
yield* Effect.gen(function* () {
|
|
const entrypoint = path.isAbsolute(ref.package)
|
|
? pathToFileURL(ref.package).href
|
|
: (yield* npm.add(ref.package)).entrypoint
|
|
if (!entrypoint) return
|
|
|
|
const mod = yield* Effect.promise(() => import(entrypoint))
|
|
const value = (yield* Schema.decodeUnknownEffect(PluginModule)(mod)).default
|
|
const plugin = "effect" in value ? value : PluginPromise.fromPromise(value)
|
|
yield* ctx.plugin.add({
|
|
id: plugin.id,
|
|
effect: (host) => plugin.effect({ ...host, options: ref.options ?? {} }),
|
|
})
|
|
}).pipe(Effect.ignoreCause)
|
|
}
|
|
}).pipe(Effect.forkScoped({ startImmediately: true }))
|
|
}),
|
|
})
|