diff --git a/package.json b/package.json index 1b64f58669..acaf7cba2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firecrawl-cli", - "version": "1.7.1", + "version": "1.8.0", "description": "Command-line interface for Firecrawl. Scrape, crawl, and extract data from any website directly from your terminal.", "main": "dist/index.js", "bin": { diff --git a/skills/firecrawl-cli/SKILL.md b/skills/firecrawl-cli/SKILL.md index 486a3a8fec..308696efb8 100644 --- a/skills/firecrawl-cli/SKILL.md +++ b/skills/firecrawl-cli/SKILL.md @@ -87,11 +87,29 @@ scrape https://docs.example.com/docs/api/auth... → got the content ``` scrape https://example.com/products → only shows first 10 items, no next-page links browser "open https://example.com/products" → open in browser -browser "snapshot" → find the pagination button +browser "snapshot -i" → find the pagination button browser "click @e12" → click "Next Page" browser "scrape" -o .firecrawl/products-p2.md → extract page 2 content ``` +**Example: login then scrape authenticated content** + +``` +browser launch-session --profile my-app → create a named persistent session +browser "open https://app.example.com/login" → navigate to login +browser "snapshot -i" → find form fields +browser "fill @e3 'user@example.com'" → fill email +browser "fill @e5 'password'" → fill password +browser "click @e7" → click Login +browser "wait 2" → wait for redirect +browser close → disconnect, state persisted + +browser launch-session --profile my-app → reconnect, cookies intact +browser "open https://app.example.com/dashboard" → already logged in +browser "scrape" -o .firecrawl/dashboard.md → extract authenticated content +browser close +``` + **Example: research task** ``` @@ -227,7 +245,7 @@ Cloud Chromium sessions in Firecrawl's remote sandboxed environment. Run `firecr ```bash # Typical browser workflow firecrawl browser "open " -firecrawl browser "snapshot" # see the page structure with @ref IDs +firecrawl browser "snapshot -i" # see interactive elements with @ref IDs firecrawl browser "click @e5" # interact with elements firecrawl browser "fill @e3 'search query'" # fill form fields firecrawl browser "scrape" -o .firecrawl/page.md # extract content @@ -238,22 +256,54 @@ Shorthand auto-launches a session if none exists - no setup required. **Core agent-browser commands:** -| Command | Description | -| -------------------- | -------------------------------------- | -| `open ` | Navigate to a URL | -| `snapshot` | Get accessibility tree with `@ref` IDs | -| `screenshot` | Capture a PNG screenshot | -| `click <@ref>` | Click an element by ref | -| `type <@ref> ` | Type into an element | -| `fill <@ref> ` | Fill a form field (clears first) | -| `scrape` | Extract page content as markdown | -| `scroll ` | Scroll up/down/left/right | -| `wait ` | Wait for a duration | -| `eval ` | Evaluate JavaScript on the page | +| Command | Description | +| -------------------- | ---------------------------------------- | +| `open ` | Navigate to a URL | +| `snapshot -i` | Get interactive elements with `@ref` IDs | +| `screenshot` | Capture a PNG screenshot | +| `click <@ref>` | Click an element by ref | +| `type <@ref> ` | Type into an element | +| `fill <@ref> ` | Fill a form field (clears first) | +| `scrape` | Extract page content as markdown | +| `scroll ` | Scroll up/down/left/right | +| `wait ` | Wait for a duration | +| `eval ` | Evaluate JavaScript on the page | Session management: `launch-session --ttl 600`, `list`, `close` -Options: `--ttl `, `--ttl-inactivity `, `--session `, `-o` +Options: `--ttl `, `--ttl-inactivity `, `--session `, `--profile `, `--write-mode `, `-o` + +**Persistent sessions** survive close and can be reconnected by name. Use them when you need to login first, then come back later to do work while already authenticated: + +```bash +# Session 1: Login and save state +firecrawl browser launch-session --profile my-app +firecrawl browser "open https://app.example.com/login" +firecrawl browser "snapshot -i" +firecrawl browser "fill @e3 'user@example.com'" +firecrawl browser "fill @e5 'password123'" +firecrawl browser "click @e7" +firecrawl browser "wait 2" +firecrawl browser close + +# Session 2: Come back authenticated +firecrawl browser launch-session --profile my-app +firecrawl browser "open https://app.example.com/dashboard" +firecrawl browser "scrape" -o .firecrawl/dashboard.md +firecrawl browser close +``` + +Read-only reconnect (no writes to session state): + +```bash +firecrawl browser launch-session --profile my-app --write-mode readonly +``` + +Shorthand with persistent session: + +```bash +firecrawl browser --profile my-app "open https://example.com" +``` ### credit-usage diff --git a/src/commands/browser.ts b/src/commands/browser.ts index 0ef6c95902..7ced874930 100644 --- a/src/commands/browser.ts +++ b/src/commands/browser.ts @@ -16,6 +16,8 @@ import { writeOutput } from '../utils/output'; export interface BrowserLaunchOptions { ttl?: number; ttlInactivity?: number; + profile?: string; + writeMode?: 'readonly' | 'readwrite'; apiKey?: string; apiUrl?: string; output?: string; @@ -50,6 +52,8 @@ export interface BrowserCloseOptions { export interface BrowserQuickExecuteOptions { code: string; + profile?: string; + writeMode?: 'readonly' | 'readwrite'; apiKey?: string; apiUrl?: string; output?: string; @@ -68,12 +72,22 @@ export async function handleBrowserLaunch( const args: { ttl?: number; activityTtl?: number; + profile?: { + name: string; + writeMode?: 'readonly' | 'readwrite'; + }; } = {}; if (options.ttl !== undefined) args.ttl = options.ttl; if (options.ttlInactivity !== undefined) args.activityTtl = options.ttlInactivity; + if (options.profile) { + args.profile = { + name: options.profile, + writeMode: options.writeMode, + }; + } - const data = await app.browser(args); + const data = await app.browser(args as Parameters[0]); if (!data.success) { console.error('Error:', data.error || 'Unknown error'); @@ -273,7 +287,23 @@ export async function handleBrowserQuickExecute( if (!existing) { try { const app = getClient({ apiKey: options.apiKey, apiUrl: options.apiUrl }); - const data = await app.browser({}); + + const launchArgs: { + profile?: { + name: string; + writeMode?: 'readonly' | 'readwrite'; + }; + } = {}; + if (options.profile) { + launchArgs.profile = { + name: options.profile, + writeMode: options.writeMode, + }; + } + + const data = await app.browser( + launchArgs as Parameters[0] + ); if (!data.success) { console.error('Error:', data.error || 'Failed to launch session'); diff --git a/src/index.ts b/src/index.ts index d262e01aa1..7ef8b1a6fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -736,12 +736,31 @@ function createBrowserCommand(): Command { 'Firecrawl API key (overrides global --api-key)' ) .option('--api-url ', 'API URL (overrides global --api-url)') + .option( + '--profile ', + 'Name for a profile (survives close, reconnect by name later)' + ) + .option( + '--write-mode ', + 'Write mode for profile: readonly or readwrite (default: readwrite)' + ) .option('-o, --output ', 'Output file path (default: stdout)') .option('--json', 'Output as JSON format', false) .action(async (code, options) => { if (code) { + if ( + options.writeMode && + !['readonly', 'readwrite'].includes(options.writeMode) + ) { + console.error( + 'Error: --write-mode must be "readonly" or "readwrite"' + ); + process.exit(1); + } await handleBrowserQuickExecute({ code, + profile: options.profile, + writeMode: options.writeMode, apiKey: options.apiKey, apiUrl: options.apiUrl, output: options.output, @@ -785,6 +804,14 @@ Explicit subcommands: parseInt ) .option('--ttl-inactivity ', 'Inactivity TTL in seconds', parseInt) + .option( + '--profile ', + 'Name for a profile (survives close, reconnect by name later)' + ) + .option( + '--write-mode ', + 'Write mode for profile: readonly or readwrite (default: readwrite)' + ) .option( '-k, --api-key ', 'Firecrawl API key (overrides global --api-key)' @@ -806,13 +833,24 @@ Examples: $ firecrawl browser launch-session $ firecrawl browser launch-session --ttl 600 $ firecrawl browser launch-session --ttl 300 --ttl-inactivity 60 + $ firecrawl browser launch-session --profile my-session + $ firecrawl browser launch-session --profile my-session --write-mode readonly $ firecrawl browser launch-session -o session.json --json ` ) .action(async (options) => { + if ( + options.writeMode && + !['readonly', 'readwrite'].includes(options.writeMode) + ) { + console.error('Error: --write-mode must be "readonly" or "readwrite"'); + process.exit(1); + } await handleBrowserLaunch({ ttl: options.ttl, ttlInactivity: options.ttlInactivity, + profile: options.profile, + writeMode: options.writeMode, apiKey: options.apiKey, apiUrl: options.apiUrl, output: options.output,