mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 12:17:40 +00:00
fix(plugins): ignore archived extension dirs during discovery
Co-authored-by: chenzhuoms <chenzhuoms@users.noreply.github.com>
This commit is contained in:
@@ -34,6 +34,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Feishu/Plugins: restore bundled Feishu SDK availability for global installs and strip `openclaw: workspace:*` from plugin `devDependencies` during plugin-version sync so npm-installed Feishu plugins do not fail dependency install. (#23611, #23645, #23603)
|
- Feishu/Plugins: restore bundled Feishu SDK availability for global installs and strip `openclaw: workspace:*` from plugin `devDependencies` during plugin-version sync so npm-installed Feishu plugins do not fail dependency install. (#23611, #23645, #23603)
|
||||||
- Plugins/Install: strip `workspace:*` devDependency entries from copied plugin manifests before `npm install --omit=dev`, preventing `EUNSUPPORTEDPROTOCOL` install failures for npm-published channel plugins (including Feishu and MS Teams).
|
- Plugins/Install: strip `workspace:*` devDependency entries from copied plugin manifests before `npm install --omit=dev`, preventing `EUNSUPPORTEDPROTOCOL` install failures for npm-published channel plugins (including Feishu and MS Teams).
|
||||||
- Config/Channels: auto-enable built-in channels by writing `channels.<id>.enabled=true` (not `plugins.entries.<id>`), and stop adding built-ins to `plugins.allow`, preventing `plugins.entries.telegram: plugin not found` validation failures.
|
- Config/Channels: auto-enable built-in channels by writing `channels.<id>.enabled=true` (not `plugins.entries.<id>`), and stop adding built-ins to `plugins.allow`, preventing `plugins.entries.telegram: plugin not found` validation failures.
|
||||||
|
- Plugins/Discovery: ignore scanned extension backup/disabled directory patterns (for example `.backup-*`, `.bak`, `.disabled*`) and move updater backup directories under `.openclaw-install-backups`, preventing duplicate plugin-id collisions from archived copies.
|
||||||
- Dev tooling: prevent `CLAUDE.md` symlink target regressions by excluding CLAUDE symlink sentinels from `oxfmt` and marking them `-text` in `.gitattributes`, so formatter/EOL normalization cannot reintroduce trailing-newline targets. Thanks @vincentkoc.
|
- Dev tooling: prevent `CLAUDE.md` symlink target regressions by excluding CLAUDE symlink sentinels from `oxfmt` and marking them `-text` in `.gitattributes`, so formatter/EOL normalization cannot reintroduce trailing-newline targets. Thanks @vincentkoc.
|
||||||
- Cron: honor `cron.maxConcurrentRuns` in the timer loop so due jobs can execute up to the configured parallelism instead of always running serially. (#11595) Thanks @Takhoffman.
|
- Cron: honor `cron.maxConcurrentRuns` in the timer loop so due jobs can execute up to the configured parallelism instead of always running serially. (#11595) Thanks @Takhoffman.
|
||||||
- Agents/Compaction: restore embedded compaction safeguard/context-pruning extension loading in production by wiring bundled extension factories into the resource loader instead of runtime file-path resolution. (#22349) Thanks @Glucksberg.
|
- Agents/Compaction: restore embedded compaction safeguard/context-pruning extension loading in production by wiring bundled extension factories into the resource loader instead of runtime file-path resolution. (#22349) Thanks @Glucksberg.
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ export async function installPackageDir(params: {
|
|||||||
params.logger?.info?.(`Installing to ${params.targetDir}…`);
|
params.logger?.info?.(`Installing to ${params.targetDir}…`);
|
||||||
let backupDir: string | null = null;
|
let backupDir: string | null = null;
|
||||||
if (params.mode === "update" && (await fileExists(params.targetDir))) {
|
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);
|
await fs.rename(params.targetDir, backupDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,38 @@ describe("discoverOpenClawPlugins", () => {
|
|||||||
expect(ids).toContain("beta");
|
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 () => {
|
it("loads package extension packs", async () => {
|
||||||
const stateDir = makeTempDir();
|
const stateDir = makeTempDir();
|
||||||
const globalExt = path.join(stateDir, "extensions", "pack");
|
const globalExt = path.join(stateDir, "extensions", "pack");
|
||||||
|
|||||||
@@ -206,6 +206,23 @@ function isExtensionFile(filePath: string): boolean {
|
|||||||
return !filePath.endsWith(".d.ts");
|
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 {
|
function readPackageManifest(dir: string): PackageManifest | null {
|
||||||
const manifestPath = path.join(dir, "package.json");
|
const manifestPath = path.join(dir, "package.json");
|
||||||
if (!fs.existsSync(manifestPath)) {
|
if (!fs.existsSync(manifestPath)) {
|
||||||
@@ -362,6 +379,9 @@ function discoverInDirectory(params: {
|
|||||||
if (!entry.isDirectory()) {
|
if (!entry.isDirectory()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (shouldIgnoreScannedDirectory(entry.name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const manifest = readPackageManifest(fullPath);
|
const manifest = readPackageManifest(fullPath);
|
||||||
const extensions = manifest ? resolvePackageExtensions(manifest) : [];
|
const extensions = manifest ? resolvePackageExtensions(manifest) : [];
|
||||||
|
|||||||
Reference in New Issue
Block a user