fix(plugins): stop tracking runtime deps manifests

This commit is contained in:
Peter Steinberger
2026-04-23 05:48:38 +01:00
parent ccde1c4707
commit e763ea1119
19 changed files with 76 additions and 59 deletions

4
.gitignore vendored
View File

@@ -152,3 +152,7 @@ test/fixtures/openclaw-vitest-unit-report.json
analysis/
.artifacts/qa-e2e/
extensions/qa-lab/web/dist/
# Generated bundled plugin runtime dependency manifests
extensions/**/.openclaw-runtime-deps.json
extensions/**/.openclaw-runtime-deps-stamp.json

View File

@@ -1,7 +0,0 @@
{
"specs": [
"@anthropic-ai/sdk@0.90.0",
"@aws/bedrock-token-generator@^1.1.0",
"@mariozechner/pi-ai@0.69.0"
]
}

View File

@@ -1,7 +0,0 @@
{
"specs": [
"@aws-sdk/client-bedrock-runtime@3.1033.0",
"@aws-sdk/client-bedrock@3.1033.0",
"@aws-sdk/credential-provider-node@3.972.33"
]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@mariozechner/pi-ai@0.69.0"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["typebox@1.1.28"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@mariozechner/pi-coding-agent@0.69.0", "ws@^8.20.0", "zod@^4.3.6"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["typebox@1.1.28"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@mariozechner/pi-ai@0.69.0"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@clack/prompts@^1.2.0", "@mariozechner/pi-ai@0.69.0"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@google/genai@^1.50.1", "@mariozechner/pi-ai@0.69.0"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@mariozechner/pi-ai@0.69.0"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@mariozechner/pi-ai@0.69.0"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["ws@^8.20.0"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@mariozechner/pi-ai@0.69.0", "typebox@1.1.28"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@mariozechner/pi-ai@0.69.0", "ws@^8.20.0"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["typebox@1.1.28"]
}

View File

@@ -1,3 +0,0 @@
{
"specs": ["@mariozechner/pi-ai@0.69.0", "typebox@1.1.28", "ws@^8.20.0"]
}

View File

@@ -575,14 +575,21 @@ describe("ensureBundledPluginRuntimeDeps", () => {
]);
expect(installRoot).toContain(stageDir);
expect(installRoot).not.toBe(pluginRoot);
expect(
JSON.parse(fs.readFileSync(path.join(installRoot, ".openclaw-runtime-deps.json"), "utf8")),
).toEqual({ specs: ["tokenjuice@0.6.1"] });
});
it("keeps source-checkout bundled runtime deps in the plugin root by default", () => {
it("keeps source-checkout bundled runtime deps in the plugin root without manifest churn", () => {
const packageRoot = makeTempDir();
fs.mkdirSync(path.join(packageRoot, ".git"), { recursive: true });
fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
const pluginRoot = path.join(packageRoot, "extensions", "tokenjuice");
fs.mkdirSync(pluginRoot, { recursive: true });
fs.writeFileSync(
path.join(pluginRoot, ".openclaw-runtime-deps.json"),
JSON.stringify({ specs: ["stale@9.9.9"] }),
);
fs.writeFileSync(
path.join(pluginRoot, "package.json"),
JSON.stringify({
@@ -617,6 +624,43 @@ describe("ensureBundledPluginRuntimeDeps", () => {
},
]);
expect(resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} })).toBe(pluginRoot);
expect(fs.existsSync(path.join(pluginRoot, ".openclaw-runtime-deps.json"))).toBe(false);
});
it("removes stale source-checkout manifests even when runtime deps are present", () => {
const packageRoot = makeTempDir();
fs.mkdirSync(path.join(packageRoot, ".git"), { recursive: true });
fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
const pluginRoot = path.join(packageRoot, "extensions", "tokenjuice");
fs.mkdirSync(path.join(pluginRoot, "node_modules", "tokenjuice"), { recursive: true });
fs.writeFileSync(
path.join(pluginRoot, "package.json"),
JSON.stringify({
dependencies: {
tokenjuice: "0.6.1",
},
}),
);
fs.writeFileSync(
path.join(pluginRoot, "node_modules", "tokenjuice", "package.json"),
JSON.stringify({ name: "tokenjuice", version: "0.6.1" }),
);
fs.writeFileSync(
path.join(pluginRoot, ".openclaw-runtime-deps.json"),
JSON.stringify({ specs: ["stale@9.9.9"] }),
);
const result = ensureBundledPluginRuntimeDeps({
env: {},
installDeps: () => {
throw new Error("present source-checkout runtime deps should not reinstall");
},
pluginId: "tokenjuice",
pluginRoot,
});
expect(result).toEqual({ installedSpecs: [], retainSpecs: [] });
expect(fs.existsSync(path.join(pluginRoot, ".openclaw-runtime-deps.json"))).toBe(false);
});
it("treats Docker build source trees without .git as source checkouts", () => {

View File

@@ -263,6 +263,20 @@ function writeRetainedRuntimeDepsManifest(installRoot: string, specs: readonly s
);
}
function removeRetainedRuntimeDepsManifest(installRoot: string): void {
fs.rmSync(path.join(installRoot, RETAINED_RUNTIME_DEPS_MANIFEST), { force: true });
}
function shouldPersistRetainedRuntimeDepsManifest(params: {
pluginRoot: string;
installRoot: string;
}): boolean {
if (path.resolve(params.installRoot) !== path.resolve(params.pluginRoot)) {
return true;
}
return !resolveSourceCheckoutPackageRoot(params.pluginRoot);
}
export function isWritableDirectory(dir: string): boolean {
let probeDir: string | null = null;
try {
@@ -873,6 +887,13 @@ export function ensureBundledPluginRuntimeDeps(params: {
const installRoot = resolveBundledRuntimeDependencyInstallRoot(params.pluginRoot, {
env: params.env,
});
const persistRetainedManifest = shouldPersistRetainedRuntimeDepsManifest({
pluginRoot: params.pluginRoot,
installRoot,
});
if (!persistRetainedManifest) {
removeRetainedRuntimeDepsManifest(installRoot);
}
const dependencySpecs = deps
.map((dep) => `${dep.name}@${dep.version}`)
.toSorted((left, right) => left.localeCompare(right));
@@ -883,7 +904,9 @@ export function ensureBundledPluginRuntimeDeps(params: {
if (missingSpecs.length === 0) {
return { installedSpecs: [], retainSpecs: [] };
}
const retainedManifestSpecs = readRetainedRuntimeDepsManifest(installRoot);
const retainedManifestSpecs = persistRetainedManifest
? readRetainedRuntimeDepsManifest(installRoot)
: [];
const installSpecs = [
...new Set([...(params.retainSpecs ?? []), ...retainedManifestSpecs, ...dependencySpecs]),
].toSorted((left, right) => left.localeCompare(right));
@@ -918,7 +941,9 @@ export function ensureBundledPluginRuntimeDeps(params: {
env: params.env,
}));
install({ installRoot, installExecutionRoot, missingSpecs, installSpecs });
writeRetainedRuntimeDepsManifest(installRoot, installSpecs);
if (persistRetainedManifest) {
writeRetainedRuntimeDepsManifest(installRoot, installSpecs);
}
storeSourceCheckoutRuntimeDepsCache({ cacheDir, installRoot });
return { installedSpecs: missingSpecs, retainSpecs: installSpecs };
}