fix(plugins): ignore archived extension dirs during discovery

Co-authored-by: chenzhuoms <chenzhuoms@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-02-22 19:18:50 +01:00
parent 8839162b97
commit 9da5f9819b
4 changed files with 56 additions and 1 deletions

View File

@@ -62,7 +62,9 @@ export async function installPackageDir(params: {
params.logger?.info?.(`Installing to ${params.targetDir}`);
let backupDir: string | null = null;
if (params.mode === "update" && (await fileExists(params.targetDir))) {
backupDir = `${params.targetDir}.backup-${Date.now()}`;
const backupRoot = path.join(path.dirname(params.targetDir), ".openclaw-install-backups");
backupDir = path.join(backupRoot, `${path.basename(params.targetDir)}-${Date.now()}`);
await fs.mkdir(backupRoot, { recursive: true });
await fs.rename(params.targetDir, backupDir);
}

View File

@@ -58,6 +58,38 @@ describe("discoverOpenClawPlugins", () => {
expect(ids).toContain("beta");
});
it("ignores backup and disabled plugin directories in scanned roots", async () => {
const stateDir = makeTempDir();
const globalExt = path.join(stateDir, "extensions");
fs.mkdirSync(globalExt, { recursive: true });
const backupDir = path.join(globalExt, "feishu.backup-20260222");
fs.mkdirSync(backupDir, { recursive: true });
fs.writeFileSync(path.join(backupDir, "index.ts"), "export default function () {}", "utf-8");
const disabledDir = path.join(globalExt, "telegram.disabled.20260222");
fs.mkdirSync(disabledDir, { recursive: true });
fs.writeFileSync(path.join(disabledDir, "index.ts"), "export default function () {}", "utf-8");
const bakDir = path.join(globalExt, "discord.bak");
fs.mkdirSync(bakDir, { recursive: true });
fs.writeFileSync(path.join(bakDir, "index.ts"), "export default function () {}", "utf-8");
const liveDir = path.join(globalExt, "live");
fs.mkdirSync(liveDir, { recursive: true });
fs.writeFileSync(path.join(liveDir, "index.ts"), "export default function () {}", "utf-8");
const { candidates } = await withStateDir(stateDir, async () => {
return discoverOpenClawPlugins({});
});
const ids = candidates.map((candidate) => candidate.idHint);
expect(ids).toContain("live");
expect(ids).not.toContain("feishu.backup-20260222");
expect(ids).not.toContain("telegram.disabled.20260222");
expect(ids).not.toContain("discord.bak");
});
it("loads package extension packs", async () => {
const stateDir = makeTempDir();
const globalExt = path.join(stateDir, "extensions", "pack");

View File

@@ -206,6 +206,23 @@ function isExtensionFile(filePath: string): boolean {
return !filePath.endsWith(".d.ts");
}
function shouldIgnoreScannedDirectory(dirName: string): boolean {
const normalized = dirName.trim().toLowerCase();
if (!normalized) {
return true;
}
if (normalized.endsWith(".bak")) {
return true;
}
if (normalized.includes(".backup-")) {
return true;
}
if (normalized.includes(".disabled")) {
return true;
}
return false;
}
function readPackageManifest(dir: string): PackageManifest | null {
const manifestPath = path.join(dir, "package.json");
if (!fs.existsSync(manifestPath)) {
@@ -362,6 +379,9 @@ function discoverInDirectory(params: {
if (!entry.isDirectory()) {
continue;
}
if (shouldIgnoreScannedDirectory(entry.name)) {
continue;
}
const manifest = readPackageManifest(fullPath);
const extensions = manifest ? resolvePackageExtensions(manifest) : [];