Skip to content

Commit ce0b3ae

Browse files
author
liuxiaopai-ai
committed
fix(plugins): fallback install entrypoints for legacy manifests
1 parent dde4312 commit ce0b3ae

File tree

3 files changed

+73
-8
lines changed

3 files changed

+73
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Docs: https://docs.openclaw.ai
5050
- Web UI/config form: support SecretInput string-or-secret-ref unions in map `additionalProperties`, so provider API key fields stay editable instead of being marked unsupported. (#31866) Thanks @ningding97.
5151
- Slack/Bolt startup compatibility: remove invalid `message.channels` and `message.groups` event registrations so Slack providers no longer crash on startup with Bolt 4.6+; channel/group traffic continues through the unified `message` handler (`channel_type`). (#32033) Thanks @mahopan.
5252
- Telegram: guard duplicate-token checks and gateway startup token normalization when account tokens are missing, preventing `token.trim()` crashes during status/start flows. (#31973) Thanks @ningding97.
53+
- Plugins/install compatibility: allow installs to fall back to default plugin entry files (`./index.ts`, `./index.js`, `./dist/index.js`) when `package.json` is missing `openclaw.extensions` but a valid `openclaw.plugin.json` is present, restoring compatibility for older plugin package shapes. (#32019) Thanks @liuxiaopai-ai.
5354
- Skills/sherpa-onnx-tts: run the `sherpa-onnx-tts` bin under ESM (replace CommonJS `require` imports) and add regression coverage to prevent `require is not defined in ES module scope` startup crashes. (#31965) Thanks @bmendonca3.
5455
- Browser/default profile selection: default `browser.defaultProfile` behavior now prefers `openclaw` (managed standalone CDP) when no explicit default is configured, while still auto-provisioning the `chrome` relay profile for explicit opt-in use. (#32031) Fixes #31907. Thanks @liuxiaopai-ai.
5556
- Doctor/local memory provider checks: stop false-positive local-provider warnings when `provider=local` and no explicit `modelPath` is set by honoring default local model fallback while still warning when gateway probe reports local embeddings not ready. (#32014) Fixes #31998. Thanks @adhishthite.

src/plugins/install.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,40 @@ describe("installPluginFromArchive", () => {
428428
expect(result.error).toContain("openclaw.extensions");
429429
});
430430

431+
it("uses legacy entry fallback when openclaw.extensions is missing but plugin manifest exists", async () => {
432+
const { pluginDir, extensionsDir } = setupPluginInstallDirs();
433+
fs.writeFileSync(
434+
path.join(pluginDir, "package.json"),
435+
JSON.stringify({
436+
name: "@openclaw/legacy-entry-fallback",
437+
version: "0.0.1",
438+
}),
439+
"utf-8",
440+
);
441+
fs.writeFileSync(
442+
path.join(pluginDir, "openclaw.plugin.json"),
443+
JSON.stringify({
444+
id: "legacy-entry-fallback",
445+
configSchema: { type: "object", properties: {} },
446+
}),
447+
"utf-8",
448+
);
449+
fs.writeFileSync(path.join(pluginDir, "index.ts"), "export {};\n", "utf-8");
450+
451+
const result = await installPluginFromDir({
452+
dirPath: pluginDir,
453+
extensionsDir,
454+
});
455+
456+
expect(result.ok).toBe(true);
457+
if (!result.ok) {
458+
return;
459+
}
460+
expect(result.pluginId).toBe("legacy-entry-fallback");
461+
expect(result.extensions).toEqual(["./index.ts"]);
462+
expect(fs.existsSync(path.join(result.targetDir, "index.ts"))).toBe(true);
463+
});
464+
431465
it("warns when plugin contains dangerous code patterns", async () => {
432466
const { pluginDir, extensionsDir } = setupPluginInstallDirs();
433467

src/plugins/install.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ type PackageManifest = {
4343
dependencies?: Record<string, string>;
4444
} & Partial<Record<typeof MANIFEST_KEY, { extensions?: string[] }>>;
4545

46+
const LEGACY_EXTENSION_FALLBACK_ENTRIES = ["./index.ts", "./index.js", "./dist/index.js"] as const;
47+
4648
export type InstallPluginResult =
4749
| {
4850
ok: true;
@@ -81,16 +83,41 @@ function validatePluginId(pluginId: string): string | null {
8183
return null;
8284
}
8385

84-
async function ensureOpenClawExtensions(manifest: PackageManifest) {
85-
const extensions = manifest[MANIFEST_KEY]?.extensions;
86+
async function resolveLegacyExtensionsFallback(packageDir: string): Promise<string[]> {
87+
const manifestResult = loadPluginManifest(packageDir);
88+
if (!manifestResult.ok) {
89+
return [];
90+
}
91+
const resolvedEntries = await Promise.all(
92+
LEGACY_EXTENSION_FALLBACK_ENTRIES.map(async (entry) => ({
93+
entry,
94+
exists: await fileExists(path.join(packageDir, entry)),
95+
})),
96+
);
97+
return resolvedEntries.filter((item) => item.exists).map((item) => item.entry);
98+
}
99+
100+
async function ensureOpenClawExtensions(params: {
101+
manifest: PackageManifest;
102+
packageDir: string;
103+
}): Promise<string[]> {
104+
const extensions = params.manifest[MANIFEST_KEY]?.extensions;
105+
if (Array.isArray(extensions)) {
106+
const list = extensions.map((e) => (typeof e === "string" ? e.trim() : "")).filter(Boolean);
107+
if (list.length > 0) {
108+
return list;
109+
}
110+
}
111+
112+
const fallback = await resolveLegacyExtensionsFallback(params.packageDir);
113+
if (fallback.length > 0) {
114+
return fallback;
115+
}
116+
86117
if (!Array.isArray(extensions)) {
87118
throw new Error("package.json missing openclaw.extensions");
88119
}
89-
const list = extensions.map((e) => (typeof e === "string" ? e.trim() : "")).filter(Boolean);
90-
if (list.length === 0) {
91-
throw new Error("package.json openclaw.extensions is empty");
92-
}
93-
return list;
120+
throw new Error("package.json openclaw.extensions is empty");
94121
}
95122

96123
function buildFileInstallResult(pluginId: string, targetFile: string): InstallPluginResult {
@@ -148,7 +175,10 @@ async function installPluginFromPackageDir(params: {
148175

149176
let extensions: string[];
150177
try {
151-
extensions = await ensureOpenClawExtensions(manifest);
178+
extensions = await ensureOpenClawExtensions({
179+
manifest,
180+
packageDir: params.packageDir,
181+
});
152182
} catch (err) {
153183
return { ok: false, error: String(err) };
154184
}

0 commit comments

Comments
 (0)