fix: filter external sources from ClawHub search results

This commit is contained in:
程序员阿江(Relakkes)
2026-09-21 22:14:36 +08:00
parent 9bf6e76896
commit 02a4a66a11
2 changed files with 37 additions and 2 deletions
@@ -96,6 +96,34 @@ describe('clawhubProvider', () => {
expect(page.items[0]!.author.handle.length).toBeGreaterThan(0)
})
it('filters aggregated external search results before applying the limit', async () => {
stubFetch(() => ({ body: JSON.stringify({ results: [
{ slug: 'typesafe-ai', source: 'skills-sh', install: { kind: 'skills-sh' } },
{ slug: 'external', source: 'other-registry' },
{ slug: 'external-install', install: { kind: 'skills-sh' } },
{ slug: 'oo-typesafe-ai', source: 'clawhub', install: { kind: 'clawhub' } },
{ slug: 'legacy-native' },
] }) }))
const page = await clawhubProvider.search({ q: 'typesafe', limit: 2 })
expect(page.items.map((item) => item.id)).toEqual([
'clawhub:oo-typesafe-ai',
'clawhub:legacy-native',
])
expect(page.nextCursor).toBeUndefined()
})
it('returns no installable entries when search only contains external sources', async () => {
stubFetch(() => ({ body: JSON.stringify({ results: [
{ slug: 'typesafe-ai', source: 'skills-sh', install: { kind: 'skills-sh' } },
] }) }))
const page = await clawhubProvider.search({ q: 'typesafe', limit: 24 })
expect(page.items).toEqual([])
})
it('builds detail with files, license and security from the version endpoint', async () => {
const detailBody = await fixture('clawhub-detail.json')
const versionBody = await fixture('clawhub-version-detail.json')
@@ -45,6 +45,8 @@ type ClawhubListItem = {
type ClawhubSearchResult = {
slug: string
source?: string
install?: { kind?: string }
displayName?: string
summary?: string
downloads?: number
@@ -206,8 +208,13 @@ export const clawhubProvider: MarketProvider = {
if (!Array.isArray(data.results)) {
throw new MarketUpstreamError('clawhub', MARKET_ERROR_CODES.upstreamBadResponse, 'clawhub search missing results')
}
// ClawHub search has no pagination — cap and mark exhausted.
return { items: data.results.filter((r) => r?.slug).slice(0, limit).map(normalizeSearchResult) }
// Search also aggregates external registries, which cannot use ClawHub's
// detail/file endpoints. Older native results omit source/install metadata.
// Filter before capping so external results do not consume native slots.
const nativeResults = data.results.filter((result) => result?.slug
&& (result.source === undefined || result.source === 'clawhub')
&& (result.install?.kind === undefined || result.install.kind === 'clawhub'))
return { items: nativeResults.slice(0, limit).map(normalizeSearchResult) }
},
async detail(slug): Promise<NormalizedSkillDetail> {