import type { PilotDeckPluginManifest } from "../protocol/manifest.js";
export function parsePluginManifest(raw: unknown): PilotDeckPluginManifest {
if (!isRecord(raw)) {
throw new Error("Plugin manifest must be an object.");
}
if (typeof raw.name !== "string" || raw.name.trim() === "") {
throw new Error("Plugin manifest must contain a name.");
}
return {
name: raw.name,
version: stringOrUndefined(raw.version),
description: stringOrUndefined(raw.description),
commands: stringOrStringArray(raw.commands),
agents: stringOrStringArray(raw.agents),
skills: stringOrStringArray(raw.skills),
hooks: isRecord(raw.hooks) || typeof raw.hooks === "string" ? raw.hooks : undefined,
mcpServers: isRecord(raw.mcpServers) ? raw.mcpServers : undefined,
lspServers: isRecord(raw.lspServers) ? raw.lspServers : undefined,
outputStyles: stringOrStringArray(raw.outputStyles),
marketplace: isRecord(raw.marketplace) && typeof raw.marketplace.name === "string" && typeof raw.marketplace.plugin === "string"
? {
name: raw.marketplace.name,
plugin: raw.marketplace.plugin,
version: stringOrUndefined(raw.marketplace.version),
source: parseMarketplaceSource(raw.marketplace.source),
url: stringOrUndefined(raw.marketplace.url),
}
: undefined,
mcpb: typeof raw.mcpb === "string" && (raw.mcpb.endsWith(".mcpb") || raw.mcpb.endsWith(".dxt"))
? raw.mcpb
: undefined,
settings: isRecord(raw.settings) ? raw.settings : undefined,
};
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function stringOrUndefined(value: unknown): string | undefined {
return typeof value === "string" ? value : undefined;
}
function stringOrStringArray(value: unknown): string | string[] | undefined {
if (typeof value === "string") {
return value;
}
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
return value;
}
return undefined;
}
function parseMarketplaceSource(value: unknown): "marketplace" | "git" | "zip" | "mcpb" | undefined {
return value === "marketplace" || value === "git" || value === "zip" || value === "mcpb" ? value : undefined;
}