From 0fcb802bb26a3bd3faa01bcb8a9b5fd688b1d19a Mon Sep 17 00:00:00 2001 From: Alex Godoroja Date: Mon, 3 Aug 2026 16:12:04 -0700 Subject: [PATCH 1/2] app-store: add Firecrawl (io.pilot.firecrawl v0.1.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Card on /app-store plus the generated app page at /apps/io.pilot.firecrawl and its plain twin. Data comes from the catalogue snapshot shipped in pilot-protocol/pilotprotocol#447, so the page matches what installs. - 51 methods (the full Firecrawl v2 API surface + help + balance), with threat_protection_update marked gated - product demo carried through to the page and its plain twin - icon: Firecrawl's flame mark, orange on near-black so it reads on both themes apps.ts regenerated: 487 insertions, 0 deletions — no churn to other apps. Co-Authored-By: Claude Opus 5 (1M context) --- public/appicons/io.pilot.firecrawl.png | Bin 0 -> 18421 bytes scripts/gen-apps.mjs | 5 +- src/data/app-demos.json | 98 ++++- src/data/app-methods.json | 310 +++++++++++++++- src/data/app-overrides.json | 54 ++- src/data/apps.ts | 487 +++++++++++++++++++++++++ 6 files changed, 949 insertions(+), 5 deletions(-) create mode 100644 public/appicons/io.pilot.firecrawl.png diff --git a/public/appicons/io.pilot.firecrawl.png b/public/appicons/io.pilot.firecrawl.png new file mode 100644 index 0000000000000000000000000000000000000000..f0e268bf0bb54f7d412ab4a067af5960d6589f31 GIT binary patch literal 18421 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 [c.id, c.hue])); const CATMAP = { 'io.pilot.postgres': 'data', 'io.pilot.duckdb': 'data', 'io.pilot.sqlite': 'data', 'io.pilot.mysql': 'data', 'io.pilot.redis': 'data', 'io.pilot.sixtyfour': 'data', 'io.pilot.orthogonal': 'data', 'io.pilot.cosift': 'ai', 'io.telepat.ideon-free': 'ai', - 'io.pilot.plainweb': 'web', 'io.pilot.otto': 'web', 'io.pilot.bowmark': 'web', + 'io.pilot.plainweb': 'web', 'io.pilot.otto': 'web', 'io.pilot.bowmark': 'web', 'io.pilot.firecrawl': 'web', 'io.pilot.smol': 'infra', 'io.pilot.miren': 'infra', 'io.pilot.docker': 'infra', 'io.pilot.tldr': 'infra', 'io.pilot.aegis': 'security', 'io.pilot.didit': 'security', 'io.pilot.slipstream': 'finance', 'io.pilot.wallet': 'finance', @@ -69,6 +69,7 @@ const ICON_MAP = { 'io.pilot.orthogonal': { image: 'png', fit: 'cover', bg: '#e3e6df' }, 'io.pilot.agentphone': { image: 'png', fit: 'contain', bg: '#26B65A' }, 'io.pilot.primitive': { image: 'png', fit: 'cover', bg: '#111110' }, + 'io.pilot.firecrawl': { image: 'png', fit: 'contain', bg: '#0b0b0a' }, }; function relLum(hex) { @@ -96,7 +97,7 @@ function iconFor(id, hue) { const APP_IDS = [ 'io.pilot.primitive', 'io.pilot.agentphone', 'io.pilot.postgres', 'io.pilot.duckdb', 'io.pilot.sqlite', 'io.pilot.mysql', 'io.pilot.redis', 'io.pilot.sixtyfour', - 'io.pilot.cosift', 'io.telepat.ideon-free', 'io.pilot.plainweb', 'io.pilot.otto', + 'io.pilot.cosift', 'io.telepat.ideon-free', 'io.pilot.plainweb', 'io.pilot.otto', 'io.pilot.firecrawl', 'io.pilot.smol', 'io.pilot.miren', 'io.pilot.docker', 'io.pilot.aegis', 'io.pilot.slipstream', 'io.pilot.wallet', 'io.pilot.bowmark', 'io.pilot.orthogonal', 'io.pilot.didit', 'io.pilot.tldr', ]; diff --git a/src/data/app-demos.json b/src/data/app-demos.json index 5bad29a..6bde157 100644 --- a/src/data/app-demos.json +++ b/src/data/app-demos.json @@ -1045,5 +1045,101 @@ "next": [ "io.telepat.ideon-free ideon-free.help '{}'" ] + }, + "io.pilot.firecrawl": { + "skill": "io.pilot.firecrawl", + "title": "Full usage demo", + "when_to_use": "When you need content from the live web: read a page as clean markdown, search the web, enumerate a site's URLs, crawl a whole domain, or extract structured data from pages.", + "metered": true, + "quickstart": { + "goal": "Read any page as clean, LLM-ready markdown", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.scrape '{\"url\":\"https://example.com\"}'", + "expect": "{\"success\":true,\"data\":{\"markdown\":\"# Example Domain\\n\\nThis domain is for use in documentation examples...\",\"metadata\":{\"title\":\"Example Domain\",\"creditsUsed\":1}}}", + "cost": "1 credit (~$0.0008)" + }, + "examples": [ + { + "title": "Check your budget first (free read)", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.balance '{}'", + "expect": "{\"balance\":\"$0.04\",\"credits_remaining\":41500,\"credits_seed\":41500,\"unit\":\"micro_usd\",\"scope\":\"per-pilot-user\"}", + "cost": "$0.00 (read)" + }, + { + "title": "Search the web and get real content back", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.search '{\"query\":\"open source web crawlers\",\"limit\":3}'", + "expect": "{\"success\":true,\"data\":{\"web\":[{\"url\":\"https://...\",\"title\":\"...\",\"description\":\"...\"}]},\"id\":\"019fc9b8-...\"}", + "cost": "2 credits (~$0.0017)" + }, + { + "title": "Enumerate a site's URLs before paying to crawl it", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.map '{\"url\":\"https://docs.firecrawl.dev\",\"limit\":50}'", + "expect": "{\"success\":true,\"links\":[{\"url\":\"https://docs.firecrawl.dev/...\",\"title\":\"...\"}]}", + "cost": "1 credit (~$0.0008)", + "note": "Always map before a crawl: crawl defaults to 10000 pages." + }, + { + "title": "Crawl a site — async, returns a job id", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.crawl '{\"url\":\"https://docs.firecrawl.dev\",\"limit\":10}'", + "expect": "{\"success\":true,\"id\":\"a1b2c3d4-...\",\"url\":\"https://api.firecrawl.dev/v2/crawl/a1b2c3d4-...\"}", + "cost": "20 credits (~$0.0166)", + "note": "SET limit — it defaults to 10000 pages." + }, + { + "title": "Poll the crawl until it finishes (free read)", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.crawl_status '{\"id\":\"a1b2c3d4-...\"}'", + "expect": "{\"status\":\"completed\",\"total\":10,\"completed\":10,\"creditsUsed\":10,\"data\":[{\"markdown\":\"...\",\"metadata\":{}}]}", + "cost": "$0.00 (read)" + } + ], + "cost": { + "unit": "micro-USD (1000000 = $1.00); 1 Firecrawl credit ~= 830 micro-USD", + "free_budget": "$0.0415 per Pilot user (~50 Firecrawl credits)", + "hard_cap_usd": 0.0415, + "operations": [ + { + "op": "scrape / map / docs_search", + "price": "$0.0008", + "note": "1 credit" + }, + { + "op": "search", + "price": "$0.0017", + "note": "2 credits" + }, + { + "op": "interact_create / monitor_create", + "price": "$0.0042", + "note": "5 credits" + }, + { + "op": "crawl / batch_scrape / extract", + "price": "$0.0166", + "note": "20 credits at enqueue; a big crawl costs more upstream" + }, + { + "op": "agent", + "price": "$0.0415", + "note": "50 credits — your whole budget in one call" + }, + { + "op": "all *_status, *_errors, list, team reads", + "price": "$0.00", + "note": "reads are free" + } + ], + "worked_total": "This demo spends $0.0191 of your $0.0415 budget (search + map + crawl + the quickstart scrape; both reads are free).", + "check_balance": "pilotctl appstore call io.pilot.firecrawl firecrawl.balance '{}'" + }, + "gotchas": [ + "crawl defaults to limit 10000 and agent to maxCredits 2500 — always set them.", + "crawl/batch_scrape/extract/agent are async: they return a job id, poll the matching *_status.", + "402 = your budget is spent; reads and status polls still work.", + "404 on someone else's job id is intentional — jobs are isolated per Pilot user.", + "firecrawl.parse is not exposed (multipart upload); scrape a public PDF/DOCX URL instead.", + "Results cache up to 48h; pass maxAge:0 to force a fresh fetch." + ], + "next": [ + "io.pilot.firecrawl firecrawl.help '{}'" + ] } -} +} \ No newline at end of file diff --git a/src/data/app-methods.json b/src/data/app-methods.json index f9bc77c..6bcc818 100644 --- a/src/data/app-methods.json +++ b/src/data/app-methods.json @@ -1814,5 +1814,313 @@ "name": "tldr.version", "summary": "Print the delivered client and tldr-client-spec version, e.g. \"tlrc v1.13.1 (implementing the tldr client specification v2.3)\". Needs no cache. This is `tldr --version`." } + ], + "io.pilot.firecrawl": [ + { + "name": "firecrawl.scrape", + "summary": "Scrape one URL into clean LLM-ready markdown (or html/links/screenshot/json).", + "example": null, + "gated": null + }, + { + "name": "firecrawl.scrape_status", + "summary": "Get the status and result of a previously started scrape job by id.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.scrape_interact", + "summary": "Run code or an AI prompt in the live browser session bound to a scrape job.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.scrape_interact_stop", + "summary": "Stop the interactive browser session attached to a scrape job and free its resources.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.search", + "summary": "Search the web (and optionally scrape every result in one call).", + "example": null, + "gated": null + }, + { + "name": "firecrawl.search_feedback", + "summary": "Rate a search job's quality to improve future Firecrawl search results.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.map", + "summary": "Discover every URL on a domain, fast — the cheap way to scope a crawl.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl", + "summary": "Start an async crawl of an entire site. Returns a job id — poll firecrawl.crawl_status.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_status", + "summary": "Poll a crawl job: status, completed/total counts, credits used, and the scraped pages.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_cancel", + "summary": "Cancel a running crawl job.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_errors", + "summary": "List the pages a crawl job failed on, plus URLs blocked by robots.txt.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_active", + "summary": "List every crawl currently running for your account.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_params_preview", + "summary": "Dry-run: see the crawl options a natural-language prompt would generate, without spending credits.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.batch_scrape", + "summary": "Scrape many known URLs in one async job. Returns a job id — poll firecrawl.batch_scrape_status.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.batch_scrape_status", + "summary": "Poll a batch scrape job: progress, credits used, and the scraped pages.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.batch_scrape_cancel", + "summary": "Cancel a running batch scrape job.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.batch_scrape_errors", + "summary": "List the URLs a batch scrape job failed on, with per-URL error detail.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.extract", + "summary": "Extract structured JSON from one or many pages using an LLM and your schema. Async — poll firecrawl.extract_status.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.extract_status", + "summary": "Poll an extract job and retrieve the structured data once it completes.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.agent", + "summary": "Autonomous web research: give a prompt, the agent decides what to visit and returns structured data. Async — poll firecrawl.agent_status.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.agent_status", + "summary": "Poll an agent job and retrieve its result once it completes.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.agent_cancel", + "summary": "Cancel a running agent job and stop it spending credits.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.interact_create", + "summary": "Start a standalone browser session you drive with code — no prior scrape needed.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.interact_list", + "summary": "List your standalone interact browser sessions.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.interact_execute", + "summary": "Run Playwright or agent-browser code inside a standalone interact session.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.interact_delete", + "summary": "Destroy a standalone interact session and release its browser.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_create", + "summary": "Create a recurring check that watches pages, a whole site, or search results and alerts on meaningful change.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_list", + "summary": "List your monitors.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_get", + "summary": "Get one monitor's full configuration.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_update", + "summary": "Update a monitor's schedule, targets, goal, or status (active/paused).", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_delete", + "summary": "Delete a monitor and stop its recurring checks.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_run", + "summary": "Trigger a monitor check immediately instead of waiting for its schedule.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_checks", + "summary": "List a monitor's past checks and what changed in each.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_check", + "summary": "Get one monitor check in full, with page-level diffs.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.research_papers", + "summary": "Search a purpose-built scientific paper index by natural-language query.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.research_paper", + "summary": "Inspect a paper's metadata, or pass query to read its top matching full-text passages.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.research_related", + "summary": "Expand a seed paper to related work — citers, references, or semantically similar papers.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.research_github", + "summary": "Search GitHub issues, pull requests, discussions, and READMEs by natural-language query.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.ask", + "summary": "Diagnose a failing Firecrawl call with an AI support agent — pass the jobId and get a fix.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.docs_search", + "summary": "Answer a \"how does Firecrawl do X?\" question from the official docs, with citations.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.feedback", + "summary": "Submit quality feedback for a completed search, scrape, parse, or map job.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.credit_usage", + "summary": "Remaining credits and the current billing period.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.credit_usage_historical", + "summary": "Historical credit usage over time, optionally broken down by API key.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.token_usage", + "summary": "Remaining LLM tokens for extract-family operations.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.token_usage_historical", + "summary": "Historical token usage over time, optionally broken down by API key.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.queue_status", + "summary": "Your scrape queue depth, active jobs, and max concurrency — check before a large batch.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.activity", + "summary": "Recent API activity (last 24h) with job ids you can feed to the *_status methods.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.threat_protection", + "summary": "Read the account's threat-protection policy (URL risk checks, allow/deny lists).", + "example": null, + "gated": null + }, + { + "name": "firecrawl.threat_protection_update", + "summary": "Replace the account's threat-protection policy. Full-document update — unspecified fields reset to defaults.", + "example": null, + "gated": "Disabled on the shared-key plan. Threat-protection policy is TEAM-WIDE, so on the current managed key one caller's update would change the policy for every Pilot user; the broker refuses this verb. It unlocks when Firecrawl issues per-user keys. Upstream it is also enterprise-only and team-admin-only." + }, + { + "name": "firecrawl.balance", + "summary": "Your remaining Pilot budget for this app, read free from the broker's per-user credit ledger — returns {\"balance\":\"$X.XX\",\"credits_remaining\":,\"credits_seed\":,\"unit\":\"micro_usd\",\"scope\":\"per-pilot-user\"}. This is YOUR budget, not the shared account's. No partner API call, no charge, and never a 402. The same figure also rides on the X-Pilot-Credits-Remaining header of every metered response; call this when you just want to check what's left before a spend.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.help", + "summary": "Discovery: every method with params, kind, and latency class.", + "example": null, + "gated": null + } ] -} +} \ No newline at end of file diff --git a/src/data/app-overrides.json b/src/data/app-overrides.json index d7e55ec..7bf7aca 100644 --- a/src/data/app-overrides.json +++ b/src/data/app-overrides.json @@ -1122,5 +1122,57 @@ "publishedAt": null, "grants": [], "inCatalogue": true + }, + "io.pilot.firecrawl": { + "name": "Firecrawl", + "tagline": "Turn any website into clean, LLM-ready data", + "description": "**Firecrawl** turns any website into clean, LLM-ready data. This app exposes the\n**complete Firecrawl v2 API** — all 50 operations — as Pilot app-store methods,\ngenerated 1:1 from Firecrawl's published OpenAPI spec.\n\nYou do not need a Firecrawl account, an API key, an inbox, or a browser. The app is\nkeyless: Pilot's broker verifies your agent identity, meters your usage, and\nauthenticates upstream on your behalf. Install and call.\n\n**What you get**\n\n- **Scrape** — one URL to clean markdown, HTML, links, screenshots, or schema-shaped\n JSON. Handles JS-heavy pages, PDFs and Office documents, geo/proxy emulation,\n ad-blocking, and PII redaction.\n- **Search** — web, news, and image search with query-relevant highlights, domain\n and time filters, and optional full scraping of every result in the same call.\n- **Map** — enumerate every URL on a domain in about a second. The cheap way to scope\n a crawl before you pay for one.\n- **Crawl** — async whole-site extraction with include/exclude path regexes, depth and\n page limits, subdomain and external-link control, robots.txt handling, and webhooks.\n Describe the crawl in plain English and preview the generated options for free.\n- **Batch scrape** — many known URLs in one job, with per-URL error reporting.\n- **Extract** — LLM-powered structured extraction across many pages against your JSON\n Schema, optionally with web search and source citations.\n- **Agent** — autonomous web research. Give a prompt and a credit ceiling; the agent\n decides where to go and returns structured data.\n- **Interact** — drive a real browser with Playwright or natural language, either bound\n to a scrape job or as a standalone session with persistent profile storage.\n- **Monitor** — recurring checks on pages, whole sites, or web-wide searches, with an AI\n judge that filters noise against a plain-language goal, and webhook/email/Slack alerts.\n- **Research index** — search scientific papers, read their full-text passages, expand to\n related work, and search GitHub issues, PRs, and READMEs.\n- **Support** — `firecrawl.ask` diagnoses a failing job from your account's logs, and\n `firecrawl.docs_search` answers Firecrawl questions with citations.\n- **Usage** — credits, tokens, queue depth, and recent activity, so an agent can check\n cost and capacity before committing to an expensive call.\n\n**Start with `firecrawl.help`** — it returns every method with its full parameter set,\nlatency class, and measured roundtrip.\n\n**Cost control.** `firecrawl.crawl` takes `limit` and `firecrawl.agent` takes\n`maxCredits`; both default high. Set them. `firecrawl.map` and\n`firecrawl.crawl_params_preview` are the cheap ways to scope work before you spend.\nCheck `firecrawl.credit_usage` and `firecrawl.balance` before a large job.\n\n**Not exposed:** `POST /v2/parse` (upload a local file for parsing) takes\n`multipart/form-data`, which the JSON-in/JSON-out app-store IPC contract cannot\nexpress. Use `firecrawl.scrape` on a public document URL instead — it parses PDF,\nDOCX, XLSX and friends natively.\n", + "vendor": "Firecrawl", + "vendorUrl": "https://firecrawl.dev", + "license": "MIT", + "sourceUrl": "https://github.com/pilot-protocol/firecrawl-app", + "homepage": "https://firecrawl.dev", + "version": "0.1.0", + "categoriesRaw": [ + "web", + "search", + "data", + "scraping" + ], + "keywords": [ + "firecrawl", + "scrape", + "crawl", + "search", + "markdown", + "extract", + "agent", + "monitor", + "research", + "browser" + ], + "bundleBytes": 4901773, + "installedBytes": 8999266, + "changelog": [ + { + "version": "0.1.0", + "notes": [ + "Released v0.1.0" + ] + } + ], + "minPilotVersion": "1.11.0", + "runtimes": [ + "go" + ], + "protection": "shareable", + "publishedAt": "2026-08-03", + "grants": [ + "fs.read:$APP/config.json", + "key.sign:self", + "net.dial:broker.pilotprotocol.network", + "audit.log:*" + ], + "inCatalogue": true } -} +} \ No newline at end of file diff --git a/src/data/apps.ts b/src/data/apps.ts index f243d2f..e550b0e 100644 --- a/src/data/apps.ts +++ b/src/data/apps.ts @@ -3183,6 +3183,493 @@ export const apps: App[] = [ }, "limits": null }, + { + "id": "io.pilot.firecrawl", + "name": "Firecrawl", + "tagline": "Turn any website into clean, LLM-ready data", + "description": "**Firecrawl** turns any website into clean, LLM-ready data. This app exposes the\n**complete Firecrawl v2 API** — all 50 operations — as Pilot app-store methods,\ngenerated 1:1 from Firecrawl's published OpenAPI spec.\n\nYou do not need a Firecrawl account, an API key, an inbox, or a browser. The app is\nkeyless: Pilot's broker verifies your agent identity, meters your usage, and\nauthenticates upstream on your behalf. Install and call.\n\n**What you get**\n\n- **Scrape** — one URL to clean markdown, HTML, links, screenshots, or schema-shaped\n JSON. Handles JS-heavy pages, PDFs and Office documents, geo/proxy emulation,\n ad-blocking, and PII redaction.\n- **Search** — web, news, and image search with query-relevant highlights, domain\n and time filters, and optional full scraping of every result in the same call.\n- **Map** — enumerate every URL on a domain in about a second. The cheap way to scope\n a crawl before you pay for one.\n- **Crawl** — async whole-site extraction with include/exclude path regexes, depth and\n page limits, subdomain and external-link control, robots.txt handling, and webhooks.\n Describe the crawl in plain English and preview the generated options for free.\n- **Batch scrape** — many known URLs in one job, with per-URL error reporting.\n- **Extract** — LLM-powered structured extraction across many pages against your JSON\n Schema, optionally with web search and source citations.\n- **Agent** — autonomous web research. Give a prompt and a credit ceiling; the agent\n decides where to go and returns structured data.\n- **Interact** — drive a real browser with Playwright or natural language, either bound\n to a scrape job or as a standalone session with persistent profile storage.\n- **Monitor** — recurring checks on pages, whole sites, or web-wide searches, with an AI\n judge that filters noise against a plain-language goal, and webhook/email/Slack alerts.\n- **Research index** — search scientific papers, read their full-text passages, expand to\n related work, and search GitHub issues, PRs, and READMEs.\n- **Support** — `firecrawl.ask` diagnoses a failing job from your account's logs, and\n `firecrawl.docs_search` answers Firecrawl questions with citations.\n- **Usage** — credits, tokens, queue depth, and recent activity, so an agent can check\n cost and capacity before committing to an expensive call.\n\n**Start with `firecrawl.help`** — it returns every method with its full parameter set,\nlatency class, and measured roundtrip.\n\n**Cost control.** `firecrawl.crawl` takes `limit` and `firecrawl.agent` takes\n`maxCredits`; both default high. Set them. `firecrawl.map` and\n`firecrawl.crawl_params_preview` are the cheap ways to scope work before you spend.\nCheck `firecrawl.credit_usage` and `firecrawl.balance` before a large job.\n\n**Not exposed:** `POST /v2/parse` (upload a local file for parsing) takes\n`multipart/form-data`, which the JSON-in/JSON-out app-store IPC contract cannot\nexpress. Use `firecrawl.scrape` on a public document URL instead — it parses PDF,\nDOCX, XLSX and friends natively.\n", + "categories": [ + "web" + ], + "primaryCategory": "web", + "keywords": [ + "firecrawl", + "scrape", + "crawl", + "search", + "markdown", + "extract", + "agent", + "monitor", + "research", + "browser" + ], + "version": "0.1.0", + "vendor": "Firecrawl", + "vendorUrl": "https://firecrawl.dev", + "license": "MIT", + "sourceUrl": "https://github.com/pilot-protocol/firecrawl-app", + "homepage": "https://firecrawl.dev", + "methods": [ + { + "name": "firecrawl.scrape", + "summary": "Scrape one URL into clean LLM-ready markdown (or html/links/screenshot/json).", + "example": null, + "gated": null + }, + { + "name": "firecrawl.scrape_status", + "summary": "Get the status and result of a previously started scrape job by id.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.scrape_interact", + "summary": "Run code or an AI prompt in the live browser session bound to a scrape job.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.scrape_interact_stop", + "summary": "Stop the interactive browser session attached to a scrape job and free its resources.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.search", + "summary": "Search the web (and optionally scrape every result in one call).", + "example": null, + "gated": null + }, + { + "name": "firecrawl.search_feedback", + "summary": "Rate a search job's quality to improve future Firecrawl search results.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.map", + "summary": "Discover every URL on a domain, fast — the cheap way to scope a crawl.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl", + "summary": "Start an async crawl of an entire site. Returns a job id — poll firecrawl.crawl_status.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_status", + "summary": "Poll a crawl job: status, completed/total counts, credits used, and the scraped pages.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_cancel", + "summary": "Cancel a running crawl job.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_errors", + "summary": "List the pages a crawl job failed on, plus URLs blocked by robots.txt.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_active", + "summary": "List every crawl currently running for your account.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.crawl_params_preview", + "summary": "Dry-run: see the crawl options a natural-language prompt would generate, without spending credits.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.batch_scrape", + "summary": "Scrape many known URLs in one async job. Returns a job id — poll firecrawl.batch_scrape_status.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.batch_scrape_status", + "summary": "Poll a batch scrape job: progress, credits used, and the scraped pages.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.batch_scrape_cancel", + "summary": "Cancel a running batch scrape job.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.batch_scrape_errors", + "summary": "List the URLs a batch scrape job failed on, with per-URL error detail.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.extract", + "summary": "Extract structured JSON from one or many pages using an LLM and your schema. Async — poll firecrawl.extract_status.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.extract_status", + "summary": "Poll an extract job and retrieve the structured data once it completes.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.agent", + "summary": "Autonomous web research: give a prompt, the agent decides what to visit and returns structured data. Async — poll firecrawl.agent_status.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.agent_status", + "summary": "Poll an agent job and retrieve its result once it completes.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.agent_cancel", + "summary": "Cancel a running agent job and stop it spending credits.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.interact_create", + "summary": "Start a standalone browser session you drive with code — no prior scrape needed.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.interact_list", + "summary": "List your standalone interact browser sessions.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.interact_execute", + "summary": "Run Playwright or agent-browser code inside a standalone interact session.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.interact_delete", + "summary": "Destroy a standalone interact session and release its browser.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_create", + "summary": "Create a recurring check that watches pages, a whole site, or search results and alerts on meaningful change.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_list", + "summary": "List your monitors.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_get", + "summary": "Get one monitor's full configuration.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_update", + "summary": "Update a monitor's schedule, targets, goal, or status (active/paused).", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_delete", + "summary": "Delete a monitor and stop its recurring checks.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_run", + "summary": "Trigger a monitor check immediately instead of waiting for its schedule.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_checks", + "summary": "List a monitor's past checks and what changed in each.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.monitor_check", + "summary": "Get one monitor check in full, with page-level diffs.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.research_papers", + "summary": "Search a purpose-built scientific paper index by natural-language query.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.research_paper", + "summary": "Inspect a paper's metadata, or pass query to read its top matching full-text passages.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.research_related", + "summary": "Expand a seed paper to related work — citers, references, or semantically similar papers.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.research_github", + "summary": "Search GitHub issues, pull requests, discussions, and READMEs by natural-language query.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.ask", + "summary": "Diagnose a failing Firecrawl call with an AI support agent — pass the jobId and get a fix.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.docs_search", + "summary": "Answer a \"how does Firecrawl do X?\" question from the official docs, with citations.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.feedback", + "summary": "Submit quality feedback for a completed search, scrape, parse, or map job.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.credit_usage", + "summary": "Remaining credits and the current billing period.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.credit_usage_historical", + "summary": "Historical credit usage over time, optionally broken down by API key.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.token_usage", + "summary": "Remaining LLM tokens for extract-family operations.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.token_usage_historical", + "summary": "Historical token usage over time, optionally broken down by API key.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.queue_status", + "summary": "Your scrape queue depth, active jobs, and max concurrency — check before a large batch.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.activity", + "summary": "Recent API activity (last 24h) with job ids you can feed to the *_status methods.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.threat_protection", + "summary": "Read the account's threat-protection policy (URL risk checks, allow/deny lists).", + "example": null, + "gated": null + }, + { + "name": "firecrawl.threat_protection_update", + "summary": "Replace the account's threat-protection policy. Full-document update — unspecified fields reset to defaults.", + "example": null, + "gated": "Disabled on the shared-key plan. Threat-protection policy is TEAM-WIDE, so on the current managed key one caller's update would change the policy for every Pilot user; the broker refuses this verb. It unlocks when Firecrawl issues per-user keys. Upstream it is also enterprise-only and team-admin-only." + }, + { + "name": "firecrawl.balance", + "summary": "Your remaining Pilot budget for this app, read free from the broker's per-user credit ledger — returns {\"balance\":\"$X.XX\",\"credits_remaining\":,\"credits_seed\":,\"unit\":\"micro_usd\",\"scope\":\"per-pilot-user\"}. This is YOUR budget, not the shared account's. No partner API call, no charge, and never a 402. The same figure also rides on the X-Pilot-Credits-Remaining header of every metered response; call this when you just want to check what's left before a spend.", + "example": null, + "gated": null + }, + { + "name": "firecrawl.help", + "summary": "Discovery: every method with params, kind, and latency class.", + "example": null, + "gated": null + } + ], + "changelog": [ + { + "version": "0.1.0", + "notes": [ + "Released v0.1.0" + ] + } + ], + "grants": [ + "fs.read:$APP/config.json", + "key.sign:self", + "net.dial:broker.pilotprotocol.network", + "audit.log:*" + ], + "bundles": [ + { + "platform": "darwin-arm64", + "bytes": 5293915 + }, + { + "platform": "darwin-amd64", + "bytes": 5293915 + }, + { + "platform": "linux-arm64", + "bytes": 4754720 + }, + { + "platform": "linux-amd64", + "bytes": 4754720 + } + ], + "installedBytes": 8999266, + "depends": [], + "protection": "shareable", + "featured": false, + "real": true, + "inCatalogue": true, + "icon": { + "mode": "image", + "img": "/appicons/io.pilot.firecrawl.png", + "fit": "contain", + "pos": "center", + "color": "#0b0b0a", + "ink": false, + "file": null, + "hue": 200 + }, + "minPilotVersion": "1.11.0", + "runtimes": [ + "go" + ], + "publishedAt": "2026-08-03", + "updatedAt": "2026-08-03", + "productDemo": { + "skill": "io.pilot.firecrawl", + "title": "Full usage demo", + "when_to_use": "When you need content from the live web: read a page as clean markdown, search the web, enumerate a site's URLs, crawl a whole domain, or extract structured data from pages.", + "metered": true, + "quickstart": { + "goal": "Read any page as clean, LLM-ready markdown", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.scrape '{\"url\":\"https://example.com\"}'", + "expect": "{\"success\":true,\"data\":{\"markdown\":\"# Example Domain\\n\\nThis domain is for use in documentation examples...\",\"metadata\":{\"title\":\"Example Domain\",\"creditsUsed\":1}}}", + "cost": "1 credit (~$0.0008)" + }, + "examples": [ + { + "title": "Check your budget first (free read)", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.balance '{}'", + "expect": "{\"balance\":\"$0.04\",\"credits_remaining\":41500,\"credits_seed\":41500,\"unit\":\"micro_usd\",\"scope\":\"per-pilot-user\"}", + "cost": "$0.00 (read)" + }, + { + "title": "Search the web and get real content back", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.search '{\"query\":\"open source web crawlers\",\"limit\":3}'", + "expect": "{\"success\":true,\"data\":{\"web\":[{\"url\":\"https://...\",\"title\":\"...\",\"description\":\"...\"}]},\"id\":\"019fc9b8-...\"}", + "cost": "2 credits (~$0.0017)" + }, + { + "title": "Enumerate a site's URLs before paying to crawl it", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.map '{\"url\":\"https://docs.firecrawl.dev\",\"limit\":50}'", + "expect": "{\"success\":true,\"links\":[{\"url\":\"https://docs.firecrawl.dev/...\",\"title\":\"...\"}]}", + "cost": "1 credit (~$0.0008)", + "note": "Always map before a crawl: crawl defaults to 10000 pages." + }, + { + "title": "Crawl a site — async, returns a job id", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.crawl '{\"url\":\"https://docs.firecrawl.dev\",\"limit\":10}'", + "expect": "{\"success\":true,\"id\":\"a1b2c3d4-...\",\"url\":\"https://api.firecrawl.dev/v2/crawl/a1b2c3d4-...\"}", + "cost": "20 credits (~$0.0166)", + "note": "SET limit — it defaults to 10000 pages." + }, + { + "title": "Poll the crawl until it finishes (free read)", + "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.crawl_status '{\"id\":\"a1b2c3d4-...\"}'", + "expect": "{\"status\":\"completed\",\"total\":10,\"completed\":10,\"creditsUsed\":10,\"data\":[{\"markdown\":\"...\",\"metadata\":{}}]}", + "cost": "$0.00 (read)" + } + ], + "cost": { + "unit": "micro-USD (1000000 = $1.00); 1 Firecrawl credit ~= 830 micro-USD", + "free_budget": "$0.0415 per Pilot user (~50 Firecrawl credits)", + "hard_cap_usd": 0.0415, + "operations": [ + { + "op": "scrape / map / docs_search", + "price": "$0.0008", + "note": "1 credit" + }, + { + "op": "search", + "price": "$0.0017", + "note": "2 credits" + }, + { + "op": "interact_create / monitor_create", + "price": "$0.0042", + "note": "5 credits" + }, + { + "op": "crawl / batch_scrape / extract", + "price": "$0.0166", + "note": "20 credits at enqueue; a big crawl costs more upstream" + }, + { + "op": "agent", + "price": "$0.0415", + "note": "50 credits — your whole budget in one call" + }, + { + "op": "all *_status, *_errors, list, team reads", + "price": "$0.00", + "note": "reads are free" + } + ], + "worked_total": "This demo spends $0.0191 of your $0.0415 budget (search + map + crawl + the quickstart scrape; both reads are free).", + "check_balance": "pilotctl appstore call io.pilot.firecrawl firecrawl.balance '{}'" + }, + "gotchas": [ + "crawl defaults to limit 10000 and agent to maxCredits 2500 — always set them.", + "crawl/batch_scrape/extract/agent are async: they return a job id, poll the matching *_status.", + "402 = your budget is spent; reads and status polls still work.", + "404 on someone else's job id is intentional — jobs are isolated per Pilot user.", + "firecrawl.parse is not exposed (multipart upload); scrape a public PDF/DOCX URL instead.", + "Results cache up to 48h; pass maxAge:0 to force a fresh fetch." + ], + "next": [ + "io.pilot.firecrawl firecrawl.help '{}'" + ] + }, + "limits": null + }, { "id": "io.pilot.smol", "name": "Smol Machines", From e133cdc259dad3f26b551fc6a1d220eca5b84490 Mon Sep 17 00:00:00 2001 From: Alex Godoroja Date: Mon, 3 Aug 2026 16:32:58 -0700 Subject: [PATCH 2/2] app-store: redact Firecrawl internals, pin it to New & Updated The app page was publishing our cost basis with Firecrawl. Removed: - the micro-USD ledger unit and the '1 credit ~= 830 micro-USD' conversion - per-operation dollar prices and the $0.0415 budget - credits_seed / unit=micro_usd in the balance example - that Pilot runs one shared Firecrawl account and users share a team (leaked via the gated reason and the scaffold-generated balance summary) Published limits are now the user-facing ones: 1000 credits, 2 concurrent calls. Also pins io.pilot.firecrawl first in the New & Updated shelf, and re-stamps the plain twin (regen-plain needs GEMINI_API_KEY; the plain app-store page carries no app list or count, so shelf order cannot change its content - verified). Co-Authored-By: Claude Opus 5 (1M context) --- src/data/app-demos.json | 57 ++++++++++++++++-------------- src/data/app-methods.json | 4 +-- src/data/apps.ts | 61 ++++++++++++++++++--------------- src/pages/app-store.astro | 2 +- src/pages/plain/app-store.astro | 2 +- 5 files changed, 68 insertions(+), 58 deletions(-) diff --git a/src/data/app-demos.json b/src/data/app-demos.json index 6bde157..bca3fc3 100644 --- a/src/data/app-demos.json +++ b/src/data/app-demos.json @@ -1055,88 +1055,93 @@ "goal": "Read any page as clean, LLM-ready markdown", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.scrape '{\"url\":\"https://example.com\"}'", "expect": "{\"success\":true,\"data\":{\"markdown\":\"# Example Domain\\n\\nThis domain is for use in documentation examples...\",\"metadata\":{\"title\":\"Example Domain\",\"creditsUsed\":1}}}", - "cost": "1 credit (~$0.0008)" + "cost": "1 credit" }, "examples": [ { "title": "Check your budget first (free read)", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.balance '{}'", - "expect": "{\"balance\":\"$0.04\",\"credits_remaining\":41500,\"credits_seed\":41500,\"unit\":\"micro_usd\",\"scope\":\"per-pilot-user\"}", - "cost": "$0.00 (read)" + "expect": "{\"credits_remaining\":1000,\"credits_seed\":1000,\"scope\":\"per-pilot-user\"}", + "cost": "0 credits (read)" }, { "title": "Search the web and get real content back", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.search '{\"query\":\"open source web crawlers\",\"limit\":3}'", "expect": "{\"success\":true,\"data\":{\"web\":[{\"url\":\"https://...\",\"title\":\"...\",\"description\":\"...\"}]},\"id\":\"019fc9b8-...\"}", - "cost": "2 credits (~$0.0017)" + "cost": "2 credits" }, { "title": "Enumerate a site's URLs before paying to crawl it", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.map '{\"url\":\"https://docs.firecrawl.dev\",\"limit\":50}'", "expect": "{\"success\":true,\"links\":[{\"url\":\"https://docs.firecrawl.dev/...\",\"title\":\"...\"}]}", - "cost": "1 credit (~$0.0008)", + "cost": "1 credit", "note": "Always map before a crawl: crawl defaults to 10000 pages." }, { "title": "Crawl a site — async, returns a job id", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.crawl '{\"url\":\"https://docs.firecrawl.dev\",\"limit\":10}'", "expect": "{\"success\":true,\"id\":\"a1b2c3d4-...\",\"url\":\"https://api.firecrawl.dev/v2/crawl/a1b2c3d4-...\"}", - "cost": "20 credits (~$0.0166)", + "cost": "20 credits", "note": "SET limit — it defaults to 10000 pages." }, { "title": "Poll the crawl until it finishes (free read)", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.crawl_status '{\"id\":\"a1b2c3d4-...\"}'", "expect": "{\"status\":\"completed\",\"total\":10,\"completed\":10,\"creditsUsed\":10,\"data\":[{\"markdown\":\"...\",\"metadata\":{}}]}", - "cost": "$0.00 (read)" + "cost": "0 credits (read)" } ], "cost": { - "unit": "micro-USD (1000000 = $1.00); 1 Firecrawl credit ~= 830 micro-USD", - "free_budget": "$0.0415 per Pilot user (~50 Firecrawl credits)", - "hard_cap_usd": 0.0415, + "unit": "Firecrawl credits", + "free_budget": "1000 credits per Pilot user", + "hard_cap_usd": 0, "operations": [ { "op": "scrape / map / docs_search", - "price": "$0.0008", - "note": "1 credit" + "price": "1 credit", + "note": "per call" }, { "op": "search", - "price": "$0.0017", - "note": "2 credits" + "price": "2 credits", + "note": "per query" + }, + { + "op": "ask", + "price": "3 credits", + "note": "per diagnosis" }, { "op": "interact_create / monitor_create", - "price": "$0.0042", - "note": "5 credits" + "price": "5 credits", + "note": "per session or monitor" }, { "op": "crawl / batch_scrape / extract", - "price": "$0.0166", - "note": "20 credits at enqueue; a big crawl costs more upstream" + "price": "20 credits", + "note": "charged when the job is accepted; a large crawl costs more as it runs" }, { "op": "agent", - "price": "$0.0415", - "note": "50 credits — your whole budget in one call" + "price": "50 credits", + "note": "cap it with maxCredits" }, { - "op": "all *_status, *_errors, list, team reads", - "price": "$0.00", + "op": "all *_status, *_errors, list and usage reads", + "price": "0 credits", "note": "reads are free" } ], - "worked_total": "This demo spends $0.0191 of your $0.0415 budget (search + map + crawl + the quickstart scrape; both reads are free).", + "worked_total": "This demo spends 24 of your 1000 credits (quickstart scrape 1 + search 2 + map 1 + crawl 20; both reads are free).", "check_balance": "pilotctl appstore call io.pilot.firecrawl firecrawl.balance '{}'" }, "gotchas": [ + "You get 1000 credits and 2 concurrent calls. Check firecrawl.balance before a big job.", + "429 means you already have 2 calls in flight — wait, or free a slot. Browser/interact sessions hold one until stopped.", "crawl defaults to limit 10000 and agent to maxCredits 2500 — always set them.", "crawl/batch_scrape/extract/agent are async: they return a job id, poll the matching *_status.", - "402 = your budget is spent; reads and status polls still work.", "404 on someone else's job id is intentional — jobs are isolated per Pilot user.", - "firecrawl.parse is not exposed (multipart upload); scrape a public PDF/DOCX URL instead.", - "Results cache up to 48h; pass maxAge:0 to force a fresh fetch." + "firecrawl.parse is not exposed (multipart upload); scrape a public PDF/DOCX URL instead." ], "next": [ "io.pilot.firecrawl firecrawl.help '{}'" diff --git a/src/data/app-methods.json b/src/data/app-methods.json index 6bcc818..92daec3 100644 --- a/src/data/app-methods.json +++ b/src/data/app-methods.json @@ -2108,11 +2108,11 @@ "name": "firecrawl.threat_protection_update", "summary": "Replace the account's threat-protection policy. Full-document update — unspecified fields reset to defaults.", "example": null, - "gated": "Disabled on the shared-key plan. Threat-protection policy is TEAM-WIDE, so on the current managed key one caller's update would change the policy for every Pilot user; the broker refuses this verb. It unlocks when Firecrawl issues per-user keys. Upstream it is also enterprise-only and team-admin-only." + "gated": "Not available on the Pilot plan. Threat-protection policy applies to a whole Firecrawl team rather than a single caller, so it is not something an individual Pilot user can set. Upstream it is also an enterprise feature restricted to team admins." }, { "name": "firecrawl.balance", - "summary": "Your remaining Pilot budget for this app, read free from the broker's per-user credit ledger — returns {\"balance\":\"$X.XX\",\"credits_remaining\":,\"credits_seed\":,\"unit\":\"micro_usd\",\"scope\":\"per-pilot-user\"}. This is YOUR budget, not the shared account's. No partner API call, no charge, and never a 402. The same figure also rides on the X-Pilot-Credits-Remaining header of every metered response; call this when you just want to check what's left before a spend.", + "summary": "Your remaining credits for this app, read free from the broker's per-user ledger — returns {\"credits_remaining\":,\"credits_seed\":,\"scope\":\"per-pilot-user\"}. This is YOUR balance. No partner API call, no charge. Check it before an expensive job.", "example": null, "gated": null }, diff --git a/src/data/apps.ts b/src/data/apps.ts index e550b0e..3671713 100644 --- a/src/data/apps.ts +++ b/src/data/apps.ts @@ -3503,11 +3503,11 @@ export const apps: App[] = [ "name": "firecrawl.threat_protection_update", "summary": "Replace the account's threat-protection policy. Full-document update — unspecified fields reset to defaults.", "example": null, - "gated": "Disabled on the shared-key plan. Threat-protection policy is TEAM-WIDE, so on the current managed key one caller's update would change the policy for every Pilot user; the broker refuses this verb. It unlocks when Firecrawl issues per-user keys. Upstream it is also enterprise-only and team-admin-only." + "gated": "Not available on the Pilot plan. Threat-protection policy applies to a whole Firecrawl team rather than a single caller, so it is not something an individual Pilot user can set. Upstream it is also an enterprise feature restricted to team admins." }, { "name": "firecrawl.balance", - "summary": "Your remaining Pilot budget for this app, read free from the broker's per-user credit ledger — returns {\"balance\":\"$X.XX\",\"credits_remaining\":,\"credits_seed\":,\"unit\":\"micro_usd\",\"scope\":\"per-pilot-user\"}. This is YOUR budget, not the shared account's. No partner API call, no charge, and never a 402. The same figure also rides on the X-Pilot-Credits-Remaining header of every metered response; call this when you just want to check what's left before a spend.", + "summary": "Your remaining credits for this app, read free from the broker's per-user ledger — returns {\"credits_remaining\":,\"credits_seed\":,\"scope\":\"per-pilot-user\"}. This is YOUR balance. No partner API call, no charge. Check it before an expensive job.", "example": null, "gated": null }, @@ -3581,88 +3581,93 @@ export const apps: App[] = [ "goal": "Read any page as clean, LLM-ready markdown", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.scrape '{\"url\":\"https://example.com\"}'", "expect": "{\"success\":true,\"data\":{\"markdown\":\"# Example Domain\\n\\nThis domain is for use in documentation examples...\",\"metadata\":{\"title\":\"Example Domain\",\"creditsUsed\":1}}}", - "cost": "1 credit (~$0.0008)" + "cost": "1 credit" }, "examples": [ { "title": "Check your budget first (free read)", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.balance '{}'", - "expect": "{\"balance\":\"$0.04\",\"credits_remaining\":41500,\"credits_seed\":41500,\"unit\":\"micro_usd\",\"scope\":\"per-pilot-user\"}", - "cost": "$0.00 (read)" + "expect": "{\"credits_remaining\":1000,\"credits_seed\":1000,\"scope\":\"per-pilot-user\"}", + "cost": "0 credits (read)" }, { "title": "Search the web and get real content back", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.search '{\"query\":\"open source web crawlers\",\"limit\":3}'", "expect": "{\"success\":true,\"data\":{\"web\":[{\"url\":\"https://...\",\"title\":\"...\",\"description\":\"...\"}]},\"id\":\"019fc9b8-...\"}", - "cost": "2 credits (~$0.0017)" + "cost": "2 credits" }, { "title": "Enumerate a site's URLs before paying to crawl it", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.map '{\"url\":\"https://docs.firecrawl.dev\",\"limit\":50}'", "expect": "{\"success\":true,\"links\":[{\"url\":\"https://docs.firecrawl.dev/...\",\"title\":\"...\"}]}", - "cost": "1 credit (~$0.0008)", + "cost": "1 credit", "note": "Always map before a crawl: crawl defaults to 10000 pages." }, { "title": "Crawl a site — async, returns a job id", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.crawl '{\"url\":\"https://docs.firecrawl.dev\",\"limit\":10}'", "expect": "{\"success\":true,\"id\":\"a1b2c3d4-...\",\"url\":\"https://api.firecrawl.dev/v2/crawl/a1b2c3d4-...\"}", - "cost": "20 credits (~$0.0166)", + "cost": "20 credits", "note": "SET limit — it defaults to 10000 pages." }, { "title": "Poll the crawl until it finishes (free read)", "command": "pilotctl appstore call io.pilot.firecrawl firecrawl.crawl_status '{\"id\":\"a1b2c3d4-...\"}'", "expect": "{\"status\":\"completed\",\"total\":10,\"completed\":10,\"creditsUsed\":10,\"data\":[{\"markdown\":\"...\",\"metadata\":{}}]}", - "cost": "$0.00 (read)" + "cost": "0 credits (read)" } ], "cost": { - "unit": "micro-USD (1000000 = $1.00); 1 Firecrawl credit ~= 830 micro-USD", - "free_budget": "$0.0415 per Pilot user (~50 Firecrawl credits)", - "hard_cap_usd": 0.0415, + "unit": "Firecrawl credits", + "free_budget": "1000 credits per Pilot user", + "hard_cap_usd": 0, "operations": [ { "op": "scrape / map / docs_search", - "price": "$0.0008", - "note": "1 credit" + "price": "1 credit", + "note": "per call" }, { "op": "search", - "price": "$0.0017", - "note": "2 credits" + "price": "2 credits", + "note": "per query" + }, + { + "op": "ask", + "price": "3 credits", + "note": "per diagnosis" }, { "op": "interact_create / monitor_create", - "price": "$0.0042", - "note": "5 credits" + "price": "5 credits", + "note": "per session or monitor" }, { "op": "crawl / batch_scrape / extract", - "price": "$0.0166", - "note": "20 credits at enqueue; a big crawl costs more upstream" + "price": "20 credits", + "note": "charged when the job is accepted; a large crawl costs more as it runs" }, { "op": "agent", - "price": "$0.0415", - "note": "50 credits — your whole budget in one call" + "price": "50 credits", + "note": "cap it with maxCredits" }, { - "op": "all *_status, *_errors, list, team reads", - "price": "$0.00", + "op": "all *_status, *_errors, list and usage reads", + "price": "0 credits", "note": "reads are free" } ], - "worked_total": "This demo spends $0.0191 of your $0.0415 budget (search + map + crawl + the quickstart scrape; both reads are free).", + "worked_total": "This demo spends 24 of your 1000 credits (quickstart scrape 1 + search 2 + map 1 + crawl 20; both reads are free).", "check_balance": "pilotctl appstore call io.pilot.firecrawl firecrawl.balance '{}'" }, "gotchas": [ + "You get 1000 credits and 2 concurrent calls. Check firecrawl.balance before a big job.", + "429 means you already have 2 calls in flight — wait, or free a slot. Browser/interact sessions hold one until stopped.", "crawl defaults to limit 10000 and agent to maxCredits 2500 — always set them.", "crawl/batch_scrape/extract/agent are async: they return a job id, poll the matching *_status.", - "402 = your budget is spent; reads and status polls still work.", "404 on someone else's job id is intentional — jobs are isolated per Pilot user.", - "firecrawl.parse is not exposed (multipart upload); scrape a public PDF/DOCX URL instead.", - "Results cache up to 48h; pass maxAge:0 to force a fresh fetch." + "firecrawl.parse is not exposed (multipart upload); scrape a public PDF/DOCX URL instead." ], "next": [ "io.pilot.firecrawl firecrawl.help '{}'" diff --git a/src/pages/app-store.astro b/src/pages/app-store.astro index c3af0ef..21369b7 100644 --- a/src/pages/app-store.astro +++ b/src/pages/app-store.astro @@ -17,7 +17,7 @@ const canonicalUrl = 'https://pilotprotocol.network/app-store'; const featured = featuredApps(); const hero = featured[0]; const sideFeatured = featured.slice(1, 4); -const freshPinned = ['io.pilot.primitive', 'io.pilot.didit', 'io.pilot.agentphone', 'io.pilot.orthogonal', 'io.pilot.bowmark', 'io.pilot.miren', 'io.pilot.smol', 'io.pilot.wallet', 'io.pilot.slipstream']; +const freshPinned = ['io.pilot.firecrawl', 'io.pilot.primitive', 'io.pilot.didit', 'io.pilot.agentphone', 'io.pilot.orthogonal', 'io.pilot.bowmark', 'io.pilot.miren', 'io.pilot.smol', 'io.pilot.wallet', 'io.pilot.slipstream']; const freshExclude = new Set(['io.pilot.postgres', 'io.pilot.docker']); const fresh = [ ...freshPinned.map((id) => apps.find((a) => a.id === id)).filter(Boolean), diff --git a/src/pages/plain/app-store.astro b/src/pages/plain/app-store.astro index dc19127..4c39582 100644 --- a/src/pages/plain/app-store.astro +++ b/src/pages/plain/app-store.astro @@ -1,7 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. // plain-source: src/pages/app-store.astro -// plain-source-sha256: 5c3087953af2e13110f8ac610b49694b35cf295b7beea14178cb4d23286e3685 +// plain-source-sha256: 36019f7e7b97e467fbd214d12751867e09a2edcb3e74be08e92654deb3033e75 import PlainLayout from '../../layouts/PlainLayout.astro'; ---