import { Type } from "@sinclair/typebox"; import { readFile, readdir } from "node:fs/promises"; import path from "node:path"; import type { ExtensionAPI } from "../_shared/pi-api.js"; import { errorResult, getProjectRoot, setTextWidget, textResult } from "../_shared/pi-api.js"; import { validateParams } from "../_shared/validation.js"; const LoaderParams = Type.Object({ action: Type.Union([Type.Literal("list"), Type.Literal("preview"), Type.Literal("apply"), Type.Literal("rollback")], { description: "Catalog action" }), id: Type.Optional(Type.String({ description: "Catalog entry id" })), }); interface Manifest { name: string; version: string; description: string; enabled: boolean; reviewedBy: string | null; reviewedAt: string | null; risk: string } export default function dynamicLoader(pi: ExtensionAPI): void { pi.registerTool({ name: "dynamicLoader", description: "Read-only local catalog viewer; apply/rollback fail closed until OMP plugin manager is ported.", parameters: LoaderParams, async execute(_toolCallId, params, _signal, _update, ctx) { const valid = validateParams(LoaderParams, params); if (!valid.ok) return valid.result; return loader(valid.value.action, getProjectRoot(ctx), valid.value.id); }, }); pi.registerCommand("extensions", { description: "Show local audited extension catalog.", handler: async (_args, ctx) => { const result = await loader("list", getProjectRoot(ctx)); setTextWidget(ctx, "extensions", result.content[0]?.type === "text" ? result.content[0].text : ""); }, }); } async function loader(action: string, projectRoot: string, id?: string) { const entries = await listCatalog(projectRoot); if (action === "list") return textResult(entries.map((entry) => `${entry.id}: ${entry.manifest.name} (${entry.manifest.risk})`).join("\n") || "No catalog entries", { entries }); const entry = entries.find((item) => item.id === id); if (!entry) return errorResult(`Unknown catalog entry: ${id ?? ""}`); if (action === "preview") { return textResult(JSON.stringify(entry.manifest, null, 2), { entry, owner: "omp-plugin-manager", ported: false, applyEnabled: false, diff: `Would require OMP plugin manager install/enable flow for ${entry.id}.`, }); } return errorResult( [ `dynamicLoader ${action} is disabled in miloc-pi.`, "OMP PluginManager and MarketplaceManager are the source truth for install, enable, disable, uninstall, lockfile state, project overrides, cache invalidation, and marketplace registry updates.", "This local extension only reads catalog manifests until that OMP behavior is ported here.", ].join("\n"), { owner: "omp-plugin-manager", ported: false, requestedAction: action, id: entry.id }, ); } async function listCatalog(projectRoot: string): Promise> { const root = path.join(projectRoot, "catalog"); const results: Array<{ id: string; manifest: Manifest; sourcePath: string }> = []; try { for (const kind of await readdir(root)) { const kindDir = path.join(root, kind); for (const id of await readdir(kindDir)) { const manifestPath = path.join(kindDir, id, "manifest.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf8")) as Manifest; results.push({ id: `${kind}/${id}`, manifest, sourcePath: path.relative(projectRoot, manifestPath) }); } } } catch {} return results; }