Project Files
src / helpers / pluginMeta.ts
import fs from "node:fs";
import path from "node:path";
export type PluginMeta = {
version: string;
owner: string;
name: string;
revisionsUrl: string;
pluginIdentifier: string;
};
function getModuleDir(): string {
// This plugin ultimately bundles to CommonJS (dist/index.js), where __dirname
// is stable and points at the dist folder. From there we can walk upwards.
try {
// eslint-disable-next-line no-undef
return __dirname;
} catch {
return process.cwd();
}
}
function readJsonFile<T = any>(filePath: string): T | null {
try {
const raw = fs.readFileSync(filePath, "utf8");
return JSON.parse(raw) as T;
} catch {
return null;
}
}
function findUpwards(startDir: string, fileName: string, maxDepth = 8): string | null {
let dir = startDir;
for (let i = 0; i < maxDepth; i++) {
const candidate = path.join(dir, fileName);
if (fs.existsSync(candidate)) return candidate;
const parent = path.dirname(dir);
if (parent === dir) break;
dir = parent;
}
return null;
}
export function getPluginMeta(): PluginMeta {
const moduleDir = getModuleDir();
const packageJsonPath = findUpwards(moduleDir, "package.json");
const manifestJsonPath = findUpwards(moduleDir, "manifest.json");
const packageJson = packageJsonPath ? readJsonFile<any>(packageJsonPath) : null;
const manifestJson = manifestJsonPath ? readJsonFile<any>(manifestJsonPath) : null;
const version = String((packageJson as any)?.version || "unknown");
const owner = String((manifestJson as any)?.owner || "").trim();
const name = String((manifestJson as any)?.name || "").trim();
// We intentionally reference the public docs repository changelog instead of the LM Studio Hub
// revisions endpoint, as the hub page may not reliably expose the expected revision info.
// Convention: <owner>/<pluginName>-docs, file: /docs/CHANGELOG.md on branch main.
const revisionsUrl =
owner && name
? `https://raw.githubusercontent.com/${owner}/${name}-docs/main/docs/CHANGELOG.md`
: "https://raw.githubusercontent.com";
const pluginIdentifier = owner && name ? `${owner}/${name}` : name ? name : "unknown";
return { version, owner, name, revisionsUrl, pluginIdentifier };
}
export function formatToolMetaBlock(meta: PluginMeta = getPluginMeta()): string {
// UI currently does not allow clicking links in tool descriptions.
// Keep this block minimal until we decide on a better UX.
return `Plugin-Identifier: ${meta.pluginIdentifier}\nPlugin version: ${meta.version}`;
}