317 lines
8.5 KiB
JavaScript
317 lines
8.5 KiB
JavaScript
import { m as resolveUserPath } from "./utils-PmTbZoD1.js";
|
|
import { n as discoverOpenClawPlugins, r as loadPluginManifest } from "./manifest-registry-tuAcHxrV.js";
|
|
import { i as resolvePluginInstallDir, n as installPluginFromNpmSpec, t as recordPluginInstall } from "./installs-C5cjVarj.js";
|
|
import fs from "node:fs/promises";
|
|
|
|
//#region src/plugins/update.ts
|
|
async function readInstalledPackageVersion(dir) {
|
|
try {
|
|
const raw = await fs.readFile(`${dir}/package.json`, "utf-8");
|
|
const parsed = JSON.parse(raw);
|
|
return typeof parsed.version === "string" ? parsed.version : void 0;
|
|
} catch {
|
|
return;
|
|
}
|
|
}
|
|
function resolveBundledPluginSources(params) {
|
|
const discovery = discoverOpenClawPlugins({ workspaceDir: params.workspaceDir });
|
|
const bundled = /* @__PURE__ */ new Map();
|
|
for (const candidate of discovery.candidates) {
|
|
if (candidate.origin !== "bundled") continue;
|
|
const manifest = loadPluginManifest(candidate.rootDir);
|
|
if (!manifest.ok) continue;
|
|
const pluginId = manifest.manifest.id;
|
|
if (bundled.has(pluginId)) continue;
|
|
const npmSpec = candidate.packageManifest?.install?.npmSpec?.trim() || candidate.packageName?.trim() || void 0;
|
|
bundled.set(pluginId, {
|
|
pluginId,
|
|
localPath: candidate.rootDir,
|
|
npmSpec
|
|
});
|
|
}
|
|
return bundled;
|
|
}
|
|
function pathsEqual(left, right) {
|
|
if (!left || !right) return false;
|
|
return resolveUserPath(left) === resolveUserPath(right);
|
|
}
|
|
function buildLoadPathHelpers(existing) {
|
|
let paths = [...existing];
|
|
const resolveSet = () => new Set(paths.map((entry) => resolveUserPath(entry)));
|
|
let resolved = resolveSet();
|
|
let changed = false;
|
|
const addPath = (value) => {
|
|
const normalized = resolveUserPath(value);
|
|
if (resolved.has(normalized)) return;
|
|
paths.push(value);
|
|
resolved.add(normalized);
|
|
changed = true;
|
|
};
|
|
const removePath = (value) => {
|
|
const normalized = resolveUserPath(value);
|
|
if (!resolved.has(normalized)) return;
|
|
paths = paths.filter((entry) => resolveUserPath(entry) !== normalized);
|
|
resolved = resolveSet();
|
|
changed = true;
|
|
};
|
|
return {
|
|
addPath,
|
|
removePath,
|
|
get changed() {
|
|
return changed;
|
|
},
|
|
get paths() {
|
|
return paths;
|
|
}
|
|
};
|
|
}
|
|
async function updateNpmInstalledPlugins(params) {
|
|
const logger = params.logger ?? {};
|
|
const installs = params.config.plugins?.installs ?? {};
|
|
const targets = params.pluginIds?.length ? params.pluginIds : Object.keys(installs);
|
|
const outcomes = [];
|
|
let next = params.config;
|
|
let changed = false;
|
|
for (const pluginId of targets) {
|
|
if (params.skipIds?.has(pluginId)) {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "skipped",
|
|
message: `Skipping "${pluginId}" (already updated).`
|
|
});
|
|
continue;
|
|
}
|
|
const record = installs[pluginId];
|
|
if (!record) {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "skipped",
|
|
message: `No install record for "${pluginId}".`
|
|
});
|
|
continue;
|
|
}
|
|
if (record.source !== "npm") {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "skipped",
|
|
message: `Skipping "${pluginId}" (source: ${record.source}).`
|
|
});
|
|
continue;
|
|
}
|
|
if (!record.spec) {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "skipped",
|
|
message: `Skipping "${pluginId}" (missing npm spec).`
|
|
});
|
|
continue;
|
|
}
|
|
let installPath;
|
|
try {
|
|
installPath = record.installPath ?? resolvePluginInstallDir(pluginId);
|
|
} catch (err) {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "error",
|
|
message: `Invalid install path for "${pluginId}": ${String(err)}`
|
|
});
|
|
continue;
|
|
}
|
|
const currentVersion = await readInstalledPackageVersion(installPath);
|
|
if (params.dryRun) {
|
|
let probe;
|
|
try {
|
|
probe = await installPluginFromNpmSpec({
|
|
spec: record.spec,
|
|
mode: "update",
|
|
dryRun: true,
|
|
expectedPluginId: pluginId,
|
|
logger
|
|
});
|
|
} catch (err) {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "error",
|
|
message: `Failed to check ${pluginId}: ${String(err)}`
|
|
});
|
|
continue;
|
|
}
|
|
if (!probe.ok) {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "error",
|
|
message: `Failed to check ${pluginId}: ${probe.error}`
|
|
});
|
|
continue;
|
|
}
|
|
const nextVersion = probe.version ?? "unknown";
|
|
const currentLabel = currentVersion ?? "unknown";
|
|
if (currentVersion && probe.version && currentVersion === probe.version) outcomes.push({
|
|
pluginId,
|
|
status: "unchanged",
|
|
currentVersion: currentVersion ?? void 0,
|
|
nextVersion: probe.version ?? void 0,
|
|
message: `${pluginId} is up to date (${currentLabel}).`
|
|
});
|
|
else outcomes.push({
|
|
pluginId,
|
|
status: "updated",
|
|
currentVersion: currentVersion ?? void 0,
|
|
nextVersion: probe.version ?? void 0,
|
|
message: `Would update ${pluginId}: ${currentLabel} -> ${nextVersion}.`
|
|
});
|
|
continue;
|
|
}
|
|
let result;
|
|
try {
|
|
result = await installPluginFromNpmSpec({
|
|
spec: record.spec,
|
|
mode: "update",
|
|
expectedPluginId: pluginId,
|
|
logger
|
|
});
|
|
} catch (err) {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "error",
|
|
message: `Failed to update ${pluginId}: ${String(err)}`
|
|
});
|
|
continue;
|
|
}
|
|
if (!result.ok) {
|
|
outcomes.push({
|
|
pluginId,
|
|
status: "error",
|
|
message: `Failed to update ${pluginId}: ${result.error}`
|
|
});
|
|
continue;
|
|
}
|
|
const nextVersion = result.version ?? await readInstalledPackageVersion(result.targetDir);
|
|
next = recordPluginInstall(next, {
|
|
pluginId,
|
|
source: "npm",
|
|
spec: record.spec,
|
|
installPath: result.targetDir,
|
|
version: nextVersion
|
|
});
|
|
changed = true;
|
|
const currentLabel = currentVersion ?? "unknown";
|
|
const nextLabel = nextVersion ?? "unknown";
|
|
if (currentVersion && nextVersion && currentVersion === nextVersion) outcomes.push({
|
|
pluginId,
|
|
status: "unchanged",
|
|
currentVersion: currentVersion ?? void 0,
|
|
nextVersion: nextVersion ?? void 0,
|
|
message: `${pluginId} already at ${currentLabel}.`
|
|
});
|
|
else outcomes.push({
|
|
pluginId,
|
|
status: "updated",
|
|
currentVersion: currentVersion ?? void 0,
|
|
nextVersion: nextVersion ?? void 0,
|
|
message: `Updated ${pluginId}: ${currentLabel} -> ${nextLabel}.`
|
|
});
|
|
}
|
|
return {
|
|
config: next,
|
|
changed,
|
|
outcomes
|
|
};
|
|
}
|
|
async function syncPluginsForUpdateChannel(params) {
|
|
const summary = {
|
|
switchedToBundled: [],
|
|
switchedToNpm: [],
|
|
warnings: [],
|
|
errors: []
|
|
};
|
|
const bundled = resolveBundledPluginSources({ workspaceDir: params.workspaceDir });
|
|
if (bundled.size === 0) return {
|
|
config: params.config,
|
|
changed: false,
|
|
summary
|
|
};
|
|
let next = params.config;
|
|
const loadHelpers = buildLoadPathHelpers(next.plugins?.load?.paths ?? []);
|
|
const installs = next.plugins?.installs ?? {};
|
|
let changed = false;
|
|
if (params.channel === "dev") for (const [pluginId, record] of Object.entries(installs)) {
|
|
const bundledInfo = bundled.get(pluginId);
|
|
if (!bundledInfo) continue;
|
|
loadHelpers.addPath(bundledInfo.localPath);
|
|
if (record.source === "path" && pathsEqual(record.sourcePath, bundledInfo.localPath)) continue;
|
|
next = recordPluginInstall(next, {
|
|
pluginId,
|
|
source: "path",
|
|
sourcePath: bundledInfo.localPath,
|
|
installPath: bundledInfo.localPath,
|
|
spec: record.spec ?? bundledInfo.npmSpec,
|
|
version: record.version
|
|
});
|
|
summary.switchedToBundled.push(pluginId);
|
|
changed = true;
|
|
}
|
|
else for (const [pluginId, record] of Object.entries(installs)) {
|
|
const bundledInfo = bundled.get(pluginId);
|
|
if (!bundledInfo) continue;
|
|
if (record.source === "npm") {
|
|
loadHelpers.removePath(bundledInfo.localPath);
|
|
continue;
|
|
}
|
|
if (record.source !== "path") continue;
|
|
if (!pathsEqual(record.sourcePath, bundledInfo.localPath)) continue;
|
|
const spec = record.spec ?? bundledInfo.npmSpec;
|
|
if (!spec) {
|
|
summary.warnings.push(`Missing npm spec for ${pluginId}; keeping local path.`);
|
|
continue;
|
|
}
|
|
let result;
|
|
try {
|
|
result = await installPluginFromNpmSpec({
|
|
spec,
|
|
mode: "update",
|
|
expectedPluginId: pluginId,
|
|
logger: params.logger
|
|
});
|
|
} catch (err) {
|
|
summary.errors.push(`Failed to install ${pluginId}: ${String(err)}`);
|
|
continue;
|
|
}
|
|
if (!result.ok) {
|
|
summary.errors.push(`Failed to install ${pluginId}: ${result.error}`);
|
|
continue;
|
|
}
|
|
next = recordPluginInstall(next, {
|
|
pluginId,
|
|
source: "npm",
|
|
spec,
|
|
installPath: result.targetDir,
|
|
version: result.version,
|
|
sourcePath: void 0
|
|
});
|
|
summary.switchedToNpm.push(pluginId);
|
|
changed = true;
|
|
loadHelpers.removePath(bundledInfo.localPath);
|
|
}
|
|
if (loadHelpers.changed) {
|
|
next = {
|
|
...next,
|
|
plugins: {
|
|
...next.plugins,
|
|
load: {
|
|
...next.plugins?.load,
|
|
paths: loadHelpers.paths
|
|
}
|
|
}
|
|
};
|
|
changed = true;
|
|
}
|
|
return {
|
|
config: next,
|
|
changed,
|
|
summary
|
|
};
|
|
}
|
|
|
|
//#endregion
|
|
export { updateNpmInstalledPlugins as n, syncPluginsForUpdateChannel as t }; |