@meetopenbot/firecrawl 1.0.5 → 1.0.6
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 +57 -267
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,61 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
return definition;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
// ../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";
|
|
36
|
-
var POLL_INTERVAL_MS = 3e3;
|
|
37
|
-
var POLL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
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
|
-
};
|
|
58
|
-
var isAuthError = (message) => !!message && (/unauthor/i.test(message) || /api[\s-]?key/i.test(message) || /401|403/.test(message));
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { defineAgentPlugin, missingSecretMessage } from "@meetopenbot/plugin-sdk";
|
|
59
3
|
|
|
60
4
|
// src/client.ts
|
|
61
5
|
async function firecrawlRequest(apiKey, apiUrl, path, init = { method: "GET" }) {
|
|
@@ -83,224 +27,70 @@ async function firecrawlRequest(apiKey, apiUrl, path, init = { method: "GET" })
|
|
|
83
27
|
return payload;
|
|
84
28
|
}
|
|
85
29
|
|
|
86
|
-
// src/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
30
|
+
// src/config.ts
|
|
31
|
+
import { lookupSecret } from "@meetopenbot/plugin-sdk";
|
|
32
|
+
var DEFAULT_API_URL = "https://api.firecrawl.dev";
|
|
33
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
34
|
+
var POLL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
35
|
+
var API_KEY_ENV_VAR = "FIRECRAWL_API_KEY";
|
|
36
|
+
var resolveApiUrl = () => process.env.FIRECRAWL_API_URL?.trim() || DEFAULT_API_URL;
|
|
37
|
+
var resolveApiKey = () => lookupSecret({ envKeys: [API_KEY_ENV_VAR] });
|
|
38
|
+
|
|
39
|
+
// src/agent.ts
|
|
40
|
+
function resultText(data) {
|
|
41
|
+
if (data == null) return "Done.";
|
|
42
|
+
if (typeof data === "string") return data;
|
|
43
|
+
try {
|
|
44
|
+
return JSON.stringify(data, null, 2);
|
|
45
|
+
} catch {
|
|
46
|
+
return String(data);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function runFirecrawlAgent(args) {
|
|
50
|
+
const started = await firecrawlRequest(args.apiKey, args.apiUrl, "/v2/agent", {
|
|
51
|
+
method: "POST",
|
|
52
|
+
body: { prompt: args.prompt }
|
|
53
|
+
});
|
|
54
|
+
if (!started?.success || !started.id) {
|
|
55
|
+
return started?.error || "Failed to start Firecrawl agent.";
|
|
56
|
+
}
|
|
57
|
+
const startTime = Date.now();
|
|
58
|
+
while (true) {
|
|
59
|
+
if (Date.now() - startTime > POLL_TIMEOUT_MS) {
|
|
60
|
+
return `Timed out after ${Math.round(POLL_TIMEOUT_MS / 1e3)}s.`;
|
|
61
|
+
}
|
|
62
|
+
const status = await firecrawlRequest(
|
|
63
|
+
args.apiKey,
|
|
64
|
+
args.apiUrl,
|
|
65
|
+
`/v2/agent/${started.id}`
|
|
66
|
+
);
|
|
67
|
+
if (status.status === "completed") {
|
|
68
|
+
return resultText(status.data);
|
|
69
|
+
}
|
|
70
|
+
if (status.status === "failed" || status.status === "cancelled") {
|
|
71
|
+
return `Agent ${status.status}: ${status.error || "no details provided"}`;
|
|
110
72
|
}
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
111
74
|
}
|
|
112
|
-
}
|
|
75
|
+
}
|
|
113
76
|
|
|
114
77
|
// src/index.ts
|
|
115
|
-
var plugin =
|
|
78
|
+
var plugin = await defineAgentPlugin({
|
|
116
79
|
name: "Firecrawl",
|
|
117
80
|
description: "Firecrawl agent for web data gathering",
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
apiUrl: {
|
|
127
|
-
type: "string",
|
|
128
|
-
description: "Firecrawl API base URL.",
|
|
129
|
-
format: "url",
|
|
130
|
-
default: DEFAULT_API_URL
|
|
131
|
-
}
|
|
132
|
-
}
|
|
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
|
-
});
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
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
|
-
}
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
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
|
-
}
|
|
81
|
+
emptyPrompt: "No prompt provided.",
|
|
82
|
+
async run({ prompt }) {
|
|
83
|
+
const apiKey = resolveApiKey();
|
|
84
|
+
if (!apiKey) return missingSecretMessage(API_KEY_ENV_VAR);
|
|
85
|
+
return runFirecrawlAgent({
|
|
86
|
+
prompt,
|
|
87
|
+
apiKey,
|
|
88
|
+
apiUrl: resolveApiUrl()
|
|
258
89
|
});
|
|
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") {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
const apiKey = values?.apiKey;
|
|
267
|
-
if (typeof apiKey !== "string" || !apiKey) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
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
|
-
});
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
);
|
|
301
90
|
}
|
|
302
91
|
});
|
|
303
92
|
var src_default = plugin;
|
|
304
93
|
export {
|
|
305
|
-
src_default as default
|
|
94
|
+
src_default as default,
|
|
95
|
+
plugin
|
|
306
96
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/firecrawl",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Firecrawl agent plugin for OpenBot",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"esbuild": "^0.21.0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@meetopenbot/plugin-sdk": "^0.
|
|
28
|
+
"@meetopenbot/plugin-sdk": "^0.3.0"
|
|
29
29
|
},
|
|
30
30
|
"types": "./dist/index.d.ts",
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js && node ../../scripts/write-plugin-declaration.mjs",
|
|
33
|
-
"dev": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js --watch",
|
|
34
|
-
"typecheck": "tsc
|
|
32
|
+
"build": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js --external:@meetopenbot/plugin-sdk && node ../../scripts/write-plugin-declaration.mjs",
|
|
33
|
+
"dev": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js --external:@meetopenbot/plugin-sdk --watch",
|
|
34
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
35
35
|
}
|
|
36
36
|
}
|