firecrawl 4.35.0 → 4.37.0
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-666CR5HH.js → chunk-LFX7WDGT.js} +1 -1
- package/dist/index.cjs +92 -15
- package/dist/index.d.cts +174 -2
- package/dist/index.d.ts +174 -2
- package/dist/index.js +93 -16
- package/dist/{package-ZOVFJE7Z.js → package-SHMK2LJE.js} +1 -1
- package/package.json +1 -1
- package/src/__tests__/unit/v2/agent-effort-trace.unit.test.ts +140 -0
- package/src/__tests__/unit/v2/scrapeAutoResume.test.ts +145 -0
- package/src/v1/index.ts +1 -0
- package/src/v2/client.ts +22 -4
- package/src/v2/methods/agent.ts +34 -1
- package/src/v2/methods/scrape.ts +123 -19
- package/src/v2/types.ts +165 -0
- package/src/v2/utils/httpClient.ts +6 -0
package/src/v2/methods/scrape.ts
CHANGED
|
@@ -12,38 +12,142 @@ import {
|
|
|
12
12
|
normalizeAxiosError,
|
|
13
13
|
} from "../utils/errorHandler";
|
|
14
14
|
|
|
15
|
+
/** Auto-resume bounds: a resume only ever follows the server's explicit
|
|
16
|
+
* "processing continues" signal, and stops after this many resumes or
|
|
17
|
+
* this much total sleeping — whichever comes first. */
|
|
18
|
+
const RESUME_MAX_ATTEMPTS = 5;
|
|
19
|
+
const RESUME_MAX_TOTAL_WAIT_MS = 20 * 60 * 1000;
|
|
20
|
+
const RESUME_MIN_DELAY_MS = 5 * 1000;
|
|
21
|
+
const RESUME_MAX_DELAY_MS = 10 * 60 * 1000;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* When a request failed because the document is still processing
|
|
25
|
+
* server-side (large PDFs outlive their request window by design), the
|
|
26
|
+
* error carries `details.state === "processing_continues"` plus a retry
|
|
27
|
+
* delay. Returns that delay in ms, or undefined for every other error.
|
|
28
|
+
*/
|
|
29
|
+
export function processingContinuesDelayMs(err: unknown): number | undefined {
|
|
30
|
+
const response = (err as { isAxiosError?: boolean; response?: any })
|
|
31
|
+
?.isAxiosError
|
|
32
|
+
? (err as { response?: any }).response
|
|
33
|
+
: undefined;
|
|
34
|
+
if (!response || response.status !== 408) return undefined;
|
|
35
|
+
const body = response.data as
|
|
36
|
+
| { code?: string; details?: { state?: string; retryAfterSeconds?: number } }
|
|
37
|
+
| undefined;
|
|
38
|
+
if (
|
|
39
|
+
body?.code !== "SCRAPE_TIMEOUT" ||
|
|
40
|
+
body?.details?.state !== "processing_continues"
|
|
41
|
+
) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const headerSeconds = Number(response.headers?.["retry-after"]);
|
|
45
|
+
const seconds =
|
|
46
|
+
typeof body.details.retryAfterSeconds === "number" &&
|
|
47
|
+
Number.isFinite(body.details.retryAfterSeconds)
|
|
48
|
+
? body.details.retryAfterSeconds
|
|
49
|
+
: Number.isFinite(headerSeconds)
|
|
50
|
+
? headerSeconds
|
|
51
|
+
: 60;
|
|
52
|
+
return Math.min(RESUME_MAX_DELAY_MS, Math.max(RESUME_MIN_DELAY_MS, seconds * 1000));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Options for a direct scrape call. `autoResume` lives here — NOT on the
|
|
57
|
+
* shared ScrapeOptions — because that type is reused verbatim inside
|
|
58
|
+
* batch/crawl/search/extract payloads, where an SDK-only key would leak
|
|
59
|
+
* onto the wire and fail strict endpoints.
|
|
60
|
+
*/
|
|
61
|
+
export type ScrapeCallOptions = ScrapeOptions & {
|
|
62
|
+
/**
|
|
63
|
+
* SDK-only (never sent to the API). Large documents (big PDFs) that
|
|
64
|
+
* outlive the request window keep processing server-side; the API's
|
|
65
|
+
* timeout error then carries `details.state === "processing_continues"`
|
|
66
|
+
* and a Retry-After. When this is not `false`, the SDK sleeps that
|
|
67
|
+
* long and re-issues the same request — the retry attaches to the
|
|
68
|
+
* in-flight job and returns the finished result, so a large document
|
|
69
|
+
* behaves like one slow successful call. Bounded (at most 5 resumes /
|
|
70
|
+
* 20 minutes total wait); set `false` to surface the timeout error
|
|
71
|
+
* immediately instead.
|
|
72
|
+
*/
|
|
73
|
+
autoResume?: boolean;
|
|
74
|
+
};
|
|
75
|
+
|
|
15
76
|
export async function scrape(
|
|
16
77
|
http: HttpClient,
|
|
17
78
|
url: string,
|
|
18
|
-
options?:
|
|
79
|
+
options?: ScrapeCallOptions,
|
|
80
|
+
deps: { sleepImpl?: (ms: number) => Promise<void> } = {},
|
|
19
81
|
): Promise<Document> {
|
|
82
|
+
const sleep =
|
|
83
|
+
deps.sleepImpl ??
|
|
84
|
+
((ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms)));
|
|
20
85
|
if (!url || !url.trim()) {
|
|
21
86
|
throw new Error("URL cannot be empty");
|
|
22
87
|
}
|
|
23
88
|
if (options) ensureValidScrapeOptions(options);
|
|
24
89
|
|
|
90
|
+
// autoResume is SDK behavior, never part of the wire payload.
|
|
91
|
+
const { autoResume, ...requestOptions } = options ?? {};
|
|
25
92
|
const payload: Record<string, unknown> = { url: url.trim() };
|
|
26
|
-
|
|
93
|
+
Object.assign(payload, requestOptions);
|
|
27
94
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
95
|
+
// Per-request timeout. With auto-resume disabled this is exactly the
|
|
96
|
+
// pre-existing behavior (explicit timeout +5s, else the client's own
|
|
97
|
+
// default). With auto-resume on, the client-side timeout must outlast
|
|
98
|
+
// the SERVER's wall — options.timeout, or the API's 5-minute default —
|
|
99
|
+
// otherwise axios can abort in a photo-finish with the server's
|
|
100
|
+
// processing_continues response and the resume signal is lost exactly
|
|
101
|
+
// when it matters. The caller's configured client timeout is
|
|
102
|
+
// respected as a floor, never overridden downward.
|
|
103
|
+
const requestTimeout: { timeoutMs?: number } =
|
|
104
|
+
autoResume === false
|
|
105
|
+
? typeof options?.timeout === "number"
|
|
37
106
|
? { timeoutMs: options.timeout + 5000 }
|
|
38
|
-
: {}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
107
|
+
: {}
|
|
108
|
+
: {
|
|
109
|
+
timeoutMs:
|
|
110
|
+
Math.max(
|
|
111
|
+
typeof options?.timeout === "number" ? options.timeout : 300_000,
|
|
112
|
+
typeof http.getTimeoutMs === "function" ? http.getTimeoutMs() : 0,
|
|
113
|
+
) + 30_000,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
let resumes = 0;
|
|
117
|
+
let resumeWaitedMs = 0;
|
|
118
|
+
while (true) {
|
|
119
|
+
try {
|
|
120
|
+
const res = await http.post<{
|
|
121
|
+
success: boolean;
|
|
122
|
+
data?: Document;
|
|
123
|
+
error?: string;
|
|
124
|
+
}>(
|
|
125
|
+
"/v2/scrape",
|
|
126
|
+
payload,
|
|
127
|
+
requestTimeout,
|
|
128
|
+
);
|
|
129
|
+
if (res.status !== 200 || !res.data?.success) {
|
|
130
|
+
throwForBadResponse(res, "scrape");
|
|
131
|
+
}
|
|
132
|
+
return (res.data.data || {}) as Document;
|
|
133
|
+
} catch (err: any) {
|
|
134
|
+
const delayMs = autoResume !== false ? processingContinuesDelayMs(err) : undefined;
|
|
135
|
+
if (
|
|
136
|
+
delayMs !== undefined &&
|
|
137
|
+
resumes < RESUME_MAX_ATTEMPTS &&
|
|
138
|
+
resumeWaitedMs + delayMs <= RESUME_MAX_TOTAL_WAIT_MS
|
|
139
|
+
) {
|
|
140
|
+
// The document keeps processing server-side; the retry attaches
|
|
141
|
+
// to the same in-flight job (content adoption) and returns the
|
|
142
|
+
// finished result instead of restarting the work.
|
|
143
|
+
resumes += 1;
|
|
144
|
+
resumeWaitedMs += delayMs;
|
|
145
|
+
await sleep(delayMs);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (err?.isAxiosError) return normalizeAxiosError(err, "scrape");
|
|
149
|
+
throw err;
|
|
42
150
|
}
|
|
43
|
-
return (res.data.data || {}) as Document;
|
|
44
|
-
} catch (err: any) {
|
|
45
|
-
if (err?.isAxiosError) return normalizeAxiosError(err, "scrape");
|
|
46
|
-
throw err;
|
|
47
151
|
}
|
|
48
152
|
}
|
|
49
153
|
|
package/src/v2/types.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface JsonFormat extends Format {
|
|
|
31
31
|
type: "json";
|
|
32
32
|
prompt?: string;
|
|
33
33
|
schema?: Record<string, unknown> | ZodTypeAny;
|
|
34
|
+
checkPromptInjection?: boolean;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export interface ScreenshotFormat {
|
|
@@ -1327,10 +1328,174 @@ export interface AgentStatusResponse {
|
|
|
1327
1328
|
* names makes every future model a type error at the call site.
|
|
1328
1329
|
*/
|
|
1329
1330
|
model?: "spark-1-pro" | "spark-1-mini" | "spark-2" | (string & {});
|
|
1331
|
+
/**
|
|
1332
|
+
* Reasoning effort the job ran with. Only set for runs that specified it;
|
|
1333
|
+
* older rows and default runs have no effort.
|
|
1334
|
+
*/
|
|
1335
|
+
effort?: "low" | "medium" | "high";
|
|
1330
1336
|
expiresAt: string;
|
|
1331
1337
|
creditsUsed?: number;
|
|
1332
1338
|
}
|
|
1333
1339
|
|
|
1340
|
+
/** Reasoning effort for agent jobs. Every level runs spark-2. */
|
|
1341
|
+
export type AgentEffort = "low" | "medium" | "high";
|
|
1342
|
+
|
|
1343
|
+
export type AgentTraceAgentRole = "orchestrator" | "subagent" | "browser" | "system";
|
|
1344
|
+
|
|
1345
|
+
export interface AgentTraceAgentIdentity {
|
|
1346
|
+
id: string;
|
|
1347
|
+
role: AgentTraceAgentRole;
|
|
1348
|
+
name: string;
|
|
1349
|
+
parentId?: string;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
export interface AgentTraceError {
|
|
1353
|
+
code: "cancelled" | "credit_limit_reached" | "parent_finished" | "refused" | "internal";
|
|
1354
|
+
source: "agent" | "tool" | "billing" | "system";
|
|
1355
|
+
retryable: boolean;
|
|
1356
|
+
message: string;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
export interface AgentTraceArtifactChange {
|
|
1360
|
+
kind: "json" | "markdown" | "html" | "screenshot" | "text";
|
|
1361
|
+
artifactId: string;
|
|
1362
|
+
path?: string;
|
|
1363
|
+
/** Fetch the full content via getAgentSnapshot(jobId, snapshotId). */
|
|
1364
|
+
snapshotId: string;
|
|
1365
|
+
change: "init" | "partial" | "append" | "modify" | "update";
|
|
1366
|
+
changedFields?: string[];
|
|
1367
|
+
itemCount?: number;
|
|
1368
|
+
sourceToolCallId?: string;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
interface AgentTraceEventBase {
|
|
1372
|
+
schemaVersion: 1;
|
|
1373
|
+
eventId: string;
|
|
1374
|
+
runId: string;
|
|
1375
|
+
occurredAt: string;
|
|
1376
|
+
producerSequence: number;
|
|
1377
|
+
agent: AgentTraceAgentIdentity;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
export interface AgentTraceRunStartedEvent extends AgentTraceEventBase {
|
|
1381
|
+
type: "run.started";
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
export interface AgentTraceRunCancelRequestedEvent extends AgentTraceEventBase {
|
|
1385
|
+
type: "run.cancel_requested";
|
|
1386
|
+
reason: "user";
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
export interface AgentTraceRunFinishedEvent extends AgentTraceEventBase {
|
|
1390
|
+
type: "run.finished";
|
|
1391
|
+
outcome: "succeeded" | "failed" | "cancelled" | "refused" | "credit_limit_reached";
|
|
1392
|
+
/** The canonical schema always writes this key (nullable), but older rows may omit it. */
|
|
1393
|
+
error?: AgentTraceError | null;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
export interface AgentTraceAgentStartedEvent extends AgentTraceEventBase {
|
|
1397
|
+
type: "agent.started";
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
export interface AgentTraceAgentFinishedEvent extends AgentTraceEventBase {
|
|
1401
|
+
type: "agent.finished";
|
|
1402
|
+
outcome: "succeeded" | "failed" | "cancelled" | "refused";
|
|
1403
|
+
durationMs: number;
|
|
1404
|
+
/** The canonical schema always writes this key (nullable), but older rows may omit it. */
|
|
1405
|
+
error?: AgentTraceError | null;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
export interface AgentTraceBrowserSessionStartedEvent extends AgentTraceEventBase {
|
|
1409
|
+
type: "browser.session.started";
|
|
1410
|
+
sessionId: string;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
export interface AgentTraceBrowserSessionFinishedEvent extends AgentTraceEventBase {
|
|
1414
|
+
type: "browser.session.finished";
|
|
1415
|
+
sessionId: string;
|
|
1416
|
+
durationMs: number;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
export interface AgentTraceProgressReportedEvent extends AgentTraceEventBase {
|
|
1420
|
+
type: "progress.reported";
|
|
1421
|
+
phase: "planning" | "working" | "finalizing";
|
|
1422
|
+
message: string;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
export interface AgentTraceReasoningSummaryEvent extends AgentTraceEventBase {
|
|
1426
|
+
type: "reasoning.summary";
|
|
1427
|
+
text: string;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
export interface AgentTraceToolCallStartedEvent extends AgentTraceEventBase {
|
|
1431
|
+
type: "tool_call.started";
|
|
1432
|
+
toolCallId: string;
|
|
1433
|
+
toolName: string;
|
|
1434
|
+
parameters: unknown;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
export interface AgentTraceToolCallFinishedEvent extends AgentTraceEventBase {
|
|
1438
|
+
type: "tool_call.finished";
|
|
1439
|
+
toolCallId: string;
|
|
1440
|
+
toolName: string;
|
|
1441
|
+
result: unknown;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
export interface AgentTraceArtifactUpdatedEvent extends AgentTraceEventBase {
|
|
1445
|
+
type: "artifact.updated";
|
|
1446
|
+
artifact: AgentTraceArtifactChange;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
export interface AgentTraceErrorOccurredEvent extends AgentTraceEventBase {
|
|
1450
|
+
type: "error.occurred";
|
|
1451
|
+
error: AgentTraceError;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* One event in an agent job's trace. Mirrors the canonical event schema of
|
|
1456
|
+
* the agent service (schemaVersion 1); `usage.recorded` events are withheld
|
|
1457
|
+
* server-side and `agent.started` carries no model name.
|
|
1458
|
+
*/
|
|
1459
|
+
export type AgentTraceEvent =
|
|
1460
|
+
| AgentTraceRunStartedEvent
|
|
1461
|
+
| AgentTraceRunCancelRequestedEvent
|
|
1462
|
+
| AgentTraceRunFinishedEvent
|
|
1463
|
+
| AgentTraceAgentStartedEvent
|
|
1464
|
+
| AgentTraceAgentFinishedEvent
|
|
1465
|
+
| AgentTraceBrowserSessionStartedEvent
|
|
1466
|
+
| AgentTraceBrowserSessionFinishedEvent
|
|
1467
|
+
| AgentTraceProgressReportedEvent
|
|
1468
|
+
| AgentTraceReasoningSummaryEvent
|
|
1469
|
+
| AgentTraceToolCallStartedEvent
|
|
1470
|
+
| AgentTraceToolCallFinishedEvent
|
|
1471
|
+
| AgentTraceArtifactUpdatedEvent
|
|
1472
|
+
| AgentTraceErrorOccurredEvent;
|
|
1473
|
+
|
|
1474
|
+
export interface AgentTraceActiveBrowserSession {
|
|
1475
|
+
id: string;
|
|
1476
|
+
liveViewUrl: string;
|
|
1477
|
+
viewport: { width: number; height: number };
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
export interface AgentTraceResponse {
|
|
1481
|
+
success: boolean;
|
|
1482
|
+
id?: string;
|
|
1483
|
+
events?: AgentTraceEvent[];
|
|
1484
|
+
creditsUsed?: number;
|
|
1485
|
+
/** Present only when the trace was requested with liveView: true. */
|
|
1486
|
+
activeBrowserSessions?: AgentTraceActiveBrowserSession[];
|
|
1487
|
+
error?: string;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
export interface AgentSnapshotResponse {
|
|
1491
|
+
success: boolean;
|
|
1492
|
+
id?: string;
|
|
1493
|
+
snapshotId?: string;
|
|
1494
|
+
/** Full artifact content as a JSON/string blob. */
|
|
1495
|
+
snapshot?: string;
|
|
1496
|
+
error?: string;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1334
1499
|
export interface AgentOptions {
|
|
1335
1500
|
model: "FIRE-1" | "v3-beta";
|
|
1336
1501
|
}
|
|
@@ -51,6 +51,12 @@ export class HttpClient {
|
|
|
51
51
|
return this.apiKey;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/** The client-wide default request timeout, for callers that need to
|
|
55
|
+
* extend a specific request beyond it (e.g. scrape auto-resume). */
|
|
56
|
+
getTimeoutMs(): number {
|
|
57
|
+
return (this.instance.defaults.timeout as number | undefined) ?? 300000;
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
private async request<T = any>(
|
|
55
61
|
config: AxiosRequestConfig,
|
|
56
62
|
): Promise<AxiosResponse<T>> {
|