
























@@ -96,7 +96,7 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
9696expect(result.diagnostics).toEqual([]);
9797});
989899-it("keeps persisted package plugins on the fast path when file signatures match", () => {
99+it("keeps persisted package plugins when metadata still matches", () => {
100100const tempRoot = makeTempDir();
101101const rootDir = path.join(tempRoot, "workspace");
102102const stateDir = path.join(tempRoot, "state");
@@ -113,22 +113,14 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
113113expect(record?.packageJson?.fileSignature).toBeDefined();
114114writePersistedInstalledPluginIndexSync(index, { stateDir });
115115116-const readFileSyncSpy = vi.spyOn(fs, "readFileSync");
117116const result = loadPluginRegistrySnapshotWithMetadata({
118117 config,
119118 env,
120119 stateDir,
121120});
122-const pluginMetadataFileReads = readFileSyncSpy.mock.calls.filter((call) => {
123-const filePath = String(call[0]);
124-return (
125-filePath === path.join(rootDir, "openclaw.plugin.json") ||
126-filePath === path.join(rootDir, "package.json")
127-);
128-});
129121130122expect(result.source).toBe("persisted");
131-expect(pluginMetadataFileReads).toEqual([]);
123+expect(result.diagnostics).toEqual([]);
132124});
133125134126it("detects same-size same-mtime manifest replacements", () => {
@@ -197,4 +189,55 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
197189expect.objectContaining({ code: "persisted-registry-stale-source" }),
198190);
199191});
192+193+it("detects package.json replacements even when stored stat fields still match", () => {
194+const tempRoot = makeTempDir();
195+const rootDir = path.join(tempRoot, "workspace");
196+const stateDir = path.join(tempRoot, "state");
197+const env = { ...createHermeticEnv(tempRoot), OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1" };
198+const config = {
199+plugins: {
200+load: { paths: [rootDir] },
201+},
202+};
203+writePackagePlugin(rootDir);
204+const index = loadInstalledPluginIndex({ config, env });
205+206+replaceFilePreservingSizeAndMtime(
207+path.join(rootDir, "package.json"),
208+JSON.stringify({ name: "demo", version: "1.0.1" }),
209+);
210+const stat = fs.statSync(path.join(rootDir, "package.json"));
211+const [plugin] = index.plugins;
212+if (!plugin?.packageJson) {
213+throw new Error("expected test plugin package metadata");
214+}
215+const stalePlugin = {
216+ ...plugin,
217+packageJson: {
218+ ...plugin.packageJson,
219+fileSignature: {
220+size: stat.size,
221+mtimeMs: stat.mtimeMs,
222+ctimeMs: stat.ctimeMs,
223+},
224+},
225+};
226+const staleIndex: InstalledPluginIndex = {
227+ ...index,
228+plugins: [stalePlugin, ...index.plugins.slice(1)],
229+};
230+writePersistedInstalledPluginIndexSync(staleIndex, { stateDir });
231+232+const result = loadPluginRegistrySnapshotWithMetadata({
233+ config,
234+ env,
235+ stateDir,
236+});
237+238+expect(result.source).toBe("derived");
239+expect(result.diagnostics).toContainEqual(
240+expect.objectContaining({ code: "persisted-registry-stale-source" }),
241+);
242+});
200243});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。