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
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": "0.0.4",
"version": "0.0.5",
"description": "Command-line interface for Firecrawl. Scrape, crawl, and extract data from any website directly from your terminal.",
"main": "dist/index.js",
"bin": {
Expand Down
117 changes: 78 additions & 39 deletions src/commands/scrape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,69 +7,108 @@ import type { ScrapeOptions, ScrapeResult } from '../types/scrape';
import { getClient } from '../utils/client';
import { handleScrapeOutput } from '../utils/output';

/**
* Output timing information if requested
*/
function outputTiming(
options: ScrapeOptions,
requestStartTime: number,
requestEndTime: number,
error?: Error | unknown
): void {
if (!options.timing) return;

const requestDuration = requestEndTime - requestStartTime;
const timingInfo: {
url: string;
requestTime: string;
duration: string;
status: 'success' | 'error';
error?: string;
} = {
url: options.url,
requestTime: new Date(requestStartTime).toISOString(),
duration: `${requestDuration}ms`,
status: error ? 'error' : 'success',
};

if (error) {
timingInfo.error = error instanceof Error ? error.message : 'Unknown error';
}

console.error('Timing:', JSON.stringify(timingInfo, null, 2));
}

/**
* Execute the scrape command
*/
export async function executeScrape(
options: ScrapeOptions
): Promise<ScrapeResult> {
try {
// Get client instance (updates global config if apiKey provided)
const app = getClient({ apiKey: options.apiKey });
// Get client instance (updates global config if apiKey provided)
const app = getClient({ apiKey: options.apiKey });

// Build scrape options
const formats: FormatOption[] = [];
// Build scrape options
const formats: FormatOption[] = [];

if (options.format) {
formats.push(options.format);
}
if (options.format) {
formats.push(options.format);
}

if (options.screenshot) {
// Add screenshot format if not already included
if (!formats.includes('screenshot')) {
formats.push('screenshot');
}
if (options.screenshot) {
// Add screenshot format if not already included
if (!formats.includes('screenshot')) {
formats.push('screenshot');
}
}

// If no formats specified, default to markdown
if (formats.length === 0) {
formats.push('markdown');
}
// If no formats specified, default to markdown
if (formats.length === 0) {
formats.push('markdown');
}

const scrapeParams: {
formats?: FormatOption[];
onlyMainContent?: boolean;
waitFor?: number;
includeTags?: string[];
excludeTags?: string[];
} = {
formats,
};
const scrapeParams: {
formats?: FormatOption[];
onlyMainContent?: boolean;
waitFor?: number;
includeTags?: string[];
excludeTags?: string[];
} = {
formats,
};

if (options.onlyMainContent !== undefined) {
scrapeParams.onlyMainContent = options.onlyMainContent;
}
if (options.onlyMainContent !== undefined) {
scrapeParams.onlyMainContent = options.onlyMainContent;
}

if (options.waitFor !== undefined) {
scrapeParams.waitFor = options.waitFor;
}
if (options.waitFor !== undefined) {
scrapeParams.waitFor = options.waitFor;
}

if (options.includeTags && options.includeTags.length > 0) {
scrapeParams.includeTags = options.includeTags;
}
if (options.includeTags && options.includeTags.length > 0) {
scrapeParams.includeTags = options.includeTags;
}

if (options.excludeTags && options.excludeTags.length > 0) {
scrapeParams.excludeTags = options.excludeTags;
}
if (options.excludeTags && options.excludeTags.length > 0) {
scrapeParams.excludeTags = options.excludeTags;
}

// Execute scrape
// Execute scrape with timing - only wrap the scrape call in try-catch
const requestStartTime = Date.now();

try {
const result = await app.scrape(options.url, scrapeParams);
const requestEndTime = Date.now();
outputTiming(options, requestStartTime, requestEndTime);

return {
success: true,
data: result,
};
} catch (error) {
const requestEndTime = Date.now();
outputTiming(options, requestStartTime, requestEndTime, error);

return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ function createScrapeCommand(): Command {
)
.option('-o, --output <path>', 'Output file path (default: stdout)')
.option('--pretty', 'Pretty print JSON output', false)
.option(
'--timing',
'Show request timing and other useful information',
false
)
.action(async (positionalUrl, options) => {
// Use positional URL if provided, otherwise use --url option
const url = positionalUrl || options.url;
Expand Down
2 changes: 2 additions & 0 deletions src/types/scrape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export interface ScrapeOptions {
output?: string;
/** Pretty print JSON output */
pretty?: boolean;
/** Show request timing and other useful information */
timing?: boolean;
}

export interface ScrapeResult {
Expand Down
1 change: 1 addition & 0 deletions src/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ export function parseScrapeOptions(options: any): ScrapeOptions {
apiKey: options.apiKey,
output: options.output,
pretty: options.pretty,
timing: options.timing,
};
}