mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-17 21:21:18 -04:00
79 lines
3.2 KiB
TypeScript
79 lines
3.2 KiB
TypeScript
export * as ConfigReferencePlugin from "./reference.js"
|
|
|
|
import { define } from "@opencode-ai/plugin/effect/plugin"
|
|
import { Document } from "@opencode-ai/schema/config"
|
|
import { ConfigReference } from "@opencode-ai/schema/config/reference"
|
|
import path from "path"
|
|
import { Effect, Stream } from "effect"
|
|
import { Config } from "../../config.js"
|
|
import { Reference } from "../../reference.js"
|
|
import { AbsolutePath } from "../../schema.js"
|
|
import { Global } from "@opencode-ai/util/global"
|
|
import { Location } from "../../location.js"
|
|
|
|
export const Plugin = define({
|
|
id: "opencode.config.reference",
|
|
effect: Effect.fn(function* (ctx) {
|
|
const config = yield* Config.Service
|
|
const location = yield* Location.Service
|
|
const global = yield* Global.Service
|
|
const loaded = { entries: yield* config.entries() }
|
|
yield* ctx.reference.transform((draft) => {
|
|
const entries = new Map<string, Reference.Source>()
|
|
for (const doc of loaded.entries.filter((entry): entry is Document => entry.type === "document")) {
|
|
const directory = doc.path ? path.dirname(doc.path) : location.directory
|
|
for (const [name, entry] of Object.entries(doc.info.references ?? {})) {
|
|
if (!validAlias(name)) continue
|
|
const description = typeof entry === "string" ? undefined : entry.description
|
|
const hidden = typeof entry === "string" ? undefined : entry.hidden
|
|
entries.set(
|
|
name,
|
|
local(entry)
|
|
? Reference.LocalSource.make({
|
|
type: "local",
|
|
path: AbsolutePath.make(
|
|
localPath(directory, global.home, typeof entry === "string" ? entry : entry.path),
|
|
),
|
|
...(description === undefined ? {} : { description }),
|
|
...(hidden === undefined ? {} : { hidden }),
|
|
})
|
|
: Reference.GitSource.make({
|
|
type: "git",
|
|
repository: typeof entry === "string" ? entry : entry.repository,
|
|
...(entry.branch === undefined ? {} : { branch: entry.branch }),
|
|
...(description === undefined ? {} : { description }),
|
|
...(hidden === undefined ? {} : { hidden }),
|
|
}),
|
|
)
|
|
}
|
|
}
|
|
for (const [name, source] of entries) draft.add(name, source)
|
|
})
|
|
yield* ctx.event.subscribe().pipe(
|
|
Stream.filter((event) => event.type === "config.updated"),
|
|
Stream.runForEach(() =>
|
|
config.entries().pipe(
|
|
Effect.tap((entries) => Effect.sync(() => (loaded.entries = entries))),
|
|
Effect.andThen(ctx.reference.reload()),
|
|
),
|
|
),
|
|
Effect.forkScoped({ startImmediately: true }),
|
|
)
|
|
}),
|
|
})
|
|
|
|
function validAlias(name: string) {
|
|
return name.length > 0 && !/[\/\s`,]/.test(name)
|
|
}
|
|
|
|
function local(entry: ConfigReference.Entry): entry is string | ConfigReference.Local {
|
|
return typeof entry === "string"
|
|
? entry.startsWith(".") || entry.startsWith("/") || entry.startsWith("~")
|
|
: "path" in entry
|
|
}
|
|
|
|
function localPath(directory: string, home: string, value: string) {
|
|
if (value.startsWith("~/")) return path.join(home, value.slice(2))
|
|
return path.isAbsolute(value) ? value : path.resolve(directory, value)
|
|
}
|