@meetopenbot/firecrawl 1.0.2 → 1.0.4
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/index.js +267 -158
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1,34 +1,63 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
1
|
+
// node_modules/@meetopenbot/plugin-sdk/dist/plugin.js
|
|
2
|
+
function definePlugin(definition) {
|
|
3
|
+
return definition;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// node_modules/@meetopenbot/plugin-sdk/dist/helpers.js
|
|
7
|
+
function shouldHandleInvoke(event, agentId) {
|
|
8
|
+
const routedTo = event.data?.agentId;
|
|
9
|
+
return !(typeof routedTo === "string" && routedTo && routedTo !== agentId);
|
|
10
|
+
}
|
|
11
|
+
function agentOutput(args) {
|
|
12
|
+
return {
|
|
13
|
+
type: "agent:output",
|
|
14
|
+
data: { content: args.content },
|
|
15
|
+
meta: {
|
|
16
|
+
...args.meta ?? {},
|
|
17
|
+
agentId: args.agentId,
|
|
18
|
+
...args.threadId ? { threadId: args.threadId } : {}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function uiWidget(args) {
|
|
23
|
+
return {
|
|
24
|
+
type: "client:ui:widget",
|
|
25
|
+
data: args.widget,
|
|
26
|
+
meta: {
|
|
27
|
+
...args.meta ?? {},
|
|
28
|
+
agentId: args.agentId,
|
|
29
|
+
...args.threadId ? { threadId: args.threadId } : {}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/constants.ts
|
|
35
|
+
var DEFAULT_API_URL = "https://api.firecrawl.dev";
|
|
3
36
|
var POLL_INTERVAL_MS = 3e3;
|
|
4
37
|
var POLL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
envVar: "FIRECRAWL_API_KEY",
|
|
26
|
-
source: "firecrawl"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
meta: { agentId, threadId }
|
|
30
|
-
});
|
|
38
|
+
var API_KEY_ENV_VAR = "FIRECRAWL_API_KEY";
|
|
39
|
+
|
|
40
|
+
// src/utils.ts
|
|
41
|
+
var readEnv = () => globalThis.process?.env ?? {};
|
|
42
|
+
var resolveApiUrl = (config) => {
|
|
43
|
+
const configured = config.apiUrl;
|
|
44
|
+
if (typeof configured === "string" && configured.trim()) {
|
|
45
|
+
return configured.trim();
|
|
46
|
+
}
|
|
47
|
+
const envUrl = readEnv().FIRECRAWL_API_URL;
|
|
48
|
+
return envUrl?.trim() || DEFAULT_API_URL;
|
|
49
|
+
};
|
|
50
|
+
var resolveApiKey = (config) => {
|
|
51
|
+
const configured = config.apiKey;
|
|
52
|
+
if (typeof configured === "string" && configured.trim()) {
|
|
53
|
+
return configured.trim();
|
|
54
|
+
}
|
|
55
|
+
const envKey = readEnv()[API_KEY_ENV_VAR];
|
|
56
|
+
return envKey?.trim() || void 0;
|
|
57
|
+
};
|
|
31
58
|
var isAuthError = (message) => !!message && (/unauthor/i.test(message) || /api[\s-]?key/i.test(message) || /401|403/.test(message));
|
|
59
|
+
|
|
60
|
+
// src/client.ts
|
|
32
61
|
async function firecrawlRequest(apiKey, apiUrl, path, init = { method: "GET" }) {
|
|
33
62
|
const res = await fetch(`${apiUrl.replace(/\/$/, "")}${path}`, {
|
|
34
63
|
method: init.method,
|
|
@@ -45,153 +74,233 @@ async function firecrawlRequest(apiKey, apiUrl, path, init = { method: "GET" })
|
|
|
45
74
|
payload = null;
|
|
46
75
|
}
|
|
47
76
|
if (!res.ok) {
|
|
48
|
-
const
|
|
77
|
+
const errorPayload = payload;
|
|
78
|
+
const message = errorPayload?.error || errorPayload?.message || `Firecrawl request failed (${res.status} ${res.statusText})`;
|
|
49
79
|
const err = new Error(message);
|
|
50
80
|
err.status = res.status;
|
|
51
81
|
throw err;
|
|
52
82
|
}
|
|
53
83
|
return payload;
|
|
54
84
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
85
|
+
|
|
86
|
+
// src/ui.ts
|
|
87
|
+
var buildApiKeyWidget = (agentId, threadId, reason) => uiWidget({
|
|
88
|
+
agentId,
|
|
89
|
+
threadId,
|
|
90
|
+
widget: {
|
|
91
|
+
kind: "form",
|
|
92
|
+
widgetId: `firecrawl_api_key_request_${Date.now()}`,
|
|
93
|
+
title: "Firecrawl API Key Required",
|
|
94
|
+
description: `Firecrawl could not authenticate${reason ? ` (${reason})` : ""}. Provide a Firecrawl API key to continue. The key is stored as a workspace variable on your machine and never leaves your local runtime.`,
|
|
95
|
+
fields: [
|
|
96
|
+
{
|
|
97
|
+
id: "apiKey",
|
|
98
|
+
label: "API Key",
|
|
99
|
+
type: "text",
|
|
100
|
+
placeholder: "fc-...",
|
|
101
|
+
required: true
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
submitLabel: "Save API Key",
|
|
105
|
+
metadata: {
|
|
106
|
+
type: "api_key_request",
|
|
107
|
+
provider: "firecrawl",
|
|
108
|
+
envVar: API_KEY_ENV_VAR,
|
|
109
|
+
source: "firecrawl"
|
|
66
110
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// src/index.ts
|
|
115
|
+
var plugin = definePlugin({
|
|
116
|
+
name: "Firecrawl",
|
|
117
|
+
description: "Firecrawl agent for web data gathering",
|
|
118
|
+
configSchema: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
apiKey: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Firecrawl API key. Falls back to the FIRECRAWL_API_KEY workspace variable.",
|
|
124
|
+
format: "password"
|
|
125
|
+
},
|
|
126
|
+
apiUrl: {
|
|
127
|
+
type: "string",
|
|
128
|
+
description: "Firecrawl API base URL.",
|
|
129
|
+
format: "url",
|
|
130
|
+
default: DEFAULT_API_URL
|
|
131
|
+
}
|
|
71
132
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
133
|
+
},
|
|
134
|
+
factory: (context) => (builder) => {
|
|
135
|
+
const apiUrl = resolveApiUrl(context.config);
|
|
136
|
+
const getApiKey = () => resolveApiKey(context.config);
|
|
137
|
+
builder.on("agent:invoke", async function* (event, ctx) {
|
|
138
|
+
if (!shouldHandleInvoke(event, context.agentId)) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const { content } = event.data;
|
|
142
|
+
const threadId = event.meta?.threadId ?? ctx.state.threadId;
|
|
143
|
+
if (!content) {
|
|
144
|
+
yield agentOutput({
|
|
145
|
+
agentId: context.agentId,
|
|
146
|
+
content: "No prompt provided.",
|
|
147
|
+
threadId
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const apiKey = getApiKey();
|
|
152
|
+
if (!apiKey) {
|
|
153
|
+
yield buildApiKeyWidget(context.agentId, threadId);
|
|
154
|
+
yield agentOutput({
|
|
155
|
+
agentId: context.agentId,
|
|
156
|
+
content: "API key required. Please provide it in the widget.",
|
|
157
|
+
threadId
|
|
158
|
+
});
|
|
88
159
|
return;
|
|
89
160
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
161
|
+
try {
|
|
162
|
+
yield agentOutput({
|
|
163
|
+
agentId: context.agentId,
|
|
164
|
+
content: "Starting Firecrawl agent...",
|
|
165
|
+
threadId
|
|
166
|
+
});
|
|
167
|
+
const started = await firecrawlRequest(apiKey, apiUrl, "/v2/agent", {
|
|
168
|
+
method: "POST",
|
|
169
|
+
body: { prompt: content }
|
|
170
|
+
});
|
|
171
|
+
if (!started?.success || !started.id) {
|
|
172
|
+
const errMsg = started?.error || "Unknown error";
|
|
173
|
+
if (isAuthError(errMsg)) {
|
|
174
|
+
yield buildApiKeyWidget(context.agentId, threadId, errMsg);
|
|
175
|
+
yield agentOutput({
|
|
176
|
+
agentId: context.agentId,
|
|
177
|
+
content: `Authentication failed: ${errMsg}. Please update the API key in the widget.`,
|
|
178
|
+
threadId
|
|
179
|
+
});
|
|
180
|
+
} else {
|
|
181
|
+
yield agentOutput({
|
|
182
|
+
agentId: context.agentId,
|
|
183
|
+
content: `Error starting Firecrawl agent: ${errMsg}`,
|
|
184
|
+
threadId
|
|
185
|
+
});
|
|
186
|
+
}
|
|
102
187
|
return;
|
|
103
188
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
189
|
+
yield agentOutput({
|
|
190
|
+
agentId: context.agentId,
|
|
191
|
+
content: `Task created (id: ${started.id}). Waiting for results...`,
|
|
192
|
+
threadId
|
|
193
|
+
});
|
|
194
|
+
const startTime = Date.now();
|
|
195
|
+
let lastStatus = "";
|
|
196
|
+
while (true) {
|
|
197
|
+
if (Date.now() - startTime > POLL_TIMEOUT_MS) {
|
|
198
|
+
yield agentOutput({
|
|
199
|
+
agentId: context.agentId,
|
|
200
|
+
content: `Timed out after ${Math.round(POLL_TIMEOUT_MS / 1e3)}s.`,
|
|
201
|
+
threadId
|
|
202
|
+
});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const status = await firecrawlRequest(
|
|
206
|
+
apiKey,
|
|
207
|
+
apiUrl,
|
|
208
|
+
`/v2/agent/${started.id}`
|
|
209
|
+
);
|
|
210
|
+
if (status.status === "completed") {
|
|
211
|
+
yield agentOutput({
|
|
212
|
+
agentId: context.agentId,
|
|
213
|
+
content: "Completed successfully.",
|
|
214
|
+
threadId
|
|
215
|
+
});
|
|
216
|
+
yield agentOutput({
|
|
217
|
+
agentId: context.agentId,
|
|
218
|
+
content: "```json\n" + JSON.stringify(status.data ?? {}, null, 2) + "\n```",
|
|
219
|
+
threadId
|
|
220
|
+
});
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (status.status === "failed" || status.status === "cancelled") {
|
|
224
|
+
yield agentOutput({
|
|
225
|
+
agentId: context.agentId,
|
|
226
|
+
content: `Agent ${status.status}: ${status.error || "no details provided"}`,
|
|
227
|
+
threadId
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (status.status && status.status !== lastStatus) {
|
|
232
|
+
yield agentOutput({
|
|
233
|
+
agentId: context.agentId,
|
|
234
|
+
content: `Status: ${status.status}...`,
|
|
235
|
+
threadId
|
|
236
|
+
});
|
|
237
|
+
lastStatus = status.status;
|
|
238
|
+
}
|
|
239
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
240
|
+
}
|
|
241
|
+
} catch (error) {
|
|
242
|
+
const message = error instanceof Error ? error.message : "Firecrawl request failed.";
|
|
243
|
+
if (isAuthError(message)) {
|
|
244
|
+
yield buildApiKeyWidget(context.agentId, threadId, message);
|
|
245
|
+
yield agentOutput({
|
|
246
|
+
agentId: context.agentId,
|
|
247
|
+
content: `Authentication failed: ${message}. Please update the API key in the widget.`,
|
|
248
|
+
threadId
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
yield agentOutput({
|
|
252
|
+
agentId: context.agentId,
|
|
253
|
+
content: `Error: ${message}`,
|
|
254
|
+
threadId
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
builder.on(
|
|
260
|
+
"client:ui:widget:response",
|
|
261
|
+
async function* (event, handlerCtx) {
|
|
262
|
+
const { metadata, values, widgetId } = event.data;
|
|
263
|
+
if (!metadata || metadata.type !== "api_key_request" || metadata.source !== "firecrawl") {
|
|
115
264
|
return;
|
|
116
265
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
type: "agent:output",
|
|
120
|
-
data: {
|
|
121
|
-
content: `Firecrawl agent ${status.status}: ${status.error || "no details provided"}`
|
|
122
|
-
}
|
|
123
|
-
};
|
|
266
|
+
const apiKey = values?.apiKey;
|
|
267
|
+
if (typeof apiKey !== "string" || !apiKey) {
|
|
124
268
|
return;
|
|
125
269
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
270
|
+
const envVar = typeof metadata.envVar === "string" && metadata.envVar.trim() ? metadata.envVar : API_KEY_ENV_VAR;
|
|
271
|
+
try {
|
|
272
|
+
await context.storage.createVariable({ key: envVar, value: apiKey, secret: true });
|
|
273
|
+
readEnv()[envVar] = apiKey;
|
|
274
|
+
yield uiWidget({
|
|
275
|
+
agentId: context.agentId,
|
|
276
|
+
widget: {
|
|
277
|
+
widgetId,
|
|
278
|
+
kind: "message",
|
|
279
|
+
title: "API Key Saved",
|
|
280
|
+
body: `Saved ${envVar} as a workspace variable. You can now continue the conversation.`,
|
|
281
|
+
state: "submitted",
|
|
282
|
+
actions: [{ id: "ok", label: "Got it", variant: "primary" }]
|
|
283
|
+
},
|
|
284
|
+
meta: { agentId: handlerCtx.state.agentId }
|
|
285
|
+
});
|
|
286
|
+
yield agentOutput({
|
|
287
|
+
agentId: context.agentId,
|
|
288
|
+
content: "API key saved to workspace variables. Re-send your last message to retry.",
|
|
289
|
+
meta: { agentId: handlerCtx.state.agentId }
|
|
290
|
+
});
|
|
291
|
+
} catch (error) {
|
|
292
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
293
|
+
yield agentOutput({
|
|
294
|
+
agentId: context.agentId,
|
|
295
|
+
content: `Failed to save API key: ${errorMessage}`,
|
|
296
|
+
meta: { agentId: handlerCtx.state.agentId }
|
|
297
|
+
});
|
|
129
298
|
}
|
|
130
|
-
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
131
|
-
}
|
|
132
|
-
} catch (error) {
|
|
133
|
-
const message = error?.message || "Firecrawl request failed.";
|
|
134
|
-
if (isAuthError(message)) {
|
|
135
|
-
yield buildApiKeyWidget(ctx.state.agentId, threadId, message);
|
|
136
|
-
} else {
|
|
137
|
-
yield { type: "agent:output", data: { content: `Error: ${message}` } };
|
|
138
299
|
}
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (!metadata || metadata.type !== "api_key_request" || metadata.source !== "firecrawl") return;
|
|
144
|
-
const apiKey = values?.apiKey;
|
|
145
|
-
if (typeof apiKey !== "string" || !apiKey) return;
|
|
146
|
-
const envVar = typeof metadata.envVar === "string" ? metadata.envVar : "FIRECRAWL_API_KEY";
|
|
147
|
-
if (!storage) {
|
|
148
|
-
yield {
|
|
149
|
-
type: "agent:output",
|
|
150
|
-
data: { content: "[firecrawl] no storage available; cannot persist API key." },
|
|
151
|
-
meta: { agentId: context.state.agentId }
|
|
152
|
-
};
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
try {
|
|
156
|
-
await storage.createVariable({ key: envVar, value: apiKey, secret: true });
|
|
157
|
-
env[envVar] = apiKey;
|
|
158
|
-
yield {
|
|
159
|
-
type: "client:ui:widget",
|
|
160
|
-
data: {
|
|
161
|
-
widgetId,
|
|
162
|
-
kind: "message",
|
|
163
|
-
title: "API Key Saved",
|
|
164
|
-
body: `Saved ${envVar} as a workspace variable. You can now continue the conversation.`,
|
|
165
|
-
state: "submitted",
|
|
166
|
-
actions: [{ id: "ok", label: "Got it", variant: "primary" }]
|
|
167
|
-
},
|
|
168
|
-
meta: { agentId: context.state.agentId }
|
|
169
|
-
};
|
|
170
|
-
yield {
|
|
171
|
-
type: "agent:output",
|
|
172
|
-
data: {
|
|
173
|
-
content: "Saved Firecrawl API key to workspace variables. Re-send your last message to retry."
|
|
174
|
-
},
|
|
175
|
-
meta: { agentId: context.state.agentId }
|
|
176
|
-
};
|
|
177
|
-
} catch (error) {
|
|
178
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
179
|
-
yield {
|
|
180
|
-
type: "agent:output",
|
|
181
|
-
data: { content: `[firecrawl] failed to save API key: ${errorMessage}` },
|
|
182
|
-
meta: { agentId: context.state.agentId }
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
};
|
|
187
|
-
var plugin = {
|
|
188
|
-
id: "firecrawl",
|
|
189
|
-
name: "Firecrawl",
|
|
190
|
-
description: "Firecrawl agent for web data gathering",
|
|
191
|
-
kind: "runtime",
|
|
192
|
-
factory: (options) => firecrawlPlugin(options)
|
|
193
|
-
};
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
var src_default = plugin;
|
|
194
304
|
export {
|
|
195
|
-
|
|
196
|
-
plugin
|
|
305
|
+
src_default as default
|
|
197
306
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/firecrawl",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Firecrawl agent plugin for OpenBot",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dist"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build": "esbuild index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js",
|
|
17
|
+
"build": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js",
|
|
18
18
|
"prepublishOnly": "npm run build"
|
|
19
19
|
},
|
|
20
20
|
"engines": {
|
|
@@ -23,5 +23,8 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^20.10.1",
|
|
25
25
|
"esbuild": "^0.21.0"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@meetopenbot/plugin-sdk": "^0.1.1"
|
|
26
29
|
}
|
|
27
30
|
}
|