Skip to content

Commit 8fb15dd

Browse files
Galin IlievGalin Iliev
authored andcommitted
fix(plugins): cache installed manifest fingerprints
1 parent 083377a commit 8fb15dd

2 files changed

Lines changed: 75 additions & 1 deletion

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import fs from "node:fs";
2+
import os from "node:os";
3+
import path from "node:path";
4+
import { afterEach, describe, expect, test, vi } from "vitest";
5+
import type { InstalledPluginIndex } from "./installed-plugin-index.js";
6+
import { resolveInstalledManifestRegistryIndexFingerprint } from "./manifest-registry-installed.js";
7+
8+
describe("installed manifest registry fingerprint cache", () => {
9+
const tempDirs: string[] = [];
10+
11+
afterEach(() => {
12+
vi.restoreAllMocks();
13+
for (const tempDir of tempDirs.splice(0)) {
14+
fs.rmSync(tempDir, { recursive: true, force: true });
15+
}
16+
});
17+
18+
test("reuses the fingerprint for the same installed index object without re-statting files", () => {
19+
const pluginDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-installed-fingerprint-"));
20+
tempDirs.push(pluginDir);
21+
const manifestPath = path.join(pluginDir, "plugin.json");
22+
const packageJsonPath = path.join(pluginDir, "package.json");
23+
fs.writeFileSync(manifestPath, JSON.stringify({ id: "test-plugin" }), "utf-8");
24+
fs.writeFileSync(packageJsonPath, JSON.stringify({ name: "test-plugin" }), "utf-8");
25+
const index = {
26+
version: 1,
27+
hostContractVersion: "test-host",
28+
compatRegistryVersion: "test-compat",
29+
migrationVersion: 1,
30+
policyHash: "test-policy",
31+
generatedAtMs: 1,
32+
installRecords: {},
33+
diagnostics: [],
34+
plugins: [
35+
{
36+
pluginId: "test-plugin",
37+
manifestPath,
38+
manifestHash: "test-manifest-hash",
39+
packageJson: {
40+
path: "package.json",
41+
hash: "test-package-hash",
42+
},
43+
rootDir: pluginDir,
44+
origin: "external",
45+
enabled: true,
46+
startup: {
47+
sidecar: false,
48+
memory: false,
49+
deferConfiguredChannelFullLoadUntilAfterListen: false,
50+
agentHarnesses: [],
51+
},
52+
compat: [],
53+
},
54+
],
55+
} as InstalledPluginIndex;
56+
const statSpy = vi.spyOn(fs, "statSync");
57+
58+
const first = resolveInstalledManifestRegistryIndexFingerprint(index);
59+
const statCallsAfterFirst = statSpy.mock.calls.length;
60+
const second = resolveInstalledManifestRegistryIndexFingerprint(index);
61+
62+
expect(second).toBe(first);
63+
expect(statCallsAfterFirst).toBeGreaterThan(0);
64+
expect(statSpy.mock.calls.length).toBe(statCallsAfterFirst);
65+
});
66+
});

src/plugins/manifest-registry-installed.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,18 @@ function buildInstalledManifestRegistryIndexKey(index: InstalledPluginIndex) {
113113
};
114114
}
115115

116+
const installedManifestRegistryIndexFingerprintCache = new WeakMap<InstalledPluginIndex, string>();
117+
116118
export function resolveInstalledManifestRegistryIndexFingerprint(
117119
index: InstalledPluginIndex,
118120
): string {
119-
return hashJson(buildInstalledManifestRegistryIndexKey(index));
121+
const cached = installedManifestRegistryIndexFingerprintCache.get(index);
122+
if (cached) {
123+
return cached;
124+
}
125+
const fingerprint = hashJson(buildInstalledManifestRegistryIndexKey(index));
126+
installedManifestRegistryIndexFingerprintCache.set(index, fingerprint);
127+
return fingerprint;
120128
}
121129

122130
function resolveInstalledPluginRootDir(record: InstalledPluginIndexRecord): string {

0 commit comments

Comments
 (0)