@meetopenbot/firecrawl 1.0.2 → 1.0.3
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 +252 -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,218 @@ 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);
|
|
88
154
|
return;
|
|
89
155
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
156
|
+
try {
|
|
157
|
+
yield agentOutput({
|
|
158
|
+
agentId: context.agentId,
|
|
159
|
+
content: "Starting Firecrawl agent...",
|
|
160
|
+
threadId
|
|
161
|
+
});
|
|
162
|
+
const started = await firecrawlRequest(apiKey, apiUrl, "/v2/agent", {
|
|
163
|
+
method: "POST",
|
|
164
|
+
body: { prompt: content }
|
|
165
|
+
});
|
|
166
|
+
if (!started?.success || !started.id) {
|
|
167
|
+
const errMsg = started?.error || "Unknown error";
|
|
168
|
+
if (isAuthError(errMsg)) {
|
|
169
|
+
yield buildApiKeyWidget(context.agentId, threadId, errMsg);
|
|
170
|
+
} else {
|
|
171
|
+
yield agentOutput({
|
|
172
|
+
agentId: context.agentId,
|
|
173
|
+
content: `Error starting Firecrawl agent: ${errMsg}`,
|
|
174
|
+
threadId
|
|
175
|
+
});
|
|
176
|
+
}
|
|
102
177
|
return;
|
|
103
178
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
179
|
+
yield agentOutput({
|
|
180
|
+
agentId: context.agentId,
|
|
181
|
+
content: `Firecrawl agent task created (id: ${started.id}). Waiting for results...`,
|
|
182
|
+
threadId
|
|
183
|
+
});
|
|
184
|
+
const startTime = Date.now();
|
|
185
|
+
let lastStatus = "";
|
|
186
|
+
while (true) {
|
|
187
|
+
if (Date.now() - startTime > POLL_TIMEOUT_MS) {
|
|
188
|
+
yield agentOutput({
|
|
189
|
+
agentId: context.agentId,
|
|
190
|
+
content: `Firecrawl agent timed out after ${Math.round(POLL_TIMEOUT_MS / 1e3)}s.`,
|
|
191
|
+
threadId
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const status = await firecrawlRequest(
|
|
196
|
+
apiKey,
|
|
197
|
+
apiUrl,
|
|
198
|
+
`/v2/agent/${started.id}`
|
|
199
|
+
);
|
|
200
|
+
if (status.status === "completed") {
|
|
201
|
+
yield agentOutput({
|
|
202
|
+
agentId: context.agentId,
|
|
203
|
+
content: "Firecrawl agent completed successfully.",
|
|
204
|
+
threadId
|
|
205
|
+
});
|
|
206
|
+
yield agentOutput({
|
|
207
|
+
agentId: context.agentId,
|
|
208
|
+
content: "```json\n" + JSON.stringify(status.data ?? {}, null, 2) + "\n```",
|
|
209
|
+
threadId
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (status.status === "failed" || status.status === "cancelled") {
|
|
214
|
+
yield agentOutput({
|
|
215
|
+
agentId: context.agentId,
|
|
216
|
+
content: `Firecrawl agent ${status.status}: ${status.error || "no details provided"}`,
|
|
217
|
+
threadId
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (status.status && status.status !== lastStatus) {
|
|
222
|
+
yield agentOutput({
|
|
223
|
+
agentId: context.agentId,
|
|
224
|
+
content: `Status: ${status.status}...`,
|
|
225
|
+
threadId
|
|
226
|
+
});
|
|
227
|
+
lastStatus = status.status;
|
|
228
|
+
}
|
|
229
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
const message = error instanceof Error ? error.message : "Firecrawl request failed.";
|
|
233
|
+
if (isAuthError(message)) {
|
|
234
|
+
yield buildApiKeyWidget(context.agentId, threadId, message);
|
|
235
|
+
} else {
|
|
236
|
+
yield agentOutput({
|
|
237
|
+
agentId: context.agentId,
|
|
238
|
+
content: `Error: ${message}`,
|
|
239
|
+
threadId
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
builder.on(
|
|
245
|
+
"plugin:ui:widget:response",
|
|
246
|
+
async function* (event, handlerCtx) {
|
|
247
|
+
const { metadata, values, widgetId } = event.data;
|
|
248
|
+
if (!metadata || metadata.type !== "api_key_request" || metadata.source !== "firecrawl") {
|
|
115
249
|
return;
|
|
116
250
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
type: "agent:output",
|
|
120
|
-
data: {
|
|
121
|
-
content: `Firecrawl agent ${status.status}: ${status.error || "no details provided"}`
|
|
122
|
-
}
|
|
123
|
-
};
|
|
251
|
+
const apiKey = values?.apiKey;
|
|
252
|
+
if (typeof apiKey !== "string" || !apiKey) {
|
|
124
253
|
return;
|
|
125
254
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
255
|
+
const envVar = typeof metadata.envVar === "string" && metadata.envVar.trim() ? metadata.envVar : API_KEY_ENV_VAR;
|
|
256
|
+
try {
|
|
257
|
+
await context.storage.createVariable({ key: envVar, value: apiKey, secret: true });
|
|
258
|
+
readEnv()[envVar] = apiKey;
|
|
259
|
+
yield uiWidget({
|
|
260
|
+
agentId: context.agentId,
|
|
261
|
+
widget: {
|
|
262
|
+
widgetId,
|
|
263
|
+
kind: "message",
|
|
264
|
+
title: "API Key Saved",
|
|
265
|
+
body: `Saved ${envVar} as a workspace variable. You can now continue the conversation.`,
|
|
266
|
+
state: "submitted",
|
|
267
|
+
actions: [{ id: "ok", label: "Got it", variant: "primary" }]
|
|
268
|
+
},
|
|
269
|
+
meta: { agentId: handlerCtx.state.agentId }
|
|
270
|
+
});
|
|
271
|
+
yield agentOutput({
|
|
272
|
+
agentId: context.agentId,
|
|
273
|
+
content: "Saved Firecrawl API key to workspace variables. Re-send your last message to retry.",
|
|
274
|
+
meta: { agentId: handlerCtx.state.agentId }
|
|
275
|
+
});
|
|
276
|
+
} catch (error) {
|
|
277
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
278
|
+
yield agentOutput({
|
|
279
|
+
agentId: context.agentId,
|
|
280
|
+
content: `[firecrawl] failed to save API key: ${errorMessage}`,
|
|
281
|
+
meta: { agentId: handlerCtx.state.agentId }
|
|
282
|
+
});
|
|
129
283
|
}
|
|
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
284
|
}
|
|
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
|
-
};
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
var src_default = plugin;
|
|
194
289
|
export {
|
|
195
|
-
|
|
196
|
-
plugin
|
|
290
|
+
src_default as default
|
|
197
291
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/firecrawl",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
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
|
}
|