firecrawl 4.41.0 → 4.42.1
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/dist/{chunk-I2DFXQYR.js → chunk-YG554MNR.js} +1 -1
- package/dist/index.cjs +34 -12
- package/dist/index.d.cts +135 -12
- package/dist/index.d.ts +135 -12
- package/dist/index.js +35 -13
- package/dist/{package-V72LKFNW.js → package-FOKY5K5X.js} +1 -1
- package/package.json +1 -1
- package/pnpm-workspace.yaml +3 -1
- package/src/__tests__/unit/v1/next-url-pinning.test.ts +81 -0
- package/src/__tests__/unit/v2/agent-hints.test.ts +116 -0
- package/src/__tests__/unit/v2/agent-thread.unit.test.ts +122 -0
- package/src/__tests__/unit/v2/next-url-pinning.test.ts +104 -0
- package/src/utils/apiOrigin.ts +19 -0
- package/src/v1/index.ts +7 -3
- package/src/v2/methods/batch.ts +1 -1
- package/src/v2/methods/crawl.ts +1 -1
- package/src/v2/methods/map.ts +2 -1
- package/src/v2/methods/parse.ts +2 -1
- package/src/v2/methods/scrape.ts +2 -1
- package/src/v2/methods/search.ts +2 -1
- package/src/v2/types.ts +138 -11
- package/src/v2/utils/agentHints.ts +7 -0
- package/src/v2/utils/errorHandler.ts +5 -5
- package/src/v2/utils/httpClient.ts +2 -0
package/src/v2/methods/batch.ts
CHANGED
|
@@ -152,7 +152,7 @@ export async function getBatchScrapeErrors(
|
|
|
152
152
|
try {
|
|
153
153
|
const res = await http.get<{
|
|
154
154
|
success?: boolean;
|
|
155
|
-
data?:
|
|
155
|
+
data?: CrawlErrorsResponse;
|
|
156
156
|
}>(`/v2/batch/scrape/${jobId}/errors`);
|
|
157
157
|
if (res.status !== 200) throwForBadResponse(res, "get batch scrape errors");
|
|
158
158
|
const payload = res.data?.data ?? (res.data as any);
|
package/src/v2/methods/crawl.ts
CHANGED
|
@@ -162,7 +162,7 @@ export async function crawl(http: HttpClient, request: CrawlRequest, pollInterva
|
|
|
162
162
|
|
|
163
163
|
export async function getCrawlErrors(http: HttpClient, crawlId: string): Promise<CrawlErrorsResponse> {
|
|
164
164
|
try {
|
|
165
|
-
const res = await http.get<{ success?: boolean; data?:
|
|
165
|
+
const res = await http.get<{ success?: boolean; data?: CrawlErrorsResponse }>(`/v2/crawl/${crawlId}/errors`);
|
|
166
166
|
if (res.status !== 200) throwForBadResponse(res, "get crawl errors");
|
|
167
167
|
const payload = res.data?.data ?? (res.data as any);
|
|
168
168
|
return { errors: payload.errors || [], robotsBlocked: payload.robotsBlocked || [] };
|
package/src/v2/methods/map.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type MapData, type MapOptions, type SearchResultWeb } from "../types";
|
|
2
2
|
import { HttpClient } from "../utils/httpClient";
|
|
3
|
+
import { agentHintMetadata } from "../utils/agentHints";
|
|
3
4
|
import {
|
|
4
5
|
throwForBadResponse,
|
|
5
6
|
normalizeAxiosError,
|
|
@@ -65,7 +66,7 @@ export async function map(
|
|
|
65
66
|
description: (item as any).description,
|
|
66
67
|
});
|
|
67
68
|
}
|
|
68
|
-
return { id: res.data.id, links };
|
|
69
|
+
return { id: res.data.id, links, ...agentHintMetadata(res.data) };
|
|
69
70
|
} catch (err: any) {
|
|
70
71
|
if (err?.isAxiosError) return normalizeAxiosError(err, "map");
|
|
71
72
|
throw err;
|
package/src/v2/methods/parse.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Document, type ParseFile, type ParseOptions } from "../types";
|
|
2
2
|
import { HttpClient } from "../utils/httpClient";
|
|
3
|
+
import { agentHintMetadata } from "../utils/agentHints";
|
|
3
4
|
import { ensureValidParseOptions } from "../utils/validation";
|
|
4
5
|
import {
|
|
5
6
|
throwForBadResponse,
|
|
@@ -88,7 +89,7 @@ export async function parse(
|
|
|
88
89
|
if (res.status !== 200 || !res.data?.success) {
|
|
89
90
|
throwForBadResponse(res, "parse");
|
|
90
91
|
}
|
|
91
|
-
return
|
|
92
|
+
return { ...res.data.data, ...agentHintMetadata(res.data) };
|
|
92
93
|
} catch (err: any) {
|
|
93
94
|
if (err?.isAxiosError) return normalizeAxiosError(err, "parse");
|
|
94
95
|
throw err;
|
package/src/v2/methods/scrape.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ScrapeOptions,
|
|
7
7
|
} from "../types";
|
|
8
8
|
import { HttpClient } from "../utils/httpClient";
|
|
9
|
+
import { agentHintMetadata } from "../utils/agentHints";
|
|
9
10
|
import { ensureValidScrapeOptions } from "../utils/validation";
|
|
10
11
|
import {
|
|
11
12
|
throwForBadResponse,
|
|
@@ -131,7 +132,7 @@ export async function scrape(
|
|
|
131
132
|
if (res.status !== 200 || !res.data?.success) {
|
|
132
133
|
throwForBadResponse(res, "scrape");
|
|
133
134
|
}
|
|
134
|
-
return
|
|
135
|
+
return { ...res.data.data, ...agentHintMetadata(res.data) };
|
|
135
136
|
} catch (err: any) {
|
|
136
137
|
const delayMs = autoResume !== false ? processingContinuesDelayMs(err) : undefined;
|
|
137
138
|
if (
|
package/src/v2/methods/search.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type SearchResultImages,
|
|
9
9
|
} from "../types";
|
|
10
10
|
import { HttpClient } from "../utils/httpClient";
|
|
11
|
+
import { agentHintMetadata } from "../utils/agentHints";
|
|
11
12
|
import { ensureValidScrapeOptions } from "../utils/validation";
|
|
12
13
|
import {
|
|
13
14
|
throwForBadResponse,
|
|
@@ -101,7 +102,7 @@ export async function search(
|
|
|
101
102
|
throwForBadResponse(res, "search");
|
|
102
103
|
}
|
|
103
104
|
const data = (res.data.data || {}) as Record<string, any>;
|
|
104
|
-
const out: SearchData = {};
|
|
105
|
+
const out: SearchData = { ...agentHintMetadata(res.data) };
|
|
105
106
|
if (res.data.warning) out.warning = res.data.warning;
|
|
106
107
|
if (data.web) out.web = transformArray<SearchResultWeb>(data.web);
|
|
107
108
|
if (data.news) out.news = transformArray<SearchResultNews>(data.news);
|
package/src/v2/types.ts
CHANGED
|
@@ -678,6 +678,8 @@ export interface DocumentMetadata {
|
|
|
678
678
|
}
|
|
679
679
|
|
|
680
680
|
export interface Document {
|
|
681
|
+
/** API guidance, separate from extracted page content. */
|
|
682
|
+
agent_hints?: string[];
|
|
681
683
|
markdown?: string;
|
|
682
684
|
html?: string;
|
|
683
685
|
rawHtml?: string;
|
|
@@ -820,6 +822,7 @@ export interface SearchResultImages {
|
|
|
820
822
|
}
|
|
821
823
|
|
|
822
824
|
export interface SearchData {
|
|
825
|
+
agent_hints?: string[];
|
|
823
826
|
warning?: string;
|
|
824
827
|
web?: Array<SearchResultWeb | Document>;
|
|
825
828
|
news?: Array<SearchResultNews | Document>;
|
|
@@ -1086,6 +1089,7 @@ export interface BatchScrapeJob {
|
|
|
1086
1089
|
}
|
|
1087
1090
|
|
|
1088
1091
|
export interface MapData {
|
|
1092
|
+
agent_hints?: string[];
|
|
1089
1093
|
id?: string;
|
|
1090
1094
|
links: SearchResultWeb[];
|
|
1091
1095
|
}
|
|
@@ -1449,9 +1453,83 @@ export interface AgentExchangeOptions {
|
|
|
1449
1453
|
toolkits?: string[];
|
|
1450
1454
|
maxCalls?: number;
|
|
1451
1455
|
requireApproval?: boolean;
|
|
1452
|
-
/**
|
|
1456
|
+
/**
|
|
1457
|
+
* Answers a pendingApproval from the previous turn of the thread. A `terms`
|
|
1458
|
+
* approval is accepted or declined as a whole: `callIds` and `always` are
|
|
1459
|
+
* ignored on it.
|
|
1460
|
+
*/
|
|
1453
1461
|
approve?: { approvalId: string; callIds?: string[]; always?: boolean };
|
|
1454
1462
|
decline?: { approvalId: string };
|
|
1463
|
+
/**
|
|
1464
|
+
* What to do when a provider the agent would use needs data terms the team
|
|
1465
|
+
* has not accepted. Gated providers are never called in any mode:
|
|
1466
|
+
* - "skip" (server default): answer with accepted providers only and list
|
|
1467
|
+
* the gated ones in `exchange.skippedProviders`.
|
|
1468
|
+
* - "ask": the same, plus `exchange.requiresAction` and a `terms`
|
|
1469
|
+
* pendingApproval. Get your user's explicit consent, call terms/accept,
|
|
1470
|
+
* then continue the thread with `approve: { approvalId }`.
|
|
1471
|
+
* Terms gating is rolling out: until it is on for a thread, none of the
|
|
1472
|
+
* terms fields appear. There is no auto-accept mode. Omitted on a follow-up turn inherits the
|
|
1473
|
+
* previous turn's value.
|
|
1474
|
+
*/
|
|
1475
|
+
onTermsRequired?: AgentOnTermsRequired;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
export type AgentOnTermsRequired = "skip" | "ask";
|
|
1479
|
+
|
|
1480
|
+
/** A gated provider the run would have used but did not. */
|
|
1481
|
+
export interface AgentSkippedProvider {
|
|
1482
|
+
provider: string;
|
|
1483
|
+
name: string;
|
|
1484
|
+
capability?: string;
|
|
1485
|
+
/** What it would have added, in the agent's words. */
|
|
1486
|
+
adds?: string;
|
|
1487
|
+
reason: "terms_required";
|
|
1488
|
+
/** The gating terms version. */
|
|
1489
|
+
version: string;
|
|
1490
|
+
/** Where a person accepts the terms in the dashboard. */
|
|
1491
|
+
termsUrl: string;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
/**
|
|
1495
|
+
* The exact Exchange calls to view and accept a provider's terms. Nothing here
|
|
1496
|
+
* is executed for you, and terms/accept must only be called after the user
|
|
1497
|
+
* has explicitly agreed to that provider's terms.
|
|
1498
|
+
*/
|
|
1499
|
+
export interface AgentTermsRequiredAction {
|
|
1500
|
+
type: "accept_terms";
|
|
1501
|
+
/**
|
|
1502
|
+
* The `terms` pendingApproval that answers this. After the user agrees and
|
|
1503
|
+
* terms/accept succeeds, continue the thread with
|
|
1504
|
+
* `exchange.approve: { approvalId }`, or refuse with `decline`.
|
|
1505
|
+
*/
|
|
1506
|
+
approvalId: string;
|
|
1507
|
+
providers: {
|
|
1508
|
+
provider: string;
|
|
1509
|
+
name: string;
|
|
1510
|
+
capability?: string;
|
|
1511
|
+
adds?: string;
|
|
1512
|
+
version: string;
|
|
1513
|
+
/** null when the catalog published no digest; terms/show returns it. */
|
|
1514
|
+
digest: string | null;
|
|
1515
|
+
url: string;
|
|
1516
|
+
show: {
|
|
1517
|
+
provider: "firecrawl";
|
|
1518
|
+
capability: "terms/show";
|
|
1519
|
+
options: { provider: string };
|
|
1520
|
+
};
|
|
1521
|
+
accept: {
|
|
1522
|
+
provider: "firecrawl";
|
|
1523
|
+
capability: "terms/accept";
|
|
1524
|
+
options: {
|
|
1525
|
+
provider: string;
|
|
1526
|
+
version: string;
|
|
1527
|
+
/** null when the catalog published no digest; terms/show returns it. */
|
|
1528
|
+
digest: string | null;
|
|
1529
|
+
confirmed: true;
|
|
1530
|
+
};
|
|
1531
|
+
};
|
|
1532
|
+
}[];
|
|
1455
1533
|
}
|
|
1456
1534
|
|
|
1457
1535
|
/** Per-run summary reported on a status response. */
|
|
@@ -1460,8 +1538,26 @@ export interface AgentExchangeSummary {
|
|
|
1460
1538
|
/** What the run resolved to after thread inheritance, not what it requested. */
|
|
1461
1539
|
toolkits?: string[];
|
|
1462
1540
|
requireApproval?: boolean;
|
|
1541
|
+
onTermsRequired?: AgentOnTermsRequired;
|
|
1463
1542
|
paidCalls: number;
|
|
1464
1543
|
creditsUsed: number | null;
|
|
1544
|
+
/** Gated providers that would have helped and were not used. Any mode. */
|
|
1545
|
+
skippedProviders?: AgentSkippedProvider[];
|
|
1546
|
+
/** "ask" mode, when a terms offer ended the turn. */
|
|
1547
|
+
requiresAction?: AgentTermsRequiredAction;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/** A provider in a `terms` pendingApproval. */
|
|
1551
|
+
export interface AgentTermsGate {
|
|
1552
|
+
provider: string;
|
|
1553
|
+
name: string;
|
|
1554
|
+
logo?: string;
|
|
1555
|
+
capability?: string;
|
|
1556
|
+
adds?: string;
|
|
1557
|
+
version: string;
|
|
1558
|
+
/** null when the catalog published no digest; terms/show returns it. */
|
|
1559
|
+
digest: string | null;
|
|
1560
|
+
url: string;
|
|
1465
1561
|
}
|
|
1466
1562
|
|
|
1467
1563
|
/** A follow-up the agent offers for the next turn of the thread. */
|
|
@@ -1470,26 +1566,52 @@ export interface AgentSuggestion {
|
|
|
1470
1566
|
prompt: string;
|
|
1471
1567
|
}
|
|
1472
1568
|
|
|
1473
|
-
/** A
|
|
1474
|
-
export interface
|
|
1569
|
+
/** A paid call a `calls` pendingApproval is holding back. */
|
|
1570
|
+
export interface PendingApprovalCall {
|
|
1571
|
+
id: string;
|
|
1572
|
+
provider: string;
|
|
1573
|
+
capability: string;
|
|
1574
|
+
input: Record<string, unknown>;
|
|
1575
|
+
more?: Record<string, unknown>[];
|
|
1576
|
+
creditsEstimate: number | null;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
interface PendingApprovalBase {
|
|
1475
1580
|
id: string;
|
|
1476
1581
|
reason: string;
|
|
1477
|
-
calls: {
|
|
1478
|
-
id: string;
|
|
1479
|
-
provider: string;
|
|
1480
|
-
capability: string;
|
|
1481
|
-
input: Record<string, unknown>;
|
|
1482
|
-
more?: Record<string, unknown>[];
|
|
1483
|
-
creditsEstimate: number | null;
|
|
1484
|
-
}[];
|
|
1485
1582
|
resolution: null | {
|
|
1486
1583
|
approved: boolean;
|
|
1584
|
+
/** Calls approved. Ignored on terms offers. */
|
|
1487
1585
|
callIds: string[];
|
|
1488
1586
|
always: boolean;
|
|
1489
1587
|
byRunId: string;
|
|
1490
1588
|
};
|
|
1491
1589
|
}
|
|
1492
1590
|
|
|
1591
|
+
/**
|
|
1592
|
+
* A turn that ended waiting for the caller to allow or refuse paid calls.
|
|
1593
|
+
* `kind` is absent on items written before terms offers existed.
|
|
1594
|
+
*/
|
|
1595
|
+
export interface PendingCallsApproval extends PendingApprovalBase {
|
|
1596
|
+
kind?: "calls";
|
|
1597
|
+
calls: PendingApprovalCall[];
|
|
1598
|
+
terms?: never;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/**
|
|
1602
|
+
* A turn that ended waiting for the caller to accept providers' data terms
|
|
1603
|
+
* ("ask" mode). `calls` is always empty (typed `never[]` rather
|
|
1604
|
+
* than `[]` so existing `pendingApproval.calls[0]` code still compiles).
|
|
1605
|
+
*/
|
|
1606
|
+
export interface PendingTermsApproval extends PendingApprovalBase {
|
|
1607
|
+
kind: "terms";
|
|
1608
|
+
calls: never[];
|
|
1609
|
+
terms: AgentTermsGate[];
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
/** Narrow on `kind === "terms"`. */
|
|
1613
|
+
export type PendingApproval = PendingCallsApproval | PendingTermsApproval;
|
|
1614
|
+
|
|
1493
1615
|
export interface AgentResponse {
|
|
1494
1616
|
success: boolean;
|
|
1495
1617
|
id: string;
|
|
@@ -1833,6 +1955,8 @@ export interface CrawlErrorsResponse {
|
|
|
1833
1955
|
url: string;
|
|
1834
1956
|
code?: string;
|
|
1835
1957
|
error: string;
|
|
1958
|
+
/** Present when the page needs provider terms accepted first. */
|
|
1959
|
+
requiresAction?: RequiresAction;
|
|
1836
1960
|
}[];
|
|
1837
1961
|
robotsBlocked: string[];
|
|
1838
1962
|
}
|
|
@@ -1876,6 +2000,7 @@ export function parseRequiresAction(value: unknown): RequiresAction | undefined
|
|
|
1876
2000
|
}
|
|
1877
2001
|
|
|
1878
2002
|
export class SdkError extends Error {
|
|
2003
|
+
declare agent_hints?: string[];
|
|
1879
2004
|
requestId?: string;
|
|
1880
2005
|
status?: number;
|
|
1881
2006
|
code?: string;
|
|
@@ -1956,11 +2081,13 @@ export interface BrowserExecuteResponse {
|
|
|
1956
2081
|
stderr?: string;
|
|
1957
2082
|
exitCode?: number;
|
|
1958
2083
|
killed?: boolean;
|
|
2084
|
+
truncated?: boolean;
|
|
1959
2085
|
error?: string;
|
|
1960
2086
|
}
|
|
1961
2087
|
|
|
1962
2088
|
export interface BrowserDeleteResponse {
|
|
1963
2089
|
success: boolean;
|
|
2090
|
+
status?: string;
|
|
1964
2091
|
sessionDurationMs?: number;
|
|
1965
2092
|
creditsBilled?: number;
|
|
1966
2093
|
error?: string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Keep response guidance as metadata when convenience methods unwrap data. */
|
|
2
|
+
export function agentHintMetadata(body: unknown): { agent_hints?: string[] } {
|
|
3
|
+
const hints = (body as { agent_hints?: unknown } | null)?.agent_hints;
|
|
4
|
+
return Array.isArray(hints) && hints.every((hint) => typeof hint === "string")
|
|
5
|
+
? { agent_hints: [...hints] }
|
|
6
|
+
: {};
|
|
7
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type AxiosError, type AxiosResponse } from "axios";
|
|
2
2
|
import { SdkError, JobTimeoutError, parseRequiresAction } from "../types";
|
|
3
|
+
import { agentHintMetadata } from "./agentHints";
|
|
3
4
|
|
|
4
5
|
export function throwForBadResponse(resp: AxiosResponse, action: string): never {
|
|
5
6
|
const status = resp.status;
|
|
6
7
|
const body = resp.data || {};
|
|
7
8
|
const msg = body?.error || body?.message || `Request failed (${status}) while trying to ${action}`;
|
|
8
|
-
throw new SdkError(
|
|
9
|
+
throw Object.assign(new SdkError(
|
|
9
10
|
msg,
|
|
10
11
|
status,
|
|
11
12
|
body?.code,
|
|
@@ -13,7 +14,7 @@ export function throwForBadResponse(resp: AxiosResponse, action: string): never
|
|
|
13
14
|
undefined,
|
|
14
15
|
body?.chargeId,
|
|
15
16
|
parseRequiresAction(body?.requiresAction),
|
|
16
|
-
);
|
|
17
|
+
), agentHintMetadata(body));
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function normalizeAxiosError(err: AxiosError, action: string): never {
|
|
@@ -21,7 +22,7 @@ export function normalizeAxiosError(err: AxiosError, action: string): never {
|
|
|
21
22
|
const body: any = err.response?.data;
|
|
22
23
|
const message = body?.error || err.message || `Request failed${status ? ` (${status})` : ""} while trying to ${action}`;
|
|
23
24
|
const code = (body?.code as string) || err.code;
|
|
24
|
-
throw new SdkError(
|
|
25
|
+
throw Object.assign(new SdkError(
|
|
25
26
|
message,
|
|
26
27
|
status,
|
|
27
28
|
code,
|
|
@@ -29,7 +30,7 @@ export function normalizeAxiosError(err: AxiosError, action: string): never {
|
|
|
29
30
|
undefined,
|
|
30
31
|
body?.chargeId,
|
|
31
32
|
parseRequiresAction(body?.requiresAction),
|
|
32
|
-
);
|
|
33
|
+
), agentHintMetadata(body));
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export function isRetryableError(err: any): boolean {
|
|
@@ -64,4 +65,3 @@ export function isRetryableError(err: any): boolean {
|
|
|
64
65
|
// Default: retry on unknown errors (safer than not retrying)
|
|
65
66
|
return true;
|
|
66
67
|
}
|
|
67
|
-
|
|
@@ -4,6 +4,7 @@ import axios, {
|
|
|
4
4
|
type AxiosResponse,
|
|
5
5
|
} from "axios";
|
|
6
6
|
import { getVersion } from "./getVersion";
|
|
7
|
+
import { pinToApiOrigin } from "../../utils/apiOrigin";
|
|
7
8
|
|
|
8
9
|
export interface HttpClientOptions {
|
|
9
10
|
apiKey: string;
|
|
@@ -64,6 +65,7 @@ export class HttpClient {
|
|
|
64
65
|
config.headers = {
|
|
65
66
|
...(config.headers || {}),
|
|
66
67
|
};
|
|
68
|
+
if (config.url) config.url = pinToApiOrigin(this.apiUrl, config.url);
|
|
67
69
|
|
|
68
70
|
let lastError: any;
|
|
69
71
|
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|