openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
259 lines (258 loc) • 11.1 kB
JavaScript
import { c as resolveOpenClawReleaseCohortVersion, o as parseRegistryNpmSpec } from "./npm-registry-spec-CwucyVOH.mjs";
import { c as normalizeUpdateChannel, d as resolveRegistryUpdateChannel } from "./update-channels-YuXTW0De.mjs";
import { t as fetchNpmPackageTargetStatus } from "./update-check-package-target-R9enUjqC.mjs";
import { l as normalizePluginsConfig, u as resolveEffectiveEnableState } from "./config-state-1t-kMQI9.mjs";
import { n as satisfiesPluginApiRange } from "./package-compat-qQaLgNbY.mjs";
import { t as parseClawHubPluginSpec } from "./clawhub-spec-BL1Xg7rK.mjs";
import { a as resolveTrustedSourceLinkedOfficialClawHubInstall, c as resolveTrustedSourceLinkedOfficialNpmSpec } from "./official-external-install-records-C0WSi4q3.mjs";
import { t as checkMinHostVersion } from "./min-host-version-CLkL1CSt.mjs";
import { r as isPackageVersionDowngrade } from "./package-update-utils-BiLDeudr.mjs";
import { n as fetchClawHubPackageDetail, s as resolveLatestVersionFromPackage } from "./clawhub-packages-8F8NO-Io.mjs";
import { l as resolveClawHubUpdateSpecs } from "./update-source-Dl_gJ-Zp.mjs";
//#region src/plugins/plugin-version-drift.ts
function resolveExactNpmPinPackageName(entry) {
if (entry.source !== "npm" || !entry.spec) return;
const parsed = parseRegistryNpmSpec(entry.spec);
if (parsed?.selectorKind !== "exact-version") return;
return parsed.name;
}
/** Exact npm pins need a registry-confirmed package@version target; id-only updates preserve pins. */
function resolvePluginVersionDriftUpdateCommand(entry) {
if (entry.source === "clawhub" && (entry.targetResolution?.status === "unresolved" || entry.targetResolution?.status === "registry-current")) return;
const exactNpmPackageName = resolveExactNpmPinPackageName(entry);
if (exactNpmPackageName) {
if (entry.targetResolution?.status !== "resolved" || entry.targetResolution.packageName !== exactNpmPackageName || entry.targetResolution.requestedTarget !== resolveOpenClawReleaseCohortVersion(entry.gatewayVersion)) return;
const exactNpmTarget = `${exactNpmPackageName}@${entry.targetResolution.version}`;
if (parseRegistryNpmSpec(exactNpmTarget)?.selectorKind === "exact-version") return `openclaw plugins update ${exactNpmTarget}`;
return;
}
return `openclaw plugins update ${entry.pluginId}`;
}
/**
* Reports the registry version an install already holds when ClawHub publishes nothing
* newer, together with the Gateway version the diagnostic asked for.
*/
function resolvePluginVersionDriftRegistryLag(entry) {
const targetResolution = entry.targetResolution;
return targetResolution?.status === "registry-current" ? {
registryVersion: targetResolution.version,
expectedVersion: targetResolution.requestedTarget
} : void 0;
}
/**
* ClawHub publishes plugins on its own release train, so its latest version can sit
* below the running OpenClaw version. Resolve the upgrade target from ClawHub instead
* of assuming the host version is available there.
*/
async function fetchClawHubLatestVersion(packageName, gatewayVersion, baseUrl) {
try {
const detail = await fetchClawHubPackageDetail({
name: packageName,
baseUrl
});
const compatibility = detail.package?.compatibility;
if (!satisfiesPluginApiRange(gatewayVersion, compatibility?.pluginApiRange)) return {
version: null,
error: `ClawHub ${packageName} requires plugin API ${compatibility?.pluginApiRange}, but the target Gateway is ${gatewayVersion}`
};
if (compatibility?.minGatewayVersion && !checkMinHostVersion({
currentVersion: gatewayVersion,
minHostVersion: compatibility.minGatewayVersion,
allowLegacyBareSemver: true
}).ok) return {
version: null,
error: `ClawHub ${packageName} declares minGatewayVersion ${compatibility.minGatewayVersion}, which the target Gateway ${gatewayVersion} does not satisfy`
};
return { version: resolveLatestVersionFromPackage(detail) };
} catch (err) {
return {
version: null,
error: `ClawHub did not resolve ${packageName}: ${String(err)}`
};
}
}
async function resolveClawHubTarget(entry) {
const packageName = entry.clawhubPackage;
if (!packageName) return;
const requestedTarget = resolveOpenClawReleaseCohortVersion(entry.gatewayVersion);
try {
const { installSpec } = resolveClawHubUpdateSpecs({
record: {
source: "clawhub",
spec: entry.spec,
clawhubPackage: packageName
},
officialSpec: entry.clawhubOfficialSpec,
updateChannel: entry.clawhubUpdateChannel,
officialPackageName: packageName,
coreVersion: entry.gatewayVersion
});
const selectedSpec = installSpec ?? entry.spec ?? `clawhub:${packageName}`;
const parsed = parseClawHubPluginSpec(selectedSpec);
if (!parsed || parsed.version && parsed.version.toLowerCase() !== "latest") return {
status: "unresolved",
packageName,
requestedTarget,
error: `ClawHub latest metadata cannot confirm the selected target ${selectedSpec}`
};
} catch (err) {
return {
status: "unresolved",
packageName,
requestedTarget,
error: String(err)
};
}
const { version: latestVersion, error } = await fetchClawHubLatestVersion(packageName, entry.gatewayVersion, entry.clawhubUrl);
if (!latestVersion) return {
status: "unresolved",
packageName,
requestedTarget,
error: error ?? `ClawHub reported no latest version for ${packageName}`
};
if (latestVersion === entry.installedVersion) return {
status: "registry-current",
packageName,
requestedTarget,
version: latestVersion
};
if (isPackageVersionDowngrade(entry.installedVersion, latestVersion)) return {
status: "unresolved",
packageName,
requestedTarget,
error: `ClawHub latest ${latestVersion} is older than installed ${entry.installedVersion}`
};
return {
status: "resolved",
packageName,
requestedTarget,
version: latestVersion
};
}
async function resolveEntryTarget(entry) {
if (entry.source === "clawhub") {
const targetResolution = await resolveClawHubTarget(entry);
return targetResolution ? {
...entry,
targetResolution
} : entry;
}
const packageName = resolveExactNpmPinPackageName(entry);
if (!packageName) return entry;
const requestedTarget = resolveOpenClawReleaseCohortVersion(entry.gatewayVersion);
const requestedSpec = `${packageName}@${requestedTarget}`;
const result = parseRegistryNpmSpec(requestedSpec)?.selectorKind === "exact-version" ? await fetchNpmPackageTargetStatus({
packageName,
target: requestedTarget
}) : {
version: null,
error: "gateway release cohort is not an exact npm version"
};
const targetResolution = result.version === requestedTarget ? {
status: "resolved",
packageName,
requestedTarget,
version: requestedTarget
} : {
status: "unresolved",
packageName,
requestedTarget,
error: `npm registry did not resolve ${requestedSpec}: ${result.error ?? `returned ${JSON.stringify(result.version)}`}`
};
return {
...entry,
targetResolution
};
}
/** Resolve registry repair targets only for diagnostics that display repair guidance. */
async function resolvePluginVersionDriftTargets(report) {
return {
...report,
drifts: await Promise.all(report.drifts.map(resolveEntryTarget))
};
}
function isPluginEnabled(config, pluginId) {
const normalizedPluginConfig = normalizePluginsConfig(config?.plugins);
return resolveEffectiveEnableState({
id: pluginId,
origin: "global",
config: normalizedPluginConfig,
rootConfig: config
}).enabled;
}
function shouldCompareOfficialInstallToGateway(params) {
const officialNpmSpec = resolveTrustedSourceLinkedOfficialNpmSpec(params);
if (officialNpmSpec) return parseRegistryNpmSpec(officialNpmSpec)?.selectorKind !== "exact-version";
const officialClawHubInstall = resolveTrustedSourceLinkedOfficialClawHubInstall(params);
if (officialClawHubInstall) {
if (officialClawHubInstall.clawhubSpec) return !parseClawHubPluginSpec(officialClawHubInstall.clawhubSpec)?.version;
return parseRegistryNpmSpec(officialClawHubInstall.npmSpec ?? "")?.selectorKind !== "exact-version";
}
return false;
}
function hasOfficialPluginVersionCandidates(params) {
return Object.entries(params.installRecords).some(([pluginId, record]) => Boolean(record) && isPluginEnabled(params.config, pluginId) && shouldCompareOfficialInstallToGateway({
pluginId,
record
}));
}
/** Exact cohort targets shared by automatic updates and Doctor's drift repair. */
function resolveOfficialPluginCohortNpmSpecs(params) {
const version = resolveOpenClawReleaseCohortVersion(params.gatewayVersion);
const specs = {};
for (const [pluginId, record] of Object.entries(params.installRecords)) {
if (record.source !== "npm" || !isPluginEnabled(params.config, pluginId) || !shouldCompareOfficialInstallToGateway({
pluginId,
record
})) continue;
const official = resolveTrustedSourceLinkedOfficialNpmSpec({
pluginId,
record
});
const packageName = official ? parseRegistryNpmSpec(official)?.name : void 0;
if (packageName && parseRegistryNpmSpec(`${packageName}@${version}`)?.selectorKind === "exact-version") specs[pluginId] = `${packageName}@${version}`;
}
return specs;
}
/** Compare active official installs to the host cohort, sorted by plugin id for diagnostics. */
function detectPluginVersionDrift(params) {
const { gatewayVersion, installRecords, config } = params;
const normalizedGateway = resolveOpenClawReleaseCohortVersion(gatewayVersion);
const drifts = [];
for (const [pluginId, record] of Object.entries(installRecords)) {
if (!record || !isPluginEnabled(config, pluginId) || !shouldCompareOfficialInstallToGateway({
pluginId,
record
})) continue;
const installedVersion = record.resolvedVersion ?? record.version;
if (!installedVersion) continue;
if (resolveOpenClawReleaseCohortVersion(installedVersion) === normalizedGateway) continue;
const clawhubPackage = record.source === "clawhub" ? record.clawhubPackage?.trim() ?? parseClawHubPluginSpec(record.spec ?? "")?.name : void 0;
const clawhubOfficialSpec = clawhubPackage ? resolveTrustedSourceLinkedOfficialClawHubInstall({
pluginId,
record
})?.clawhubSpec : void 0;
drifts.push({
pluginId,
installedVersion,
gatewayVersion,
source: record.source,
...record.resolvedName ? { packageName: record.resolvedName } : {},
...record.spec ? { spec: record.spec } : {},
...clawhubPackage ? {
clawhubPackage,
...clawhubOfficialSpec ? { clawhubOfficialSpec } : {},
spec: record.spec ?? record.resolvedSpec ?? `clawhub:${clawhubPackage}`,
clawhubUrl: record.clawhubUrl ?? "https://clawhub.ai",
clawhubUpdateChannel: normalizeUpdateChannel(config?.update?.channel) ?? resolveRegistryUpdateChannel({ currentVersion: gatewayVersion })
} : {}
});
}
drifts.sort((a, b) => a.pluginId.localeCompare(b.pluginId));
return {
gatewayVersion,
drifts
};
}
//#endregion
export { resolvePluginVersionDriftTargets as a, resolvePluginVersionDriftRegistryLag as i, hasOfficialPluginVersionCandidates as n, resolvePluginVersionDriftUpdateCommand as o, resolveOfficialPluginCohortNpmSpecs as r, detectPluginVersionDrift as t };