Skip to content

Commit 76da932

Browse files
committed
fix(plugins): preserve hosted catalog metadata entries
1 parent 47ce2ac commit 76da932

2 files changed

Lines changed: 72 additions & 2 deletions

File tree

src/plugins/official-external-plugin-catalog.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,59 @@ describe("official external plugin catalog", () => {
258258
}
259259
});
260260

261+
it("keeps live ClawHub metadata-only entries after hosted feed loading", async () => {
262+
const body = JSON.stringify({
263+
schemaVersion: 2,
264+
id: "clawhub-official",
265+
generatedAt: "2026-06-25T01:19:39.629Z",
266+
sequence: 11,
267+
entries: [
268+
{
269+
type: "plugin",
270+
id: "@expediagroup/expedia-openclaw",
271+
title: "Expedia Travel",
272+
version: "1.0.4",
273+
state: "available",
274+
publisher: {
275+
id: "expediagroup",
276+
trust: "official",
277+
},
278+
install: {
279+
candidates: [
280+
{
281+
sourceRef: "public-clawhub",
282+
package: "@expediagroup/expedia-openclaw",
283+
version: "1.0.4",
284+
integrity:
285+
"sha256:b355dda04403becaab8bbab069fd1e7b0578262e7459e598cc5b19615b5bdab9",
286+
},
287+
],
288+
},
289+
},
290+
],
291+
});
292+
293+
const result = await loadHostedOfficialExternalPluginCatalogEntries({
294+
fetchImpl: vi.fn(
295+
async () =>
296+
new Response(body, {
297+
status: 200,
298+
headers: {
299+
"content-length": String(new TextEncoder().encode(body).byteLength),
300+
},
301+
}),
302+
),
303+
});
304+
305+
expect(result.source).toBe("hosted");
306+
expect(result.entries).toHaveLength(1);
307+
expect(result.entries[0]).toMatchObject({
308+
id: "@expediagroup/expedia-openclaw",
309+
title: "Expedia Travel",
310+
version: "1.0.4",
311+
});
312+
});
313+
261314
it("falls back to the bundled catalog when hosted feed validation fails", async () => {
262315
const result = await loadHostedOfficialExternalPluginCatalogEntries({
263316
fetchImpl: vi.fn(

src/plugins/official-external-plugin-catalog.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,15 +343,32 @@ function dedupeOfficialExternalPluginCatalogEntries(
343343
): OfficialExternalPluginCatalogEntry[] {
344344
const resolved = new Map<string, OfficialExternalPluginCatalogEntry>();
345345
for (const entry of entries) {
346-
const pluginId = resolveOfficialExternalPluginId(entry);
347-
const key = pluginId ? `${entry.kind ?? "plugin"}:${pluginId}` : (entry.name ?? "");
346+
const key = resolveOfficialExternalPluginCatalogEntryKey(entry);
348347
if (key && !resolved.has(key)) {
349348
resolved.set(key, entry);
350349
}
351350
}
352351
return [...resolved.values()];
353352
}
354353

354+
function resolveOfficialExternalPluginCatalogEntryKey(
355+
entry: OfficialExternalPluginCatalogEntry,
356+
): string | undefined {
357+
const pluginId = resolveOfficialExternalPluginId(entry);
358+
if (pluginId) {
359+
return `${normalizeOptionalString(entry.kind) ?? "plugin"}:${pluginId}`;
360+
}
361+
const name = normalizeOptionalString(entry.name);
362+
if (name) {
363+
return name;
364+
}
365+
const id = normalizeOptionalString(entry.id);
366+
if (id) {
367+
return `${normalizeOptionalString(entry.kind) ?? normalizeOptionalString(entry.type) ?? "plugin"}:${id}`;
368+
}
369+
return undefined;
370+
}
371+
355372
function bundledFallbackResult(
356373
error: unknown,
357374
metadata?: HostedOfficialExternalPluginCatalogLoadResult["metadata"],

0 commit comments

Comments
 (0)