Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions beta-skills/firecrawl-alexandria/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ description: Use for explicitly requested Firecrawl Alexandria beta tool discove

# Alexandria Beta

Use the beta CLI explicitly on every invocation: `npx firecrawl-cli@alexandria --enable alexandria`. Do not replace the user's stable CLI or use a direct Exchange connection. The beta must be published before this npm tag works.
Use the beta CLI explicitly on every invocation: `npx firecrawl-cli@alexandria`. Version `1.23.4-alexandria-beta.1` or newer needs no enable flag. Do not replace the user's stable CLI or use a direct Exchange connection.

Use `FIRECRAWL_API_KEY` or existing Firecrawl login credentials. Never print credentials. The hidden flag is not authorization: the API enforces team and provider access.
Use `FIRECRAWL_API_KEY` or existing Firecrawl login credentials. Never print credentials. Installing the beta is not authorization: the API enforces team and provider access.

## Discover Before Executing

```sh
npx firecrawl-cli@alexandria --enable alexandria search "GDP" --sources alexandria --json
npx firecrawl-cli@alexandria --enable alexandria find-tools --options '{"providers":["fred"]}' --pretty
npx firecrawl-cli@alexandria --enable alexandria find-tools https://example.com --pretty
npx firecrawl-cli@alexandria search "GDP" --json
npx firecrawl-cli@alexandria find-tools --options '{"providers":["fred"]}' --pretty
npx firecrawl-cli@alexandria find-tools https://example.com --pretty
```

For ordinary web results alongside provider tools, use `--sources web,alexandria`. For URL scraping with related tool discovery, use `scrape https://example.com --domain-tools --json` after the same beta prefix.
Search defaults to web results plus Alexandria tools and domain-tool discovery. Inspect both; discovery does not execute the returned provider tools. Use `--sources web` for web-only search or `--sources alexandria` for tool-only discovery. Search itself can consume credits. For URL scraping with related tool discovery, use `scrape https://example.com --domain-tools --json` after the same beta prefix.

Read the returned `data.tools` contracts before choosing a provider/capability. Use their exact input schema, pricing and access requirements; never invent options or assume a provider is free. Follow returned Find Tools requests with `find-tools --request '<returned request JSON>'`. This accepts only the `firecrawl/find-tools` discovery call, not arbitrary provider execution.

Expand All @@ -28,7 +28,7 @@ Obtain approval before paid execution unless the user has already authorized the
Once the discovered contract confirms the capability and options:

```sh
npx firecrawl-cli@alexandria --enable alexandria scrape --alexandria fred/series/observations --options '{"series_id":"GDP"}' --request-id gdp-beta-1 --json
npx firecrawl-cli@alexandria scrape --alexandria fred/series/observations --options '{"series_id":"GDP"}' --request-id gdp-beta-1 --json
```

Choose a new unique request ID for each new logical execution; the ID above is only an example. Preserve the ID printed on stderr and reuse it only for identical retries, including options and call order. For batches, repeat `--alexandria` and pair each call with a positional `--options` object (maximum 10 calls).
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firecrawl-cli",
"version": "1.23.4-alexandria-beta.0",
"version": "1.23.4-alexandria-beta.1",
"publishConfig": {
"tag": "alexandria"
},
Expand Down
56 changes: 20 additions & 36 deletions src/__tests__/alexandria-beta.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,27 @@ async function cli(args: string[], key = 'fc-test') {
}
}

