diff --git a/src/__tests__/alexandria-beta.test.ts b/src/__tests__/alexandria-beta.test.ts index 83d7332bbf..3cc1456ea0 100644 --- a/src/__tests__/alexandria-beta.test.ts +++ b/src/__tests__/alexandria-beta.test.ts @@ -904,3 +904,43 @@ it('fails clearly on an unknown thread', async () => { expect(malformed.code).toBe(1); expect(requests).toHaveLength(1); }); + +it('falls back to category browsing after an unknown provider, but preserves other failures', async () => { + responseFor = (body) => + body.alexandria[0].options.providers + ? { + success: true, + scrape_id: 'discovery-1', + data: { + creditsCost: 0, + alexandria: [ + { + provider: 'firecrawl', + capability: 'find-tools', + error: { + status: 400, + code: 'invalid_request', + message: + 'Unknown or unavailable providers. Browse the catalogue for accessible IDs; use query for natural-language search.', + }, + }, + ], + }, + } + : catalogue('providers', [{ id: 'amazon-com', provider: 'amazon-com' }]); + const result = await cli(['list', 'shopping', '--json']); + expect(result.code, JSON.stringify(result)).toBe(0); + expect(requests.map(({ body }) => body.alexandria[0].options)).toEqual([ + { providers: ['shopping'], level: 'tools', limit: 20 }, + { categories: ['shopping'], level: 'providers', limit: 20 }, + ]); + expect(result.stdout).toContain('amazon-com'); + requests.length = 0; + responseFor = () => ({ + success: false, + error: 'Rate limit exceeded', + code: 'rate_limited', + }); + expect((await cli(['list', 'shopping', '--json'])).code).toBe(1); + expect(requests).toHaveLength(1); +}); diff --git a/src/commands/list.ts b/src/commands/list.ts index 9dbdfe6565..4f095a81f1 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -371,13 +371,30 @@ export async function handleList( } let scope: Selectors = { providers: [path[0]] }; let remaining = path.slice(1); - let result = options.category - ? undefined - : await fetchPage({ + let result; + if (!options.category) { + try { + result = await fetchPage({ ...scope, level: remaining.length ? 'providers' : 'tools', limit, }); + } catch (error) { + const envelope = + error instanceof DiscoveryFailure ? error.envelope : undefined; + const failure = envelope?.data?.alexandria?.[0]?.error ?? envelope; + const message = failure?.message ?? failure?.error; + // An unknown provider can still be a category; other failures must surface. + if ( + failure?.code !== 'unknown_provider' && + !( + typeof message === 'string' && + message.startsWith('Unknown or unavailable providers.') + ) + ) + throw error; + } + } if (!result?.page.total) { scope = { categories: [path[0]] }; result = await fetchPage({ ...scope, level: 'providers', limit });