Project Files
src / pluginSettings.ts
import fs from "node:fs";
import { homedir } from "node:os";
import path from "node:path";
import { defaultPluginSettings } from "./config.js";
const GLOBAL_PLUGIN_CONFIGS_PATH = path.join(homedir(), ".lmstudio", ".internal", "global-plugin-configs.json");
const PLUGIN_ID = "ceveyne/find-image";
type PluginSettings = typeof defaultPluginSettings;
type PersistedField = {
key?: unknown;
value?: unknown;
};
function persistedFieldsFromJson(value: unknown): PersistedField[] {
const plugins = (value as { json?: { plugins?: unknown } } | undefined)?.json?.plugins;
if (!Array.isArray(plugins)) return [];
const entry = plugins.find((candidate) => Array.isArray(candidate) && candidate[0] === PLUGIN_ID);
const fields = Array.isArray(entry) ? (entry[1] as { fields?: unknown } | undefined)?.fields : undefined;
return Array.isArray(fields) ? fields as PersistedField[] : [];
}
export function mergePluginSettings(persistedConfig: unknown): PluginSettings {
const overrides: Partial<PluginSettings> = {};
for (const field of persistedFieldsFromJson(persistedConfig)) {
if (typeof field?.key === "string" && Object.prototype.hasOwnProperty.call(defaultPluginSettings, field.key)) {
(overrides as Record<string, unknown>)[field.key] = field.value;
}
}
return { ...defaultPluginSettings, ...overrides };
}
/**
* Returns the same effective global settings as the maintenance scripts: the
* current schema defaults, overlaid with known persisted LM Studio fields.
*/
export function loadPluginSettings(): PluginSettings {
try {
const parsed = JSON.parse(fs.readFileSync(GLOBAL_PLUGIN_CONFIGS_PATH, "utf8"));
return mergePluginSettings(parsed);
} catch (error) {
console.warn(
`[settings] Could not read ${GLOBAL_PLUGIN_CONFIGS_PATH}; using config defaults: ${error instanceof Error ? error.message : String(error)}`,
);
return { ...defaultPluginSettings };
}
}
/**
* Uses LM Studio's SDK value whenever it provides a non-empty string. Reading
* the persisted config is only a recovery path for a missing SDK value.
*/
export function resolvePluginStringSetting(
sdkValue: unknown,
key: Extract<keyof PluginSettings, string>,
loadSettings: () => PluginSettings = loadPluginSettings,
): string {
if (typeof sdkValue === "string" && sdkValue.trim()) return sdkValue;
const fallbackValue = loadSettings()[key];
if (typeof fallbackValue !== "string") {
throw new Error(`Plugin setting ${key} is not a string.`);
}
return fallbackValue;
}