it('keeps beta options out of normal help and refuses use without opt-in', async () => {
it('keeps beta options out of normal help and respects explicit web-only search', async () => {
for (const args of [['--help'], ['search', '--help'], ['scrape', '--help']]) {
const result = await cli(args);
expect(result.code).toBe(0);
expect(result.stdout).not.toMatch(
/alexandria|find-tools|domain-tools|--enable/i
);
}
const result = await cli(['search', 'gdp', '--sources', 'alexandria']);
expect(result.code).toBe(1);
expect(result.stderr).toContain('--enable alexandria');
const setup = await cli(['setup', 'alexandria', '--yes']);
expect(setup.code).toBe(1);
expect(setup.stderr).toContain('--enable alexandria');
expect(requests).toHaveLength(0);
response = { success: true, data: { web: [] } };
const result = await cli([
'search',
'pizza hut',
'--sources',
'web',
'--json',
]);
expect(result.code).toBe(0);
expect(requests[0].body).toMatchObject({
sources: [{ type: 'web' }],
domainTools: false,
});
});

it('preserves mixed search results, tools and billing metadata', async () => {
Expand All @@ -98,16 +104,7 @@ it('preserves mixed search results, tools and billing metadata', async () => {
tools: [{ provider: 'fred', capability: 'series/observations' }],
},
};
const result = await cli([
'--enable',
'alexandria',
'search',
'gdp',
'--sources',
'web,alexandria',
'--domain-tools',
'--json',
]);
const result = await cli(['search', 'pizza hut', '--json']);
expect(result.code).toBe(0);
expect(JSON.parse(result.stdout)).toEqual(response);
expect(requests[0]).toMatchObject({
Expand All @@ -118,12 +115,13 @@ it('preserves mixed search results, tools and billing metadata', async () => {
domainTools: true,
},
});
const readable = await cli(['search', 'pizza hut']);
expect(readable.stdout).toContain('=== Tools ===');
expect(readable.stdout).toContain('series/observations');
});

it('sends provider calls to Scrape with a stable retry ID and preserves the receipt', async () => {
const args = [
'--enable',
'alexandria',
'scrape',
'--alexandria',
'fred/series/observations',
Expand Down Expand Up @@ -167,8 +165,6 @@ it('relays terms refusals and keeps the request ID on failure', async () => {
requiresAction: { url: 'https://firecrawl.dev/terms/provider' },
};
const result = await cli([
'--enable',
'alexandria',
'scrape',
'--alexandria',
'provider/lookup',
Expand All @@ -182,13 +178,7 @@ it('relays terms refusals and keeps the request ID on failure', async () => {
});

it('executes Find Tools through the same API and refuses keyless access', async () => {
const args = [
'--enable',
'alexandria',
'find-tools',
'--options',
'{"providers":["fred"]}',
];
const args = ['find-tools', '--options', '{"providers":["fred"]}'];
expect((await cli(args, '')).code).toBe(1);
expect(requests).toHaveLength(0);
expect((await cli(args)).code).toBe(0);
Expand All @@ -211,13 +201,7 @@ it('keeps URL scrape tool contracts in the output', async () => {
success: true,
data: { markdown: 'Example', tools: [{ provider: 'fred' }] },
};
const result = await cli([
'scrape',
'https://example.com',
'--enable',
'alexandria',
'--domain-tools',
]);
const result = await cli(['scrape', 'https://example.com', '--domain-tools']);
expect(result.code).toBe(0);
expect(JSON.parse(result.stdout)).toEqual(response.data);
expect(requests[0]).toMatchObject({
Expand Down
28 changes: 3 additions & 25 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,23 +326,6 @@ program
// Update global config if API key or URL is provided via global option
const globalOptions = thisCommand.opts();
const commandOptions = actionCommand.opts();
const usesAlexandria =
actionCommand.name() === 'find-tools' ||
(actionCommand.name() === 'setup' &&
actionCommand.args[0] === 'alexandria') ||
commandOptions.domainTools ||
(actionCommand.name() === 'scrape' &&
(commandOptions.alexandria ||
commandOptions.options ||
commandOptions.requestId)) ||
(actionCommand.name() === 'search' &&
commandOptions.sources
?.split(',')
.some(
(source: string) => source.trim().toLowerCase() === 'alexandria'
));
if (usesAlexandria && globalOptions.enable !== 'alexandria')
throw new Error('This beta feature requires --enable alexandria.');
if (globalOptions.apiKey) {
updateConfig({ apiKey: globalOptions.apiKey });
}
Expand Down Expand Up @@ -1017,19 +1000,14 @@ function createSearchCommand(): Command {
.option('--json', 'Output as compact JSON', false)
.action(async (query, options) => {
// Parse sources
let sources: SearchSource[] | undefined;
let sources: SearchSource[] = ['web', 'alexandria'];
if (options.sources) {
sources = options.sources
.split(',')
.map((s: string) => s.trim().toLowerCase()) as SearchSource[];

// Validate sources
const validSources = [
'web',
'images',
'news',
...(program.opts().enable === 'alexandria' ? ['alexandria'] : []),
];
const validSources = ['web', 'images', 'news', 'alexandria'];
for (const source of sources) {
if (!validSources.includes(source)) {
console.error(
Expand Down Expand Up @@ -1069,7 +1047,7 @@ function createSearchCommand(): Command {

const searchOptions = {
query,
domainTools: options.domainTools,
domainTools: options.domainTools ?? sources.includes('alexandria'),
limit: options.limit,
sources,
categories,
Expand Down
Loading