mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 08:50:43 +00:00
fix: require default discovery for metadata reuse
This commit is contained in:
@@ -45,6 +45,22 @@ function canReuseUnscopedCurrentPluginMetadataSnapshot(config: OpenClawConfig):
|
||||
return normalizePluginsConfigWithResolver(config.plugins).loadPaths.length === 0;
|
||||
}
|
||||
|
||||
function resolveUnscopedCurrentPluginMetadataSnapshot(params: {
|
||||
config: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
workspaceDir?: string;
|
||||
}): PluginMetadataSnapshot | undefined {
|
||||
if (!canReuseUnscopedCurrentPluginMetadataSnapshot(params.config)) {
|
||||
return undefined;
|
||||
}
|
||||
return getCurrentPluginMetadataSnapshot({
|
||||
env: params.env,
|
||||
workspaceDir: params.workspaceDir,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
requireDefaultDiscoveryContext: true,
|
||||
});
|
||||
}
|
||||
|
||||
function loadBundleSettingsFile(params: {
|
||||
rootDir: string;
|
||||
relativePath: string;
|
||||
@@ -94,13 +110,11 @@ export function loadEnabledBundlePiSettingsSnapshot(params: {
|
||||
env,
|
||||
workspaceDir,
|
||||
}) ??
|
||||
(canReuseUnscopedCurrentPluginMetadataSnapshot(config)
|
||||
? getCurrentPluginMetadataSnapshot({
|
||||
env,
|
||||
workspaceDir,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
})
|
||||
: undefined) ??
|
||||
resolveUnscopedCurrentPluginMetadataSnapshot({
|
||||
config,
|
||||
env,
|
||||
workspaceDir,
|
||||
}) ??
|
||||
loadPluginMetadataSnapshot({
|
||||
workspaceDir,
|
||||
config,
|
||||
|
||||
@@ -364,6 +364,64 @@ describe("loadEnabledBundlePiSettingsSnapshot", () => {
|
||||
expect(pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("does not reuse a load-path current snapshot for a config with default load paths", async () => {
|
||||
const workspaceDir = await tempDirs.make("openclaw-workspace-");
|
||||
const pluginRoot = await createWorkspaceBundle({ workspaceDir });
|
||||
const resolvedPluginRoot = await fs.realpath(pluginRoot);
|
||||
await fs.writeFile(
|
||||
path.join(pluginRoot, "settings.json"),
|
||||
JSON.stringify({ hideThinkingBlock: true }),
|
||||
"utf-8",
|
||||
);
|
||||
const staleSnapshot = {
|
||||
policyHash: "policy",
|
||||
manifestRegistry: {
|
||||
diagnostics: [],
|
||||
plugins: [
|
||||
{
|
||||
id: "claude-bundle",
|
||||
origin: "workspace",
|
||||
format: "bundle",
|
||||
bundleFormat: "claude",
|
||||
settingsFiles: ["settings.json"],
|
||||
rootDir: resolvedPluginRoot,
|
||||
},
|
||||
],
|
||||
},
|
||||
normalizePluginId: (id: string) => id.trim(),
|
||||
};
|
||||
pluginMetadataSnapshotMocks.getCurrentPluginMetadataSnapshot.mockImplementation(
|
||||
(params: { config?: unknown; requireDefaultDiscoveryContext?: boolean }) => {
|
||||
if (params.config || params.requireDefaultDiscoveryContext) {
|
||||
return undefined;
|
||||
}
|
||||
return staleSnapshot;
|
||||
},
|
||||
);
|
||||
pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot.mockClear();
|
||||
|
||||
const snapshot = loadEnabledBundlePiSettingsSnapshot({
|
||||
cwd: workspaceDir,
|
||||
cfg: {
|
||||
plugins: {
|
||||
entries: {
|
||||
"claude-bundle": { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(snapshot.hideThinkingBlock).toBe(true);
|
||||
expect(pluginMetadataSnapshotMocks.getCurrentPluginMetadataSnapshot).toHaveBeenCalledTimes(2);
|
||||
expect(pluginMetadataSnapshotMocks.getCurrentPluginMetadataSnapshot).toHaveBeenLastCalledWith({
|
||||
env: process.env,
|
||||
workspaceDir,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
requireDefaultDiscoveryContext: true,
|
||||
});
|
||||
expect(pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("loads sanitized settings and MCP defaults from enabled bundle plugins", async () => {
|
||||
const workspaceDir = await tempDirs.make("openclaw-workspace-");
|
||||
const pluginRoot = await createWorkspaceBundle({ workspaceDir });
|
||||
|
||||
@@ -128,13 +128,18 @@ export function resolveProviderAuthAliasMap(
|
||||
env,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
}) ??
|
||||
(normalizePluginsConfig(config.plugins).loadPaths.length === 0
|
||||
? getCurrentPluginMetadataSnapshot({
|
||||
...(params?.workspaceDir !== undefined ? { workspaceDir: params.workspaceDir } : {}),
|
||||
env,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
})
|
||||
: undefined) ??
|
||||
(() => {
|
||||
if (normalizePluginsConfig(config.plugins).loadPaths.length !== 0) {
|
||||
return undefined;
|
||||
}
|
||||
const currentSnapshot = getCurrentPluginMetadataSnapshot({
|
||||
...(params?.workspaceDir !== undefined ? { workspaceDir: params.workspaceDir } : {}),
|
||||
env,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
requireDefaultDiscoveryContext: true,
|
||||
});
|
||||
return currentSnapshot;
|
||||
})() ??
|
||||
loadPluginMetadataSnapshot({
|
||||
config,
|
||||
...(params?.workspaceDir !== undefined ? { workspaceDir: params.workspaceDir } : {}),
|
||||
|
||||
@@ -146,6 +146,7 @@ describe("applyPluginAutoEnable core", () => {
|
||||
}),
|
||||
{
|
||||
config: snapshotConfig,
|
||||
env,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
},
|
||||
);
|
||||
@@ -179,6 +180,7 @@ describe("applyPluginAutoEnable core", () => {
|
||||
}),
|
||||
{
|
||||
config: snapshotConfig,
|
||||
env,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
},
|
||||
);
|
||||
@@ -202,6 +204,40 @@ describe("applyPluginAutoEnable core", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("does not reuse a load-path current manifest registry for a config with default load paths", () => {
|
||||
const manifestRegistry = makeRegistry([{ id: "load-path-chat", channels: ["load-path-chat"] }]);
|
||||
const snapshotConfig: OpenClawConfig = {
|
||||
plugins: {
|
||||
allow: ["existing"],
|
||||
load: { paths: ["/tmp/custom-plugin-root"] },
|
||||
},
|
||||
};
|
||||
setCurrentPluginMetadataSnapshot(
|
||||
createPluginMetadataSnapshot({
|
||||
config: snapshotConfig,
|
||||
manifestRegistry,
|
||||
}),
|
||||
{ config: snapshotConfig, env },
|
||||
);
|
||||
|
||||
const result = applyPluginAutoEnable({
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["existing"],
|
||||
entries: {
|
||||
"load-path-chat": { config: { token: "x" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
env,
|
||||
});
|
||||
|
||||
expect(result.config.plugins?.allow).toEqual(["existing"]);
|
||||
expect(result.changes).not.toContain(
|
||||
"load-path-chat plugin config present, added to plugin allowlist.",
|
||||
);
|
||||
});
|
||||
|
||||
it("formats typed provider-auth candidates into stable reasons", () => {
|
||||
expect(
|
||||
resolvePluginAutoEnableCandidateReason({
|
||||
|
||||
@@ -966,6 +966,7 @@ export function resolvePluginAutoEnableManifestRegistry(params: {
|
||||
const snapshot = getCurrentPluginMetadataSnapshot({
|
||||
env: params.env,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
requireDefaultDiscoveryContext: true,
|
||||
});
|
||||
return snapshot?.policyHash === resolveInstalledPluginIndexPolicyHash(params.config)
|
||||
? snapshot
|
||||
|
||||
@@ -182,15 +182,17 @@ function applyPluginAutoEnableForActivation(params: {
|
||||
workspaceDir: params.workspaceDir,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
});
|
||||
const currentManifestRegistry =
|
||||
currentSnapshot?.manifestRegistry ??
|
||||
(normalizePluginsConfig(params.config.plugins).loadPaths.length === 0
|
||||
const defaultDiscoverySnapshot =
|
||||
normalizePluginsConfig(params.config.plugins).loadPaths.length === 0
|
||||
? getCurrentPluginMetadataSnapshot({
|
||||
env: params.env,
|
||||
workspaceDir: params.workspaceDir,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
})?.manifestRegistry
|
||||
: undefined);
|
||||
requireDefaultDiscoveryContext: true,
|
||||
})
|
||||
: undefined;
|
||||
const currentManifestRegistry =
|
||||
currentSnapshot?.manifestRegistry ?? defaultDiscoverySnapshot?.manifestRegistry;
|
||||
return applyPluginAutoEnable({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
|
||||
@@ -113,6 +113,32 @@ describe("current plugin metadata snapshot", () => {
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("rejects configless default-discovery reuse for snapshots created with load paths", () => {
|
||||
const config = { plugins: { allow: ["demo"], load: { paths: ["/plugins/one"] } } };
|
||||
const snapshot = createSnapshot({ config });
|
||||
setCurrentPluginMetadataSnapshot(snapshot, { config });
|
||||
|
||||
expect(
|
||||
getCurrentPluginMetadataSnapshot({
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
requireDefaultDiscoveryContext: true,
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("accepts configless default-discovery reuse for snapshots created without load paths", () => {
|
||||
const config = { plugins: { allow: ["demo"] } };
|
||||
const snapshot = createSnapshot({ config });
|
||||
setCurrentPluginMetadataSnapshot(snapshot, { config });
|
||||
|
||||
expect(
|
||||
getCurrentPluginMetadataSnapshot({
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
requireDefaultDiscoveryContext: true,
|
||||
}),
|
||||
).toBe(snapshot);
|
||||
});
|
||||
|
||||
it("rejects a current snapshot when env-resolved plugin load paths change", () => {
|
||||
const config = { plugins: { load: { paths: ["~/plugins"] } } };
|
||||
const snapshot = createSnapshot({ config });
|
||||
|
||||
@@ -70,6 +70,7 @@ export function getCurrentPluginMetadataSnapshot(
|
||||
env?: NodeJS.ProcessEnv;
|
||||
workspaceDir?: string;
|
||||
allowWorkspaceScopedSnapshot?: boolean;
|
||||
requireDefaultDiscoveryContext?: boolean;
|
||||
} = {},
|
||||
): PluginMetadataSnapshot | undefined {
|
||||
const {
|
||||
@@ -110,6 +111,25 @@ export function getCurrentPluginMetadataSnapshot(
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (params.requireDefaultDiscoveryContext === true) {
|
||||
const defaultDiscoveryConfigFingerprint = resolvePluginMetadataControlPlaneFingerprint(
|
||||
{},
|
||||
{
|
||||
env: params.env,
|
||||
index: snapshot.index,
|
||||
policyHash: snapshot.policyHash,
|
||||
workspaceDir: requestedWorkspaceDir,
|
||||
},
|
||||
);
|
||||
const compatibleFingerprints = new Set(compatibleConfigFingerprints ?? []);
|
||||
const fingerprintMatches =
|
||||
configFingerprint === defaultDiscoveryConfigFingerprint ||
|
||||
snapshot.configFingerprint === defaultDiscoveryConfigFingerprint ||
|
||||
compatibleFingerprints.has(defaultDiscoveryConfigFingerprint);
|
||||
if (!fingerprintMatches) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (snapshot.workspaceDir !== undefined && requestedWorkspaceDir === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user