@meetopenbot/firecrawl 1.0.5 → 1.0.7

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.
Files changed (2) hide show
  1. package/dist/index.js +57 -267
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1,61 +1,5 @@
1
- // ../plugin-sdk/dist/plugin.js
2
- function definePlugin(definition) {
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/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"
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 = definePlugin({
78
+ var plugin = await defineAgentPlugin({
116
79
  name: "Firecrawl",
117
80
  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
- }
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.5",
3
+ "version": "1.0.7",
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.2.0"
28
+ "@meetopenbot/plugin-sdk": "^0.4.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 --noEmit --allowImportingTsExtensions --module ESNext --moduleResolution Bundler --target ES2022 --skipLibCheck src/index.ts"
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
  }