openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
124 lines (123 loc) • 5.32 kB
JavaScript
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { d as normalizeTrimmedStringList } from "./string-normalization-WNUDCpXX.js";
import { g as shortenHomePath } from "./utils-CCC-BEJH.js";
import { t as loadPluginManifestRegistry } from "./manifest-registry-BywZIeAq.js";
import { Nn as record, Rn as string, Zn as unknown } from "./schemas-6cH6bZ7o.js";
import { n as safeParseWithSchema, t as safeParseJsonWithSchema } from "./zod-parse-Bip-sZi_.js";
import { t as note } from "./note-BRSJp0UF.js";
import fs from "node:fs";
import path from "node:path";
//#region src/commands/doctor-plugin-manifests.ts
/** Doctor migration for legacy plugin manifest capability keys into contracts.* fields. */
const LEGACY_MANIFEST_CONTRACT_KEYS = [
"speechProviders",
"mediaUnderstandingProviders",
"imageGenerationProviders",
"tools"
];
const JsonRecordSchema = record(string(), unknown());
function readManifestJson(manifestPath) {
try {
return safeParseJsonWithSchema(JsonRecordSchema, fs.readFileSync(manifestPath, "utf-8"));
} catch {
return null;
}
}
function manifestSeenKey(manifestPath) {
try {
return fs.realpathSync.native(manifestPath);
} catch {
return path.resolve(manifestPath);
}
}
function buildLegacyManifestContractMigration(params) {
const nextRaw = { ...params.raw };
const parsedContracts = safeParseWithSchema(JsonRecordSchema, params.raw.contracts);
const nextContracts = parsedContracts ? { ...parsedContracts } : {};
const changeLines = [];
for (const key of LEGACY_MANIFEST_CONTRACT_KEYS) {
if (!(key in params.raw)) continue;
const legacyValues = normalizeTrimmedStringList(params.raw[key]);
const contractValues = normalizeTrimmedStringList(nextContracts[key]);
if (legacyValues.length > 0 && contractValues.length === 0) {
nextContracts[key] = legacyValues;
changeLines.push(`- ${shortenHomePath(params.manifestPath)}: moved ${key} to contracts.${key}`);
} else changeLines.push(`- ${shortenHomePath(params.manifestPath)}: removed legacy ${key} (kept contracts.${key})`);
delete nextRaw[key];
}
if (changeLines.length === 0) return null;
if (Object.keys(nextContracts).length > 0) nextRaw.contracts = nextContracts;
else delete nextRaw.contracts;
const pluginId = normalizeOptionalString(params.raw.id) ?? params.manifestPath;
return {
manifestPath: params.manifestPath,
pluginId,
nextRaw,
changeLines
};
}
/** Collects manifest rewrites needed to move legacy top-level capability keys under contracts. */
function collectLegacyPluginManifestContractMigrations(params) {
const seen = /* @__PURE__ */ new Set();
const migrations = [];
for (const root of params?.manifestRoots ?? []) {
if (!fs.existsSync(root)) continue;
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const manifestPath = path.join(root, entry.name, "openclaw.plugin.json");
const seenKey = manifestSeenKey(manifestPath);
if (seen.has(seenKey)) continue;
seen.add(seenKey);
const raw = readManifestJson(manifestPath);
if (!raw) continue;
const migration = buildLegacyManifestContractMigration({
manifestPath,
raw
});
if (migration) migrations.push(migration);
}
}
for (const plugin of loadPluginManifestRegistry({
...params?.config ? { config: params.config } : {},
...params?.env ? { env: params.env } : {},
...params?.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
}).plugins) {
const seenKey = manifestSeenKey(plugin.manifestPath);
if (seen.has(seenKey)) continue;
seen.add(seenKey);
const raw = readManifestJson(plugin.manifestPath);
if (!raw) continue;
const migration = buildLegacyManifestContractMigration({
manifestPath: plugin.manifestPath,
raw
});
if (migration) migrations.push(migration);
}
return migrations.toSorted((left, right) => left.manifestPath.localeCompare(right.manifestPath));
}
/** Prompts and rewrites legacy plugin manifest contract fields when doctor repair is enabled. */
async function maybeRepairLegacyPluginManifestContracts(params) {
const migrations = collectLegacyPluginManifestContractMigrations({
...params.config ? { config: params.config } : {},
...params.env ? { env: params.env } : {},
...params.manifestRoots ? { manifestRoots: params.manifestRoots } : {},
...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
});
if (migrations.length === 0) return;
const emitNote = params.note ?? note;
emitNote(["Legacy plugin manifest capability keys detected.", ...migrations.flatMap((migration) => migration.changeLines)].join("\n"), "Plugin manifests");
if (!(params.prompter.shouldRepair || await params.prompter.confirmAutoFix({
message: "Rewrite legacy plugin manifest capability keys into contracts now?",
initialValue: true
}))) return;
const applied = [];
for (const migration of migrations) try {
fs.writeFileSync(migration.manifestPath, `${JSON.stringify(migration.nextRaw, null, 2)}\n`, "utf-8");
applied.push(...migration.changeLines);
} catch (error) {
params.runtime.error(`Failed to rewrite legacy plugin manifest at ${migration.manifestPath}: ${String(error)}`);
}
if (applied.length > 0) emitNote(applied.join("\n"), "Doctor changes");
}
//#endregion
export { maybeRepairLegacyPluginManifestContracts };