mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 20:35:39 -06:00
* fix(plugins): refresh stale source plugin registry * fix(plugins): port registry snapshot to core manifest loader --------- Co-authored-by: tangtaizong666 <212687958+tangtaizong666@users.noreply.github.com> Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -499,6 +499,49 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
|
||||
expect(whatsappPlugin.origin).toBe("global");
|
||||
});
|
||||
|
||||
it("recovers configured global source plugins missing from a stale persisted registry", () => {
|
||||
const tempRoot = makeTempDir();
|
||||
const stateDir = path.join(tempRoot, "state");
|
||||
const env = {
|
||||
...createHermeticEnv(tempRoot),
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1",
|
||||
OPENCLAW_STATE_DIR: stateDir,
|
||||
};
|
||||
const config = {
|
||||
plugins: {
|
||||
entries: {
|
||||
"memory-demo": { enabled: true },
|
||||
},
|
||||
allow: ["memory-demo"],
|
||||
slots: {
|
||||
memory: "memory-demo",
|
||||
},
|
||||
},
|
||||
};
|
||||
const staleIndex = loadInstalledPluginIndex({
|
||||
config,
|
||||
env,
|
||||
stateDir,
|
||||
installRecords: {},
|
||||
});
|
||||
expect(staleIndex.plugins.map((plugin) => plugin.pluginId)).not.toContain("memory-demo");
|
||||
writePersistedInstalledPluginIndexSync(staleIndex, { stateDir });
|
||||
writePackagePlugin(path.join(stateDir, "extensions", "memory-demo-source"), {
|
||||
pluginId: "memory-demo",
|
||||
});
|
||||
|
||||
const result = loadPluginRegistrySnapshotWithMetadata({
|
||||
config,
|
||||
env,
|
||||
stateDir,
|
||||
});
|
||||
|
||||
expect(result.source).toBe("derived");
|
||||
expectDiagnosticsContainCode(result.diagnostics, "persisted-registry-stale-source");
|
||||
const memoryPlugin = requirePluginRecord(result.snapshot.plugins, "memory-demo");
|
||||
expect(memoryPlugin.origin).toBe("global");
|
||||
});
|
||||
|
||||
it("does not recover retained managed npm generations as install records", async () => {
|
||||
const tempRoot = makeTempDir();
|
||||
const stateDir = path.join(tempRoot, "state");
|
||||
|
||||
@@ -9,7 +9,7 @@ import { buildLegacyBundledRootPath } from "./bundled-load-path-aliases.js";
|
||||
import { listBundledSourceOverlayDirs } from "./bundled-source-overlays.js";
|
||||
import { normalizePluginsConfig } from "./config-state.js";
|
||||
import { getCurrentPluginMetadataSnapshot } from "./current-plugin-metadata-snapshot.js";
|
||||
import type { PluginDiscoveryResult } from "./discovery.js";
|
||||
import { discoverConfiguredPluginLoadPaths, type PluginDiscoveryResult } from "./discovery.js";
|
||||
import { resolvePluginDoctorContractArtifactPath } from "./doctor-contract-artifact.js";
|
||||
import { safeFileSignature, safeHashFile } from "./installed-plugin-index-hash.js";
|
||||
import { hasOptionalMissingPluginManifestFile } from "./installed-plugin-index-manifest.js";
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
type InstalledPluginIndexStoreOptions,
|
||||
} from "./installed-plugin-index-store.js";
|
||||
import {
|
||||
extractPluginInstallRecordsFromInstalledPluginIndex,
|
||||
getInstalledPluginRecord,
|
||||
hasMissingConfigPathActivationMetadata,
|
||||
isInstalledPluginEnabled,
|
||||
@@ -32,10 +33,14 @@ import {
|
||||
type LoadInstalledPluginIndexParams,
|
||||
type RefreshInstalledPluginIndexParams,
|
||||
} from "./installed-plugin-index.js";
|
||||
import type { PluginManifestRegistry } from "./manifest-registry.js";
|
||||
import {
|
||||
loadPluginManifestRegistryCore,
|
||||
type PluginManifestRegistry,
|
||||
} from "./manifest-registry.js";
|
||||
import { getPackageManifestMetadata, type PackageManifest } from "./manifest.js";
|
||||
import { isPathInside, safeRealpathSync } from "./path-safety.js";
|
||||
import type { PluginRegistrySnapshotSource } from "./plugin-registry-snapshot.types.js";
|
||||
import { resolvePluginSourceRoots } from "./roots.js";
|
||||
|
||||
function resolvePluginRegistryContent(
|
||||
index: InstalledPluginIndex,
|
||||
@@ -405,10 +410,61 @@ function requiresDerivedRegistryValidation(
|
||||
) ||
|
||||
hasMismatchedPersistedBundledRoot(index, env) ||
|
||||
hasStalePluginFiles() ||
|
||||
hasRecoveredInstallRecordsMissingFromPersistedIndex(index, params, env)
|
||||
hasRecoveredInstallRecordsMissingFromPersistedIndex(index, params, env) ||
|
||||
hasConfiguredGlobalSourcePluginMissingFromPersistedIndex(params, index, env)
|
||||
);
|
||||
}
|
||||
|
||||
function collectConfiguredPluginIds(config: LoadPluginRegistryParams["config"]): Set<string> {
|
||||
const plugins = normalizePluginsConfig(config?.plugins);
|
||||
const pluginIds = new Set<string>();
|
||||
for (const pluginId of Object.keys(plugins.entries)) {
|
||||
pluginIds.add(pluginId);
|
||||
}
|
||||
for (const pluginId of plugins.allow) {
|
||||
pluginIds.add(pluginId);
|
||||
}
|
||||
for (const pluginId of Object.values(plugins.slots)) {
|
||||
if (typeof pluginId === "string" && pluginId.trim() && pluginId !== "none") {
|
||||
pluginIds.add(pluginId);
|
||||
}
|
||||
}
|
||||
return pluginIds;
|
||||
}
|
||||
|
||||
function hasConfiguredGlobalSourcePluginMissingFromPersistedIndex(
|
||||
params: LoadPluginRegistryParams,
|
||||
index: InstalledPluginIndex,
|
||||
env: NodeJS.ProcessEnv,
|
||||
): boolean {
|
||||
const configuredPluginIds = collectConfiguredPluginIds(params.config);
|
||||
const persistedPluginIds = new Set(index.plugins.map((plugin) => plugin.pluginId));
|
||||
const missingConfiguredPluginIds = new Set(
|
||||
[...configuredPluginIds].filter((pluginId) => !persistedPluginIds.has(pluginId)),
|
||||
);
|
||||
if (missingConfiguredPluginIds.size === 0) {
|
||||
return false;
|
||||
}
|
||||
const globalExtensionsRoot = resolvePluginSourceRoots({
|
||||
workspaceDir: params.workspaceDir,
|
||||
env,
|
||||
}).global;
|
||||
const discovery = discoverConfiguredPluginLoadPaths({
|
||||
loadPaths: [globalExtensionsRoot],
|
||||
workspaceDir: params.workspaceDir,
|
||||
env,
|
||||
});
|
||||
const registry = loadPluginManifestRegistryCore({
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env,
|
||||
candidates: discovery.candidates,
|
||||
diagnostics: discovery.diagnostics,
|
||||
installRecords: extractPluginInstallRecordsFromInstalledPluginIndex(index),
|
||||
});
|
||||
return registry.plugins.some((plugin) => missingConfiguredPluginIds.has(plugin.id));
|
||||
}
|
||||
|
||||
export function loadPluginRegistrySnapshotWithMetadata(
|
||||
params: LoadPluginRegistryParams = {},
|
||||
): PluginRegistrySnapshotResult {
|
||||
|
||||
Reference in New Issue
Block a user