fix(plugins): merge external catalog channel hints

This commit is contained in:
Vincent Koc
2026-05-03 16:24:07 -07:00
parent 392897304c
commit b520e40cf6
3 changed files with 74 additions and 1 deletions

View File

@@ -32,6 +32,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Plugins/catalog: merge official external catalog descriptors into partial package channel config metadata, so lagging WeCom/Yuanbao manifests keep their own schema while still exposing host-supplied labels and setup text. Thanks @vincentkoc.
- Plugins/catalog: supplement lagging official external WeCom and Yuanbao npm manifests with channel config descriptors and declared tool contracts from the OpenClaw catalog, so trusted package sweeps no longer fail because external package metadata trails the host contract. Thanks @vincentkoc.
- Plugins/install: let trusted official `@openclaw/*` catalog installs recover when npm `latest` points at a prerelease by falling back to the newest stable version, or by allowing prerelease-only launch packages with a warning instead of making beta/development plugin sweeps fail at install time. Thanks @vincentkoc.
- Google Meet: grant Chrome media permissions against the actual Meet tab, start the local realtime audio bridge only after Meet joins, expose realtime transcripts in status/logs, and force explicit audio responses with current OpenAI realtime output-audio events so BlackHole capture does not keep the OpenClaw participant muted or silent.

View File

@@ -1133,6 +1133,48 @@ describe("loadPluginManifestRegistry", () => {
).toBe(false);
});
it("fills missing official external catalog descriptors for partial npm channel configs", () => {
const dir = makeTempDir();
writeManifest(dir, {
id: "wecom-openclaw-plugin",
channels: ["wecom"],
configSchema: { type: "object" },
channelConfigs: {
wecom: {
schema: {
type: "object",
additionalProperties: false,
properties: {
corpId: { type: "string" },
},
},
},
},
});
const registry = loadRegistry([
createPluginCandidate({
idHint: "wecom-openclaw-plugin",
rootDir: dir,
origin: "global",
packageName: "@wecom/wecom-openclaw-plugin",
}),
]);
expect(registry.plugins[0]?.channelConfigs?.wecom).toEqual(
expect.objectContaining({
label: "WeCom",
description: "Enterprise WeChat conversation channel.",
schema: expect.objectContaining({
additionalProperties: false,
properties: {
corpId: { type: "string" },
},
}),
}),
);
});
it("drops prototype-polluting channel config keys from plugin manifests", () => {
const dir = makeTempDir();
writeTextFile(

View File

@@ -322,7 +322,37 @@ function mergeCatalogChannelConfigs(params: {
}
for (const [key, value] of Object.entries(params.manifestChannelConfigs ?? {})) {
if (!isBlockedObjectKey(key)) {
merged[key] = value;
const catalogValue = merged[key];
merged[key] = catalogValue
? {
...catalogValue,
...value,
schema: value.schema ?? catalogValue.schema,
...(catalogValue.uiHints || value.uiHints
? {
uiHints: {
...catalogValue.uiHints,
...value.uiHints,
},
}
: {}),
...((value.runtime ?? catalogValue.runtime)
? { runtime: value.runtime ?? catalogValue.runtime }
: {}),
...((value.label ?? catalogValue.label)
? { label: value.label ?? catalogValue.label }
: {}),
...((value.description ?? catalogValue.description)
? { description: value.description ?? catalogValue.description }
: {}),
...((value.preferOver ?? catalogValue.preferOver)
? { preferOver: value.preferOver ?? catalogValue.preferOver }
: {}),
...((value.commands ?? catalogValue.commands)
? { commands: value.commands ?? catalogValue.commands }
: {}),
}
: value;
}
}
return Object.keys(merged).length > 0 ? merged : undefined;