mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:00:44 +00:00
fix: reuse plugin snapshot for embedded settings
This commit is contained in:
@@ -9,7 +9,11 @@ import {
|
||||
normalizePluginsConfigWithResolver,
|
||||
resolveEffectivePluginActivationState,
|
||||
} from "../plugins/config-policy.js";
|
||||
import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import {
|
||||
isPluginMetadataSnapshotCompatible,
|
||||
loadPluginMetadataSnapshot,
|
||||
type PluginMetadataSnapshot,
|
||||
} from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { loadEmbeddedPiMcpConfig } from "./embedded-pi-mcp.js";
|
||||
|
||||
const log = createSubsystemLogger("embedded-pi-settings");
|
||||
@@ -61,23 +65,37 @@ function loadBundleSettingsFile(params: {
|
||||
export function loadEnabledBundlePiSettingsSnapshot(params: {
|
||||
cwd: string;
|
||||
cfg?: OpenClawConfig;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
pluginMetadataSnapshot?: PluginMetadataSnapshot;
|
||||
}): PiSettingsSnapshot {
|
||||
const workspaceDir = params.cwd.trim();
|
||||
if (!workspaceDir) {
|
||||
return {};
|
||||
}
|
||||
const metadataSnapshot = loadPluginMetadataSnapshot({
|
||||
workspaceDir,
|
||||
config: params.cfg ?? {},
|
||||
env: process.env,
|
||||
});
|
||||
const config = params.cfg ?? {};
|
||||
const env = params.env ?? process.env;
|
||||
const providedSnapshot = params.pluginMetadataSnapshot;
|
||||
const metadataSnapshot =
|
||||
providedSnapshot &&
|
||||
isPluginMetadataSnapshotCompatible({
|
||||
snapshot: providedSnapshot,
|
||||
config,
|
||||
env,
|
||||
workspaceDir,
|
||||
})
|
||||
? providedSnapshot
|
||||
: loadPluginMetadataSnapshot({
|
||||
workspaceDir,
|
||||
config,
|
||||
env,
|
||||
});
|
||||
const registry = metadataSnapshot.manifestRegistry;
|
||||
if (registry.plugins.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const normalizedPlugins = normalizePluginsConfigWithResolver(
|
||||
params.cfg?.plugins,
|
||||
config.plugins,
|
||||
metadataSnapshot.normalizePluginId,
|
||||
);
|
||||
let snapshot: PiSettingsSnapshot = {};
|
||||
@@ -91,7 +109,7 @@ export function loadEnabledBundlePiSettingsSnapshot(params: {
|
||||
id: record.id,
|
||||
origin: record.origin,
|
||||
config: normalizedPlugins,
|
||||
rootConfig: params.cfg,
|
||||
rootConfig: config,
|
||||
});
|
||||
if (!activationState.activated) {
|
||||
continue;
|
||||
@@ -110,7 +128,7 @@ export function loadEnabledBundlePiSettingsSnapshot(params: {
|
||||
|
||||
const embeddedPiMcp = loadEmbeddedPiMcpConfig({
|
||||
workspaceDir,
|
||||
cfg: params.cfg,
|
||||
cfg: config,
|
||||
});
|
||||
for (const diagnostic of embeddedPiMcp.diagnostics) {
|
||||
log.warn(`bundle MCP skipped for ${diagnostic.pluginId}: ${diagnostic.message}`);
|
||||
|
||||
@@ -3,6 +3,11 @@ import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { createTrackedTempDirs } from "../test-utils/tracked-temp-dirs.js";
|
||||
|
||||
const pluginMetadataSnapshotMocks = vi.hoisted(() => ({
|
||||
isPluginMetadataSnapshotCompatible: vi.fn(),
|
||||
loadPluginMetadataSnapshot: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../infra/boundary-file-read.js", async () => {
|
||||
const fs = await import("node:fs");
|
||||
return {
|
||||
@@ -107,11 +112,17 @@ vi.mock("../plugins/plugin-metadata-snapshot.js", async () => {
|
||||
],
|
||||
};
|
||||
};
|
||||
return {
|
||||
loadPluginMetadataSnapshot: (params: { workspaceDir?: string }) => ({
|
||||
pluginMetadataSnapshotMocks.isPluginMetadataSnapshotCompatible.mockImplementation(() => false);
|
||||
pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot.mockImplementation(
|
||||
(params: { workspaceDir?: string }) => ({
|
||||
manifestRegistry: loadRegistry(params),
|
||||
normalizePluginId: (id: string) => id.trim(),
|
||||
}),
|
||||
);
|
||||
return {
|
||||
isPluginMetadataSnapshotCompatible:
|
||||
pluginMetadataSnapshotMocks.isPluginMetadataSnapshotCompatible,
|
||||
loadPluginMetadataSnapshot: pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -161,6 +172,8 @@ const tempDirs = createTrackedTempDirs();
|
||||
|
||||
afterEach(async () => {
|
||||
await tempDirs.cleanup();
|
||||
pluginMetadataSnapshotMocks.isPluginMetadataSnapshotCompatible.mockClear();
|
||||
pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot.mockClear();
|
||||
});
|
||||
|
||||
async function createWorkspaceBundle(params: {
|
||||
@@ -181,6 +194,87 @@ async function createWorkspaceBundle(params: {
|
||||
}
|
||||
|
||||
describe("loadEnabledBundlePiSettingsSnapshot", () => {
|
||||
it("reuses a compatible plugin metadata snapshot without loading a fresh one", 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",
|
||||
);
|
||||
|
||||
pluginMetadataSnapshotMocks.isPluginMetadataSnapshotCompatible.mockReturnValueOnce(true);
|
||||
pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot.mockClear();
|
||||
|
||||
const snapshot = loadEnabledBundlePiSettingsSnapshot({
|
||||
cwd: workspaceDir,
|
||||
cfg: {
|
||||
plugins: {
|
||||
entries: {
|
||||
"claude-bundle": { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
pluginMetadataSnapshot: {
|
||||
manifestRegistry: {
|
||||
diagnostics: [],
|
||||
plugins: [
|
||||
{
|
||||
id: "claude-bundle",
|
||||
origin: "workspace",
|
||||
format: "bundle",
|
||||
bundleFormat: "claude",
|
||||
settingsFiles: ["settings.json"],
|
||||
rootDir: resolvedPluginRoot,
|
||||
},
|
||||
],
|
||||
},
|
||||
normalizePluginId: (id: string) => id.trim(),
|
||||
} as unknown as Parameters<
|
||||
typeof loadEnabledBundlePiSettingsSnapshot
|
||||
>[0]["pluginMetadataSnapshot"],
|
||||
});
|
||||
|
||||
expect(snapshot.hideThinkingBlock).toBe(true);
|
||||
expect(pluginMetadataSnapshotMocks.isPluginMetadataSnapshotCompatible).toHaveBeenCalledOnce();
|
||||
expect(pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to a fresh plugin metadata load for an incompatible snapshot", async () => {
|
||||
const workspaceDir = await tempDirs.make("openclaw-workspace-");
|
||||
const pluginRoot = await createWorkspaceBundle({ workspaceDir });
|
||||
await fs.writeFile(
|
||||
path.join(pluginRoot, "settings.json"),
|
||||
JSON.stringify({ hideThinkingBlock: true }),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
pluginMetadataSnapshotMocks.isPluginMetadataSnapshotCompatible.mockReturnValueOnce(false);
|
||||
pluginMetadataSnapshotMocks.loadPluginMetadataSnapshot.mockClear();
|
||||
|
||||
const snapshot = loadEnabledBundlePiSettingsSnapshot({
|
||||
cwd: workspaceDir,
|
||||
cfg: {
|
||||
plugins: {
|
||||
entries: {
|
||||
"claude-bundle": { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
pluginMetadataSnapshot: {
|
||||
manifestRegistry: { diagnostics: [], plugins: [] },
|
||||
normalizePluginId: (id: string) => id.trim(),
|
||||
} as unknown as Parameters<
|
||||
typeof loadEnabledBundlePiSettingsSnapshot
|
||||
>[0]["pluginMetadataSnapshot"],
|
||||
});
|
||||
|
||||
expect(snapshot.hideThinkingBlock).toBe(true);
|
||||
expect(pluginMetadataSnapshotMocks.isPluginMetadataSnapshotCompatible).toHaveBeenCalledOnce();
|
||||
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 });
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SettingsManager } from "@mariozechner/pi-coding-agent";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import {
|
||||
buildEmbeddedPiSettingsSnapshot,
|
||||
loadEnabledBundlePiSettingsSnapshot,
|
||||
@@ -11,12 +12,14 @@ function createEmbeddedPiSettingsManager(params: {
|
||||
cwd: string;
|
||||
agentDir: string;
|
||||
cfg?: OpenClawConfig;
|
||||
pluginMetadataSnapshot?: PluginMetadataSnapshot;
|
||||
}): SettingsManager {
|
||||
const fileSettingsManager = SettingsManager.create(params.cwd, params.agentDir);
|
||||
const policy = resolveEmbeddedPiProjectSettingsPolicy(params.cfg);
|
||||
const pluginSettings = loadEnabledBundlePiSettingsSnapshot({
|
||||
cwd: params.cwd,
|
||||
cfg: params.cfg,
|
||||
pluginMetadataSnapshot: params.pluginMetadataSnapshot,
|
||||
});
|
||||
const hasPluginSettings = Object.keys(pluginSettings).length > 0;
|
||||
if (policy === "trusted" && !hasPluginSettings) {
|
||||
@@ -46,6 +49,7 @@ export function createPreparedEmbeddedPiSettingsManager(params: {
|
||||
cwd: string;
|
||||
agentDir: string;
|
||||
cfg?: OpenClawConfig;
|
||||
pluginMetadataSnapshot?: PluginMetadataSnapshot;
|
||||
/** Resolved context window budget so reserve-token floor can be capped for small models. */
|
||||
contextTokenBudget?: number;
|
||||
}): SettingsManager {
|
||||
|
||||
Reference in New Issue
Block a user