pi-firecrawl-lite 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/index.ts +1 -0
- package/package.json +10 -2
- package/src/index.ts +1 -1
- package/src/tools.ts +52 -9
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ For a project-local installation, add the package to `package.json` and use the
|
|
|
17
17
|
```json
|
|
18
18
|
{
|
|
19
19
|
"pi": {
|
|
20
|
-
"extensions": ["./node_modules/pi-firecrawl-lite/
|
|
20
|
+
"extensions": ["./node_modules/pi-firecrawl-lite/index.ts"]
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
```
|
|
@@ -43,7 +43,7 @@ export FIRECRAWL_TIMEOUT_MS=120000 # optional
|
|
|
43
43
|
|
|
44
44
|
The URL must be HTTP or HTTPS and must point to the host root or end in `/v2`. v1 URLs are rejected. The API key is never shown in tool output, errors, notifications, or saved response details.
|
|
45
45
|
|
|
46
|
-
Configuration can also be changed for the current Pi session
|
|
46
|
+
Configuration can also be changed interactively for the current Pi session. Run `/firecrawl` and choose an option from the menu. The same actions are available as slash commands:
|
|
47
47
|
|
|
48
48
|
```text
|
|
49
49
|
/firecrawl status
|
|
@@ -55,6 +55,8 @@ Configuration can also be changed for the current Pi session with slash commands
|
|
|
55
55
|
|
|
56
56
|
`/firecrawl key` opens an input prompt when no key follows it. Session command overrides are kept in memory and reset when the extension is reloaded; use environment variables for a durable configuration.
|
|
57
57
|
|
|
58
|
+
Search and scrape are available immediately; `firecrawl_load` is only needed before using the deferred map or crawl tools. If a local Firecrawl server is not reachable, the tool reports the active endpoint and points back to `/firecrawl`.
|
|
59
|
+
|
|
58
60
|
## Tools
|
|
59
61
|
|
|
60
62
|
Always active:
|
|
@@ -71,7 +73,7 @@ Deferred tools:
|
|
|
71
73
|
|
|
72
74
|
Every model-facing schema is flat. `options_json` accepts an object for advanced Firecrawl features such as browser actions, extraction, headers, location, tags, search filters, webhooks, and nested scrape options. It rejects malformed JSON, arrays, primitive values, and prototype-pollution keys. Explicit top-level arguments always override advanced options.
|
|
73
75
|
|
|
74
|
-
Responses are concise by default. Set `response_mode` to `raw` when debugging or passing the full Firecrawl response to another step. Output is bounded at 50 KiB and 2,000 lines. A truncated response is saved in a private, session-specific temporary directory with restrictive permissions, and the tool reports its exact path and limits. Artifacts are removed when the session shuts down or the extension reloads.
|
|
76
|
+
Responses are concise by default and are rendered with readable labels and width-aware Markdown in Pi. Set `response_mode` to `raw` when debugging or passing the full Firecrawl response to another step. Output is bounded at 50 KiB and 2,000 lines. A truncated response is saved in a private, session-specific temporary directory with restrictive permissions, and the tool reports its exact path and limits. Artifacts are removed when the session shuts down or the extension reloads.
|
|
75
77
|
|
|
76
78
|
## Development
|
|
77
79
|
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./src/index.js";
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-firecrawl-lite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A lightweight Pi extension that brings Firecrawl search, scrape, map, and crawl tools to your agent.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"firecrawl",
|
|
8
|
+
"web search",
|
|
9
|
+
"web scraping",
|
|
10
|
+
"ai agents"
|
|
11
|
+
],
|
|
5
12
|
"type": "module",
|
|
6
13
|
"private": false,
|
|
7
14
|
"license": "MIT",
|
|
@@ -11,13 +18,14 @@
|
|
|
11
18
|
"url": "https://git.jameslindfors.xyz/james/pi-firecrawl-lite.git"
|
|
12
19
|
},
|
|
13
20
|
"files": [
|
|
21
|
+
"index.ts",
|
|
14
22
|
"src",
|
|
15
23
|
"README.md",
|
|
16
24
|
"LICENSE"
|
|
17
25
|
],
|
|
18
26
|
"pi": {
|
|
19
27
|
"extensions": [
|
|
20
|
-
"./
|
|
28
|
+
"./index.ts"
|
|
21
29
|
]
|
|
22
30
|
},
|
|
23
31
|
"scripts": {
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,7 @@ export default function firecrawlExtension(pi: ExtensionAPI): void {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
pi.registerCommand("firecrawl", {
|
|
38
|
-
description: "Configure Firecrawl
|
|
38
|
+
description: "Configure Firecrawl interactively, or use /firecrawl status|url|key|timeout|reset",
|
|
39
39
|
handler: async (args, ctx) => configureFromCommand(args, runtime, ctx),
|
|
40
40
|
});
|
|
41
41
|
pi.on("session_start", () => {
|
package/src/tools.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { Markdown } from "@earendil-works/pi-tui";
|
|
3
5
|
import { ArtifactStore, FirecrawlClient, FirecrawlError, redactSecret } from "./client.js";
|
|
4
6
|
|
|
5
7
|
export const MAX_OUTPUT_BYTES = 50 * 1024;
|
|
@@ -160,12 +162,22 @@ function arrayFrom(value: unknown): unknown[] {
|
|
|
160
162
|
if (Array.isArray(value)) return value;
|
|
161
163
|
if (isPlainObject(value)) {
|
|
162
164
|
for (const key of ["data", "links", "web", "results"]) {
|
|
163
|
-
|
|
165
|
+
const child = value[key];
|
|
166
|
+
if (Array.isArray(child)) return child;
|
|
167
|
+
if (key === "data" && isPlainObject(child)) {
|
|
168
|
+
const nested = arrayFrom(child);
|
|
169
|
+
if (nested.length > 0) return nested;
|
|
170
|
+
}
|
|
164
171
|
}
|
|
165
172
|
}
|
|
166
173
|
return [];
|
|
167
174
|
}
|
|
168
175
|
|
|
176
|
+
function dataObject(value: unknown): JsonObject {
|
|
177
|
+
if (!isPlainObject(value)) return {};
|
|
178
|
+
return isPlainObject(value.data) ? value.data : value;
|
|
179
|
+
}
|
|
180
|
+
|
|
169
181
|
export function conciseSearch(payload: unknown): string {
|
|
170
182
|
const entries = arrayFrom(payload);
|
|
171
183
|
if (entries.length === 0) return "No search results.";
|
|
@@ -174,12 +186,12 @@ export function conciseSearch(payload: unknown): string {
|
|
|
174
186
|
const title = typeof item.title === "string" ? item.title : "Untitled result";
|
|
175
187
|
const url = typeof item.url === "string" ? item.url : "(no URL)";
|
|
176
188
|
const description = typeof item.description === "string" ? item.description : "";
|
|
177
|
-
return `${index + 1}. ${title}\n ${url}${description ? `\n ${description}` : ""}`;
|
|
189
|
+
return `${index + 1}. ${title}\n URL: ${url}${description ? `\n Description: ${description}` : ""}`;
|
|
178
190
|
}).join("\n\n");
|
|
179
191
|
}
|
|
180
192
|
|
|
181
193
|
export function conciseScrape(payload: unknown): string {
|
|
182
|
-
const item =
|
|
194
|
+
const item = dataObject(payload);
|
|
183
195
|
const metadata = isPlainObject(item.metadata) ? item.metadata : {};
|
|
184
196
|
const source = item.sourceURL ?? metadata.sourceURL ?? item.url;
|
|
185
197
|
const title = item.title ?? metadata.title;
|
|
@@ -281,7 +293,22 @@ function errorResult(error: unknown, client: FirecrawlClient) {
|
|
|
281
293
|
const config = (() => { try { return client.getConfig(); } catch { return undefined; } })();
|
|
282
294
|
const message = error instanceof FirecrawlError ? `${error.kind}: ${error.message}` : error instanceof Error ? error.message : String(error);
|
|
283
295
|
const safe = redactSecret(message, config?.apiKey);
|
|
284
|
-
|
|
296
|
+
const hint = error instanceof FirecrawlError && (error.kind === "connection" || error.kind === "timeout")
|
|
297
|
+
? `\nEndpoint: ${config?.apiUrl ?? "unknown"}. Run /firecrawl to configure Firecrawl.`
|
|
298
|
+
: "";
|
|
299
|
+
const text = `Firecrawl error: ${safe}${hint}`;
|
|
300
|
+
return { content: [{ type: "text" as const, text }], details: { error: text } } satisfies AgentToolResult<ToolDetails>;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function renderToolResult(result: AgentToolResult<ToolDetails>, options: { expanded: boolean; isPartial: boolean }) {
|
|
304
|
+
if (options.isPartial) return new Markdown("Fetching Firecrawl…", 0, 0, getMarkdownTheme());
|
|
305
|
+
const content = result.content[0];
|
|
306
|
+
if (content?.type !== "text") return new Markdown("Firecrawl returned no text output.", 0, 0, getMarkdownTheme());
|
|
307
|
+
const lines = content.text.split("\n");
|
|
308
|
+
const text = options.expanded || lines.length <= 14
|
|
309
|
+
? content.text
|
|
310
|
+
: `${lines.slice(0, 14).join("\n")}\n\n_${lines.length - 14} more lines — expand to view_`;
|
|
311
|
+
return new Markdown(text, 0, 0, getMarkdownTheme());
|
|
285
312
|
}
|
|
286
313
|
|
|
287
314
|
async function execute(runtime: ToolRuntime, kind: Parameters<typeof formatPayload>[0], responseMode: unknown, operation: () => Promise<unknown>): Promise<AgentToolResult<ToolDetails>> {
|
|
@@ -296,20 +323,20 @@ async function execute(runtime: ToolRuntime, kind: Parameters<typeof formatPaylo
|
|
|
296
323
|
}
|
|
297
324
|
|
|
298
325
|
export function registerSearchTool(pi: ExtensionAPI, runtime: ToolRuntime): void {
|
|
299
|
-
pi.registerTool({ name: "firecrawl_search", label: "Firecrawl Search", description: "Search the web through Firecrawl.", parameters: searchParameters, async execute(_id, params, signal) { return execute(runtime, "search", params.response_mode, () => runtime.client.search(buildSearchBody(params), signal)); } });
|
|
326
|
+
pi.registerTool({ name: "firecrawl_search", label: "Firecrawl Search", description: "Search the web through Firecrawl.", parameters: searchParameters, async execute(_id, params, signal) { return execute(runtime, "search", params.response_mode, () => runtime.client.search(buildSearchBody(params), signal)); }, renderResult: renderToolResult });
|
|
300
327
|
}
|
|
301
328
|
|
|
302
329
|
export function registerScrapeTool(pi: ExtensionAPI, runtime: ToolRuntime): void {
|
|
303
|
-
pi.registerTool({ name: "firecrawl_scrape", label: "Firecrawl Scrape", description: "Scrape a web page through Firecrawl.", parameters: scrapeParameters, async execute(_id, params, signal) { return execute(runtime, "scrape", params.response_mode, () => runtime.client.scrape(buildScrapeBody(params), signal)); } });
|
|
330
|
+
pi.registerTool({ name: "firecrawl_scrape", label: "Firecrawl Scrape", description: "Scrape a web page through Firecrawl.", parameters: scrapeParameters, async execute(_id, params, signal) { return execute(runtime, "scrape", params.response_mode, () => runtime.client.scrape(buildScrapeBody(params), signal)); }, renderResult: renderToolResult });
|
|
304
331
|
}
|
|
305
332
|
|
|
306
333
|
export function registerMapTool(pi: ExtensionAPI, runtime: ToolRuntime): void {
|
|
307
|
-
pi.registerTool({ name: "firecrawl_map", label: "Firecrawl Map", description: "Discover URLs on a website through Firecrawl.", parameters: mapParameters, async execute(_id, params, signal) { return execute(runtime, "map", params.response_mode, () => runtime.client.map(buildMapBody(params), signal)); } });
|
|
334
|
+
pi.registerTool({ name: "firecrawl_map", label: "Firecrawl Map", description: "Discover URLs on a website through Firecrawl.", parameters: mapParameters, async execute(_id, params, signal) { return execute(runtime, "map", params.response_mode, () => runtime.client.map(buildMapBody(params), signal)); }, renderResult: renderToolResult });
|
|
308
335
|
}
|
|
309
336
|
|
|
310
337
|
export function registerCrawlTools(pi: ExtensionAPI, runtime: ToolRuntime): void {
|
|
311
|
-
pi.registerTool({ name: "firecrawl_crawl", label: "Firecrawl Crawl", description: "Start a Firecrawl site crawl.", parameters: crawlParameters, async execute(_id, params, signal) { return execute(runtime, "crawl", params.response_mode, () => runtime.client.crawl(buildCrawlBody(params), signal)); } });
|
|
312
|
-
pi.registerTool({ name: "firecrawl_crawl_status", label: "Firecrawl Crawl Status", description: "Read the status and documents for a Firecrawl crawl.", parameters: crawlStatusParameters, async execute(_id, params, signal) { return execute(runtime, "crawl-status", params.response_mode, () => runtime.client.crawlStatus(requireText(params.job_id, "job_id"), signal)); } });
|
|
338
|
+
pi.registerTool({ name: "firecrawl_crawl", label: "Firecrawl Crawl", description: "Start a Firecrawl site crawl.", parameters: crawlParameters, async execute(_id, params, signal) { return execute(runtime, "crawl", params.response_mode, () => runtime.client.crawl(buildCrawlBody(params), signal)); }, renderResult: renderToolResult });
|
|
339
|
+
pi.registerTool({ name: "firecrawl_crawl_status", label: "Firecrawl Crawl Status", description: "Read the status and documents for a Firecrawl crawl.", parameters: crawlStatusParameters, async execute(_id, params, signal) { return execute(runtime, "crawl-status", params.response_mode, () => runtime.client.crawlStatus(requireText(params.job_id, "job_id"), signal)); }, renderResult: renderToolResult });
|
|
313
340
|
}
|
|
314
341
|
|
|
315
342
|
export function createRuntime(): ToolRuntime {
|
|
@@ -322,6 +349,22 @@ export async function configureFromCommand(args: string, runtime: ToolRuntime, c
|
|
|
322
349
|
const value = rest.join(" ").trim();
|
|
323
350
|
const notify = (message: string, level: "info" | "warning" = "info") => ctx.ui.notify(message, level);
|
|
324
351
|
try {
|
|
352
|
+
if (!command && ctx.hasUI) {
|
|
353
|
+
const choice = await ctx.ui.select("Configure Firecrawl", [
|
|
354
|
+
"Show status",
|
|
355
|
+
"Set API URL",
|
|
356
|
+
"Set API key",
|
|
357
|
+
"Set timeout",
|
|
358
|
+
"Reset session overrides",
|
|
359
|
+
"Cancel",
|
|
360
|
+
]);
|
|
361
|
+
if (choice === "Show status") return configureFromCommand("status", runtime, ctx);
|
|
362
|
+
if (choice === "Set API URL") return configureFromCommand("url", runtime, ctx);
|
|
363
|
+
if (choice === "Set API key") return configureFromCommand("key", runtime, ctx);
|
|
364
|
+
if (choice === "Set timeout") return configureFromCommand("timeout", runtime, ctx);
|
|
365
|
+
if (choice === "Reset session overrides") return configureFromCommand("reset", runtime, ctx);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
325
368
|
if (!command || command === "status") {
|
|
326
369
|
const config = runtime.client.getConfig();
|
|
327
370
|
notify(`Firecrawl endpoint: ${config.apiUrl}; authentication: ${config.apiKey ? "configured" : "not configured"}; timeout: ${config.timeoutMs} ms.`);
|