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": "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": {
Expand Down
80 changes: 65 additions & 15 deletions skills/firecrawl-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**

```
Expand Down Expand Up @@ -227,7 +245,7 @@ Cloud Chromium sessions in Firecrawl's remote sandboxed environment. Run `firecr
```bash
# Typical browser workflow
firecrawl browser "open <url>"
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
Expand All @@ -238,22 +256,54 @@ Shorthand auto-launches a session if none exists - no setup required.

**Core agent-browser commands:**

| Command | Description |
| -------------------- | -------------------------------------- |
| `open <url>` | 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> <text>` | Type into an element |
| `fill <@ref> <text>` | Fill a form field (clears first) |
| `scrape` | Extract page content as markdown |
| `scroll <direction>` | Scroll up/down/left/right |
| `wait <seconds>` | Wait for a duration |
| `eval <js>` | Evaluate JavaScript on the page |
| Command | Description |
| -------------------- | ---------------------------------------- |
| `open <url>` | 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> <text>` | Type into an element |
| `fill <@ref> <text>` | Fill a form field (clears first) |
| `scrape` | Extract page content as markdown |
| `scroll <direction>` | Scroll up/down/left/right |
| `wait <seconds>` | Wait for a duration |
| `eval <js>` | Evaluate JavaScript on the page |

Session management: `launch-session --ttl 600`, `list`, `close`

Options: `--ttl <seconds>`, `--ttl-inactivity <seconds>`, `--session <id>`, `-o`
Options: `--ttl <seconds>`, `--ttl-inactivity <seconds>`, `--session <id>`, `--profile <name>`, `--write-mode <readonly|readwrite>`, `-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

Expand Down
34 changes: 32 additions & 2 deletions src/commands/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -50,6 +52,8 @@ export interface BrowserCloseOptions {

export interface BrowserQuickExecuteOptions {
code: string;
profile?: string;
writeMode?: 'readonly' | 'readwrite';
apiKey?: string;
apiUrl?: string;
output?: string;
Expand All @@ -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<typeof app.browser>[0]);

if (!data.success) {
console.error('Error:', data.error || 'Unknown error');
Expand Down Expand Up @@ -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<typeof app.browser>[0]
);

if (!data.success) {
console.error('Error:', data.error || 'Failed to launch session');
Expand Down
38 changes: 38 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,12 +736,31 @@ function createBrowserCommand(): Command {
'Firecrawl API key (overrides global --api-key)'
)
.option('--api-url <url>', 'API URL (overrides global --api-url)')
.option(
'--profile <name>',
'Name for a profile (survives close, reconnect by name later)'
)
.option(
'--write-mode <mode>',
'Write mode for profile: readonly or readwrite (default: readwrite)'
)
.option('-o, --output <path>', '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,
Expand Down Expand Up @@ -785,6 +804,14 @@ Explicit subcommands:
parseInt
)
.option('--ttl-inactivity <seconds>', 'Inactivity TTL in seconds', parseInt)
.option(
'--profile <name>',
'Name for a profile (survives close, reconnect by name later)'
)
.option(
'--write-mode <mode>',
'Write mode for profile: readonly or readwrite (default: readwrite)'
)
.option(
'-k, --api-key <key>',
'Firecrawl API key (overrides global --api-key)'
Expand All @@ -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,
Expand Down