|
1 | 1 | /** |
2 | 2 | * `firecrawl create` command — scaffolds Firecrawl starter projects. |
3 | 3 | * |
4 | | - * Hidden from --help until `firecrawl-agent-cli` is published to npm. |
| 4 | + * Hidden from --help until the flow is battle-tested in the wild. The |
| 5 | + * scaffolder is vendored under `src/utils/agent-scaffold/` (mirrored from |
| 6 | + * `firecrawl/firecrawl-agent`). At runtime it clones the public agent repo |
| 7 | + * to get templates — no separate npm package for the agent CLI. |
| 8 | + * |
5 | 9 | * Once visible, the command tree will grow to include additional kinds |
6 | 10 | * (scrape, browser, ai, app). For now, `agent` is the only kind. |
7 | | - * |
8 | | - * Implementation is a thin delegator: `firecrawl create agent ...` execs |
9 | | - * `npx -y firecrawl-agent-cli create ...` and passes all flags through. |
10 | | - * This avoids vendoring the scaffold code in the root CLI; the agent repo |
11 | | - * remains the single source of truth for templates and the manifest. |
12 | 11 | */ |
13 | 12 |
|
14 | 13 | import { Command } from 'commander'; |
15 | | -import { spawn } from 'child_process'; |
16 | | - |
17 | | -/** npm package name of the Firecrawl Agent CLI (bin: `firecrawl-agent`). */ |
18 | | -const AGENT_CLI_PACKAGE = 'firecrawl-agent-cli'; |
19 | | - |
20 | | -/** |
21 | | - * Execute `npx -y <AGENT_CLI_PACKAGE> create ...` with inherited stdio so |
22 | | - * the agent CLI's interactive prompts render in the user's terminal. |
23 | | - * Resolves with the child exit code; callers forward it to `process.exit`. |
24 | | - */ |
25 | | -function runAgentCli(args: string[]): Promise<number> { |
26 | | - const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx'; |
27 | | - return new Promise((resolve) => { |
28 | | - const child = spawn(npx, ['-y', AGENT_CLI_PACKAGE, 'create', ...args], { |
29 | | - stdio: 'inherit', |
30 | | - env: process.env, |
31 | | - }); |
32 | | - child.on('exit', (code) => resolve(code ?? 1)); |
33 | | - child.on('error', (err) => { |
34 | | - console.error( |
35 | | - `\nFailed to launch ${AGENT_CLI_PACKAGE} via npx:`, |
36 | | - err.message |
37 | | - ); |
38 | | - console.error( |
39 | | - `\n Install it directly and retry: npm install -g ${AGENT_CLI_PACKAGE}\n` |
40 | | - ); |
41 | | - resolve(1); |
42 | | - }); |
43 | | - }); |
44 | | -} |
| 14 | +import type { CreateOptions } from '../utils/agent-scaffold/create-flow'; |
45 | 15 |
|
46 | 16 | function collect(val: string, acc: string[]): string[] { |
47 | 17 | acc.push(val); |
48 | 18 | return acc; |
49 | 19 | } |
50 | 20 |
|
51 | 21 | /** |
52 | | - * Build the `agent` subcommand. Flag surface mirrors `firecrawl-agent create` |
53 | | - * exactly — anything the downstream CLI accepts is passed through verbatim. |
| 22 | + * Build the `agent` subcommand. Flag surface mirrors the upstream agent CLI. |
| 23 | + * The scaffold flow itself lives in `utils/agent-scaffold/create-flow.ts`. |
54 | 24 | */ |
55 | 25 | function createAgentSubcommand(): Command { |
56 | 26 | return new Command('agent') |
@@ -88,48 +58,28 @@ function createAgentSubcommand(): Command { |
88 | 58 | [] |
89 | 59 | ) |
90 | 60 | .option('--skip-install', 'Skip npm install') |
91 | | - .allowUnknownOption() // Forward future flags without requiring a CLI update |
92 | 61 | .action( |
93 | 62 | async ( |
94 | 63 | projectName: string | undefined, |
95 | | - options: Record<string, unknown>, |
96 | | - cmd: Command |
| 64 | + options: CreateOptions & Record<string, unknown> |
97 | 65 | ) => { |
98 | | - const args: string[] = []; |
99 | | - if (projectName) args.push(projectName); |
100 | | - |
101 | | - // Pass through known options. Commander camelCases hyphenated flags, |
102 | | - // so we map back to the CLI-facing kebab-case form. |
103 | | - const flagMap: Array<[string, string]> = [ |
104 | | - ['template', '-t'], |
105 | | - ['provider', '--provider'], |
106 | | - ['model', '--model'], |
107 | | - ['subAgentProvider', '--sub-agent-provider'], |
108 | | - ['subAgentModel', '--sub-agent-model'], |
109 | | - ['from', '--from'], |
110 | | - ['apiKey', '--api-key'], |
111 | | - ]; |
112 | | - for (const [optKey, flag] of flagMap) { |
113 | | - const val = options[optKey]; |
114 | | - if (typeof val === 'string' && val.length > 0) args.push(flag, val); |
115 | | - } |
116 | | - |
117 | | - // --key is repeatable |
118 | | - const keys = options.key; |
119 | | - if (Array.isArray(keys)) { |
120 | | - for (const k of keys) { |
121 | | - if (typeof k === 'string' && k.length > 0) args.push('--key', k); |
122 | | - } |
123 | | - } |
124 | | - |
125 | | - if (options.skipInstall) args.push('--skip-install'); |
126 | | - |
127 | | - // Forward any unknown/forward-compatible options verbatim. |
128 | | - const passthrough = cmd.args.slice(projectName ? 1 : 0); |
129 | | - for (const extra of passthrough) args.push(extra); |
130 | | - |
131 | | - const code = await runAgentCli(args); |
132 | | - if (code !== 0) process.exit(code); |
| 66 | + // Lazy-load the scaffolder so startup cost (inquirer, git) only hits |
| 67 | + // users who actually invoke `create`. Keeps the rest of the CLI snappy. |
| 68 | + const { handleCreate } = |
| 69 | + await import('../utils/agent-scaffold/create-flow'); |
| 70 | + await handleCreate(projectName, { |
| 71 | + template: options.template, |
| 72 | + provider: options.provider, |
| 73 | + model: options.model, |
| 74 | + subAgentProvider: options.subAgentProvider, |
| 75 | + subAgentModel: options.subAgentModel, |
| 76 | + from: options.from, |
| 77 | + apiKey: options.apiKey, |
| 78 | + key: Array.isArray(options.key) |
| 79 | + ? (options.key as string[]) |
| 80 | + : undefined, |
| 81 | + skipInstall: options.skipInstall as boolean | undefined, |
| 82 | + }); |
133 | 83 | } |
134 | 84 | ); |
135 | 85 | } |
|
0 commit comments