@elizaos/plugin-farcaster 2.0.0-alpha.8 → 2.0.0-beta.1
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/LICENSE +21 -0
- package/README.md +108 -0
- package/auto-enable.ts +21 -0
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/auto-enable.d.ts +4 -0
- package/dist/auto-enable.d.ts.map +1 -0
- package/dist/browser/index.d.ts +2 -2
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.node.cjs +870 -719
- package/dist/cjs/index.node.js.map +23 -26
- package/dist/client/FarcasterClient.d.ts +41 -0
- package/dist/client/FarcasterClient.d.ts.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/connector-account-provider.d.ts +16 -0
- package/dist/connector-account-provider.d.ts.map +1 -0
- package/dist/generated/specs/spec-helpers.d.ts +36 -0
- package/dist/generated/specs/spec-helpers.d.ts.map +1 -0
- package/dist/generated/specs/specs.d.ts +48 -0
- package/dist/generated/specs/specs.d.ts.map +1 -0
- package/dist/index.browser.d.ts +11 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.node.d.ts +3 -0
- package/dist/index.node.d.ts.map +1 -0
- package/dist/managers/AgentManager.d.ts +20 -0
- package/dist/managers/AgentManager.d.ts.map +1 -0
- package/dist/managers/CastManager.d.ts +25 -0
- package/dist/managers/CastManager.d.ts.map +1 -0
- package/dist/managers/EmbedManager.d.ts +40 -0
- package/dist/managers/EmbedManager.d.ts.map +1 -0
- package/dist/managers/InteractionManager.d.ts +32 -0
- package/dist/managers/InteractionManager.d.ts.map +1 -0
- package/dist/managers/InteractionProcessor.d.ts +10 -0
- package/dist/managers/InteractionProcessor.d.ts.map +1 -0
- package/dist/managers/InteractionSource.d.ts +38 -0
- package/dist/managers/InteractionSource.d.ts.map +1 -0
- package/dist/managers/index.d.ts +7 -0
- package/dist/managers/index.d.ts.map +1 -0
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.node.d.ts +2 -0
- package/dist/node/index.node.js +830 -704
- package/dist/node/index.node.js.map +23 -26
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/profileProvider.d.ts +3 -0
- package/dist/providers/profileProvider.d.ts.map +1 -0
- package/dist/routes/webhook.d.ts +3 -0
- package/dist/routes/webhook.d.ts.map +1 -0
- package/dist/services/CastService.d.ts +133 -0
- package/dist/services/CastService.d.ts.map +1 -0
- package/dist/services/FarcasterService.d.ts +32 -0
- package/dist/services/FarcasterService.d.ts.map +1 -0
- package/dist/services/MessageService.d.ts +54 -0
- package/dist/services/MessageService.d.ts.map +1 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +119 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/asyncqueue.d.ts +13 -0
- package/dist/utils/asyncqueue.d.ts.map +1 -0
- package/dist/utils/callbacks.d.ts +14 -0
- package/dist/utils/callbacks.d.ts.map +1 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/index.d.ts +25 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/prompts.d.ts +5 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/workflow-credential-provider.d.ts +21 -0
- package/dist/workflow-credential-provider.d.ts.map +1 -0
- package/package.json +20 -9
package/dist/node/index.node.js
CHANGED
|
@@ -1,359 +1,41 @@
|
|
|
1
|
-
//
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
// generated/specs/specs.ts
|
|
5
|
-
var coreActionsSpec = {
|
|
6
|
-
version: "1.0.0",
|
|
7
|
-
actions: [
|
|
8
|
-
{
|
|
9
|
-
name: "SEND_CAST",
|
|
10
|
-
description: "Posts a cast (message) on Farcaster",
|
|
11
|
-
similes: ["POST_CAST", "FARCASTER_POST", "CAST", "SHARE_ON_FARCASTER", "ANNOUNCE"],
|
|
12
|
-
parameters: []
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
name: "REPLY_TO_CAST",
|
|
16
|
-
description: "Replies to a cast on Farcaster",
|
|
17
|
-
similes: ["REPLY_CAST", "RESPOND_CAST", "ANSWER_CAST", "COMMENT_CAST"],
|
|
18
|
-
parameters: []
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
};
|
|
22
|
-
var allActionsSpec = {
|
|
23
|
-
version: "1.0.0",
|
|
24
|
-
actions: [
|
|
25
|
-
{
|
|
26
|
-
name: "SEND_CAST",
|
|
27
|
-
description: "Posts a cast (message) on Farcaster",
|
|
28
|
-
similes: ["POST_CAST", "FARCASTER_POST", "CAST", "SHARE_ON_FARCASTER", "ANNOUNCE"],
|
|
29
|
-
parameters: []
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: "REPLY_TO_CAST",
|
|
33
|
-
description: "Replies to a cast on Farcaster",
|
|
34
|
-
similes: ["REPLY_CAST", "RESPOND_CAST", "ANSWER_CAST", "COMMENT_CAST"],
|
|
35
|
-
parameters: []
|
|
36
|
-
}
|
|
37
|
-
]
|
|
38
|
-
};
|
|
39
|
-
var coreProvidersSpec = {
|
|
40
|
-
version: "1.0.0",
|
|
41
|
-
providers: [
|
|
42
|
-
{
|
|
43
|
-
name: "farcasterProfile",
|
|
44
|
-
description: "Provides information about the agent",
|
|
45
|
-
dynamic: true
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: "farcasterThread",
|
|
49
|
-
description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
|
|
50
|
-
dynamic: true
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: "farcasterTimeline",
|
|
54
|
-
description: "Provides recent casts from the agent",
|
|
55
|
-
dynamic: true
|
|
56
|
-
}
|
|
57
|
-
]
|
|
58
|
-
};
|
|
59
|
-
var allProvidersSpec = {
|
|
60
|
-
version: "1.0.0",
|
|
61
|
-
providers: [
|
|
62
|
-
{
|
|
63
|
-
name: "farcasterProfile",
|
|
64
|
-
description: "Provides information about the agent",
|
|
65
|
-
dynamic: true
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: "farcasterThread",
|
|
69
|
-
description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
|
|
70
|
-
dynamic: true
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: "farcasterTimeline",
|
|
74
|
-
description: "Provides recent casts from the agent",
|
|
75
|
-
dynamic: true
|
|
76
|
-
}
|
|
77
|
-
]
|
|
78
|
-
};
|
|
79
|
-
var coreEvaluatorsSpec = {
|
|
80
|
-
version: "1.0.0",
|
|
81
|
-
evaluators: []
|
|
82
|
-
};
|
|
83
|
-
var allEvaluatorsSpec = {
|
|
84
|
-
version: "1.0.0",
|
|
85
|
-
evaluators: []
|
|
86
|
-
};
|
|
87
|
-
var coreActionDocs = coreActionsSpec.actions;
|
|
88
|
-
var allActionDocs = allActionsSpec.actions;
|
|
89
|
-
var coreProviderDocs = coreProvidersSpec.providers;
|
|
90
|
-
var allProviderDocs = allProvidersSpec.providers;
|
|
91
|
-
var coreEvaluatorDocs = coreEvaluatorsSpec.evaluators;
|
|
92
|
-
var allEvaluatorDocs = allEvaluatorsSpec.evaluators;
|
|
1
|
+
// index.ts
|
|
2
|
+
import { getConnectorAccountManager, logger } from "@elizaos/core";
|
|
93
3
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
|
|
98
|
-
var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
|
|
99
|
-
var coreEvaluatorMap = new Map(coreEvaluatorDocs.map((doc) => [doc.name, doc]));
|
|
100
|
-
var allEvaluatorMap = new Map(allEvaluatorDocs.map((doc) => [doc.name, doc]));
|
|
101
|
-
function getActionSpec(name) {
|
|
102
|
-
return coreActionMap.get(name) ?? allActionMap.get(name);
|
|
103
|
-
}
|
|
104
|
-
function requireActionSpec(name) {
|
|
105
|
-
const spec = getActionSpec(name);
|
|
106
|
-
if (!spec) {
|
|
107
|
-
throw new Error(`Action spec not found: ${name}`);
|
|
108
|
-
}
|
|
109
|
-
return spec;
|
|
110
|
-
}
|
|
111
|
-
function getProviderSpec(name) {
|
|
112
|
-
return coreProviderMap.get(name) ?? allProviderMap.get(name);
|
|
113
|
-
}
|
|
114
|
-
function requireProviderSpec(name) {
|
|
115
|
-
const spec = getProviderSpec(name);
|
|
116
|
-
if (!spec) {
|
|
117
|
-
throw new Error(`Provider spec not found: ${name}`);
|
|
118
|
-
}
|
|
119
|
-
return spec;
|
|
120
|
-
}
|
|
4
|
+
// utils/config.ts
|
|
5
|
+
import { parseBooleanFromText } from "@elizaos/core";
|
|
6
|
+
import { z as z3 } from "zod";
|
|
121
7
|
|
|
122
8
|
// types/index.ts
|
|
123
|
-
import
|
|
9
|
+
import * as zod from "zod";
|
|
10
|
+
var z2 = zod.z ?? zod;
|
|
124
11
|
var DEFAULT_MAX_CAST_LENGTH = 320;
|
|
125
12
|
var DEFAULT_POLL_INTERVAL = 120;
|
|
126
13
|
var DEFAULT_CAST_INTERVAL_MIN = 90;
|
|
127
14
|
var DEFAULT_CAST_INTERVAL_MAX = 180;
|
|
128
15
|
var DEFAULT_CAST_CACHE_TTL = 1000 * 30 * 60;
|
|
129
16
|
var DEFAULT_CAST_CACHE_SIZE = 9000;
|
|
130
|
-
var FarcasterConfigSchema =
|
|
131
|
-
FARCASTER_DRY_RUN:
|
|
132
|
-
FARCASTER_FID:
|
|
133
|
-
MAX_CAST_LENGTH:
|
|
134
|
-
FARCASTER_POLL_INTERVAL:
|
|
135
|
-
FARCASTER_MODE:
|
|
136
|
-
ENABLE_CAST:
|
|
137
|
-
CAST_INTERVAL_MIN:
|
|
138
|
-
CAST_INTERVAL_MAX:
|
|
139
|
-
ENABLE_ACTION_PROCESSING:
|
|
140
|
-
ACTION_INTERVAL:
|
|
141
|
-
CAST_IMMEDIATELY:
|
|
142
|
-
MAX_ACTIONS_PROCESSING:
|
|
143
|
-
FARCASTER_SIGNER_UUID:
|
|
144
|
-
FARCASTER_NEYNAR_API_KEY:
|
|
145
|
-
FARCASTER_HUB_URL:
|
|
17
|
+
var FarcasterConfigSchema = z2.object({
|
|
18
|
+
FARCASTER_DRY_RUN: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
19
|
+
FARCASTER_FID: z2.number().int().min(1, "Farcaster fid is required"),
|
|
20
|
+
MAX_CAST_LENGTH: z2.number().int().default(DEFAULT_MAX_CAST_LENGTH),
|
|
21
|
+
FARCASTER_POLL_INTERVAL: z2.number().int().default(DEFAULT_POLL_INTERVAL),
|
|
22
|
+
FARCASTER_MODE: z2.enum(["polling", "webhook"]).default("polling"),
|
|
23
|
+
ENABLE_CAST: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
24
|
+
CAST_INTERVAL_MIN: z2.number().int(),
|
|
25
|
+
CAST_INTERVAL_MAX: z2.number().int(),
|
|
26
|
+
ENABLE_ACTION_PROCESSING: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
27
|
+
ACTION_INTERVAL: z2.number().int(),
|
|
28
|
+
CAST_IMMEDIATELY: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
29
|
+
MAX_ACTIONS_PROCESSING: z2.number().int(),
|
|
30
|
+
FARCASTER_SIGNER_UUID: z2.string().min(1, "FARCASTER_SIGNER_UUID is not set"),
|
|
31
|
+
FARCASTER_NEYNAR_API_KEY: z2.string().min(1, "FARCASTER_NEYNAR_API_KEY is not set"),
|
|
32
|
+
FARCASTER_HUB_URL: z2.string().min(1, "FARCASTER_HUB_URL is not set")
|
|
146
33
|
});
|
|
147
34
|
var FARCASTER_SERVICE_NAME = "farcaster";
|
|
148
35
|
var FARCASTER_SOURCE = "farcaster";
|
|
149
36
|
|
|
150
|
-
// actions/replyCast.ts
|
|
151
|
-
var spec = requireActionSpec("REPLY_TO_CAST");
|
|
152
|
-
var replyCastAction = {
|
|
153
|
-
name: spec.name,
|
|
154
|
-
description: spec.description,
|
|
155
|
-
examples: [
|
|
156
|
-
[
|
|
157
|
-
{
|
|
158
|
-
name: "user",
|
|
159
|
-
content: {
|
|
160
|
-
text: "Someone asked about ElizaOS on Farcaster, can you reply?"
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
name: "assistant",
|
|
165
|
-
content: {
|
|
166
|
-
text: "I'll reply to their question about ElizaOS.",
|
|
167
|
-
actions: ["REPLY_TO_CAST"]
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
],
|
|
171
|
-
[
|
|
172
|
-
{
|
|
173
|
-
name: "user",
|
|
174
|
-
content: { text: "Reply to that cast and thank them for the feedback" }
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
name: "assistant",
|
|
178
|
-
content: {
|
|
179
|
-
text: "I'll reply with a thank you message.",
|
|
180
|
-
actions: ["REPLY_TO_CAST"]
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
]
|
|
184
|
-
],
|
|
185
|
-
validate: async (runtime, message, state, options) => {
|
|
186
|
-
const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
|
|
187
|
-
const __avText = __avTextRaw.toLowerCase();
|
|
188
|
-
const __avKeywords = ["reply", "cast"];
|
|
189
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
190
|
-
const __avRegex = /\b(?:reply|cast)\b/i;
|
|
191
|
-
const __avRegexOk = Boolean(__avText.match(__avRegex));
|
|
192
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
193
|
-
const __avExpectedSource = "farcaster";
|
|
194
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
195
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
196
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
197
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
const __avLegacyValidate = async (runtime2, message2) => {
|
|
201
|
-
const text = message2.content.text?.toLowerCase() || "";
|
|
202
|
-
const keywords = ["reply", "respond", "answer", "comment"];
|
|
203
|
-
const hasKeyword = keywords.some((keyword) => text.includes(keyword));
|
|
204
|
-
const hasParentCast = !!(message2.content.metadata && message2.content.metadata.parentCastHash);
|
|
205
|
-
const service = runtime2.getService(FARCASTER_SERVICE_NAME);
|
|
206
|
-
const isServiceAvailable = !!service?.getMessageService(runtime2.agentId);
|
|
207
|
-
return hasKeyword && (hasParentCast || isServiceAvailable);
|
|
208
|
-
};
|
|
209
|
-
try {
|
|
210
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
211
|
-
} catch {
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
handler: async (runtime, message, state) => {
|
|
216
|
-
try {
|
|
217
|
-
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
218
|
-
const messageService = service?.getMessageService(runtime.agentId);
|
|
219
|
-
if (!messageService) {
|
|
220
|
-
runtime.logger.error("[REPLY_TO_CAST] MessageService not available");
|
|
221
|
-
return { success: false, error: "MessageService not available" };
|
|
222
|
-
}
|
|
223
|
-
const metadata = message.content.metadata;
|
|
224
|
-
const stateParentCastHash = typeof state?.parentCastHash === "string" ? state.parentCastHash : undefined;
|
|
225
|
-
const parentCastHash = metadata?.parentCastHash ?? stateParentCastHash;
|
|
226
|
-
if (!parentCastHash) {
|
|
227
|
-
runtime.logger.error("[REPLY_TO_CAST] No parent cast to reply to");
|
|
228
|
-
return { success: false, error: "No parent cast to reply to" };
|
|
229
|
-
}
|
|
230
|
-
let replyContent = "";
|
|
231
|
-
if (state?.replyContent) {
|
|
232
|
-
replyContent = state.replyContent;
|
|
233
|
-
} else {
|
|
234
|
-
const prompt = `Based on this request: "${message.content.text}", generate a helpful and engaging reply for a Farcaster cast (max 320 characters).`;
|
|
235
|
-
const response = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
236
|
-
prompt
|
|
237
|
-
});
|
|
238
|
-
replyContent = typeof response === "string" ? response : String(response);
|
|
239
|
-
}
|
|
240
|
-
if (replyContent.length > 320) {
|
|
241
|
-
replyContent = `${replyContent.substring(0, 317)}...`;
|
|
242
|
-
}
|
|
243
|
-
const reply = await messageService.sendMessage({
|
|
244
|
-
agentId: runtime.agentId,
|
|
245
|
-
roomId: message.roomId,
|
|
246
|
-
text: replyContent,
|
|
247
|
-
type: "REPLY" /* REPLY */,
|
|
248
|
-
replyToId: parentCastHash,
|
|
249
|
-
metadata: {
|
|
250
|
-
parentHash: parentCastHash
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
runtime.logger.info(`[REPLY_TO_CAST] Successfully replied to cast: ${reply.id}`);
|
|
254
|
-
return { success: true, text: `Replied to cast: ${reply.id}` };
|
|
255
|
-
} catch (error) {
|
|
256
|
-
runtime.logger.error("[REPLY_TO_CAST] Error replying to cast:", typeof error === "string" ? error : error.message);
|
|
257
|
-
throw error;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
// actions/sendCast.ts
|
|
263
|
-
import {
|
|
264
|
-
createUniqueUuid,
|
|
265
|
-
ModelType as ModelType2
|
|
266
|
-
} from "@elizaos/core";
|
|
267
|
-
var spec2 = requireActionSpec("SEND_CAST");
|
|
268
|
-
var sendCastAction = {
|
|
269
|
-
name: spec2.name,
|
|
270
|
-
similes: spec2.similes ? [...spec2.similes] : [],
|
|
271
|
-
description: spec2.description,
|
|
272
|
-
examples: spec2.examples ?? [],
|
|
273
|
-
validate: async (runtime, message, state, options) => {
|
|
274
|
-
const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
|
|
275
|
-
const __avText = __avTextRaw.toLowerCase();
|
|
276
|
-
const __avKeywords = ["send", "cast"];
|
|
277
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
278
|
-
const __avRegex = /\b(?:send|cast)\b/i;
|
|
279
|
-
const __avRegexOk = Boolean(__avText.match(__avRegex));
|
|
280
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
281
|
-
const __avExpectedSource = "farcaster";
|
|
282
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
283
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
284
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
285
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
286
|
-
return false;
|
|
287
|
-
}
|
|
288
|
-
const __avLegacyValidate = async (runtime2, message2) => {
|
|
289
|
-
const text = message2.content.text?.toLowerCase() || "";
|
|
290
|
-
const keywords = ["post", "cast", "share", "announce", "farcaster"];
|
|
291
|
-
const hasKeyword = keywords.some((keyword) => text.includes(keyword));
|
|
292
|
-
const service = runtime2.getService(FARCASTER_SERVICE_NAME);
|
|
293
|
-
const isServiceAvailable = !!service?.getCastService(runtime2.agentId);
|
|
294
|
-
return hasKeyword && isServiceAvailable;
|
|
295
|
-
};
|
|
296
|
-
try {
|
|
297
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
298
|
-
} catch {
|
|
299
|
-
return false;
|
|
300
|
-
}
|
|
301
|
-
},
|
|
302
|
-
handler: async (runtime, message, state) => {
|
|
303
|
-
try {
|
|
304
|
-
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
305
|
-
const postService = service?.getCastService(runtime.agentId);
|
|
306
|
-
if (!postService) {
|
|
307
|
-
runtime.logger.error("[SEND_CAST] PostService not available");
|
|
308
|
-
return { success: false, error: "PostService not available" };
|
|
309
|
-
}
|
|
310
|
-
let castContent = "";
|
|
311
|
-
if (state?.castContent) {
|
|
312
|
-
castContent = state.castContent;
|
|
313
|
-
} else {
|
|
314
|
-
const prompt = `Based on this request: "${message.content.text}", generate a concise Farcaster cast (max 320 characters). Be engaging and use appropriate hashtags if relevant.`;
|
|
315
|
-
const response = await runtime.useModel(ModelType2.TEXT_LARGE, {
|
|
316
|
-
prompt
|
|
317
|
-
});
|
|
318
|
-
castContent = typeof response === "string" ? response : String(response);
|
|
319
|
-
}
|
|
320
|
-
if (castContent.length > 320) {
|
|
321
|
-
castContent = `${castContent.substring(0, 317)}...`;
|
|
322
|
-
}
|
|
323
|
-
const cast = await postService.createCast({
|
|
324
|
-
agentId: runtime.agentId,
|
|
325
|
-
roomId: createUniqueUuid(runtime, "farcaster-timeline"),
|
|
326
|
-
text: castContent
|
|
327
|
-
});
|
|
328
|
-
runtime.logger.info(`[SEND_CAST] Successfully posted cast: ${cast.id}`);
|
|
329
|
-
await runtime.createMemory({
|
|
330
|
-
agentId: runtime.agentId,
|
|
331
|
-
roomId: cast.roomId,
|
|
332
|
-
entityId: runtime.agentId,
|
|
333
|
-
content: {
|
|
334
|
-
text: castContent,
|
|
335
|
-
source: "farcaster",
|
|
336
|
-
metadata: {
|
|
337
|
-
castHash: String(cast.metadata?.castHash || ""),
|
|
338
|
-
action: "SEND_CAST"
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
createdAt: cast.timestamp
|
|
342
|
-
}, "messages");
|
|
343
|
-
return { success: true, text: `Posted cast: ${cast.id}` };
|
|
344
|
-
} catch (error) {
|
|
345
|
-
runtime.logger.error("[SEND_CAST] Error posting cast:", typeof error === "string" ? error : error.message);
|
|
346
|
-
return { success: false, error: error.message };
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
// actions/index.ts
|
|
352
|
-
var farcasterActions = [sendCastAction, replyCastAction];
|
|
353
|
-
|
|
354
37
|
// utils/config.ts
|
|
355
|
-
|
|
356
|
-
import { z as z2 } from "zod";
|
|
38
|
+
var DEFAULT_FARCASTER_ACCOUNT_ID = "default";
|
|
357
39
|
function getProcessEnv() {
|
|
358
40
|
if (typeof process === "undefined") {
|
|
359
41
|
return {};
|
|
@@ -367,41 +49,138 @@ function safeParseInt(value, defaultValue) {
|
|
|
367
49
|
const parsed = Number.parseInt(value, 10);
|
|
368
50
|
return Number.isNaN(parsed) ? defaultValue : Math.max(1, parsed);
|
|
369
51
|
}
|
|
370
|
-
function
|
|
371
|
-
const
|
|
52
|
+
function stringSetting(runtime, key) {
|
|
53
|
+
const value = runtime.getSetting(key);
|
|
54
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
55
|
+
}
|
|
56
|
+
function characterConfig(runtime) {
|
|
57
|
+
const settings = runtime.character?.settings;
|
|
58
|
+
const raw = settings?.farcaster;
|
|
59
|
+
return raw && typeof raw === "object" ? raw : {};
|
|
60
|
+
}
|
|
61
|
+
function parseAccountsJson(runtime) {
|
|
62
|
+
const raw = stringSetting(runtime, "FARCASTER_ACCOUNTS");
|
|
63
|
+
if (!raw)
|
|
64
|
+
return {};
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(raw);
|
|
67
|
+
if (Array.isArray(parsed)) {
|
|
68
|
+
return Object.fromEntries(parsed.filter((item) => Boolean(item) && typeof item === "object").map((item) => [normalizeFarcasterAccountId(item.accountId ?? item.id), item]));
|
|
69
|
+
}
|
|
70
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
71
|
+
} catch {
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function allAccountConfigs(runtime) {
|
|
76
|
+
return {
|
|
77
|
+
...characterConfig(runtime).accounts ?? {},
|
|
78
|
+
...parseAccountsJson(runtime)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function accountConfig(runtime, accountId) {
|
|
82
|
+
const accounts = allAccountConfigs(runtime);
|
|
83
|
+
return accounts[accountId] ?? accounts[normalizeFarcasterAccountId(accountId)] ?? {};
|
|
84
|
+
}
|
|
85
|
+
function rawField(record, keys) {
|
|
86
|
+
if (!record)
|
|
87
|
+
return;
|
|
88
|
+
for (const key of keys) {
|
|
89
|
+
const value = record[key];
|
|
90
|
+
if (typeof value === "string" && value.trim())
|
|
91
|
+
return value.trim();
|
|
92
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
93
|
+
return String(value);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
function normalizeFarcasterAccountId(accountId) {
|
|
98
|
+
if (typeof accountId !== "string")
|
|
99
|
+
return DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
100
|
+
const trimmed = accountId.trim();
|
|
101
|
+
return trimmed || DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
102
|
+
}
|
|
103
|
+
function listFarcasterAccountIds(runtime) {
|
|
104
|
+
const ids = new Set;
|
|
105
|
+
const config = characterConfig(runtime);
|
|
106
|
+
if (stringSetting(runtime, "FARCASTER_FID") || config.FARCASTER_FID && config.FARCASTER_SIGNER_UUID) {
|
|
107
|
+
ids.add(DEFAULT_FARCASTER_ACCOUNT_ID);
|
|
108
|
+
}
|
|
109
|
+
for (const id of Object.keys(allAccountConfigs(runtime))) {
|
|
110
|
+
ids.add(normalizeFarcasterAccountId(id));
|
|
111
|
+
}
|
|
112
|
+
return Array.from(ids.size ? ids : new Set([DEFAULT_FARCASTER_ACCOUNT_ID])).sort((a, b) => a.localeCompare(b));
|
|
113
|
+
}
|
|
114
|
+
function resolveDefaultFarcasterAccountId(runtime) {
|
|
115
|
+
const requested = stringSetting(runtime, "FARCASTER_DEFAULT_ACCOUNT_ID") ?? stringSetting(runtime, "FARCASTER_ACCOUNT_ID");
|
|
116
|
+
if (requested)
|
|
117
|
+
return normalizeFarcasterAccountId(requested);
|
|
118
|
+
const ids = listFarcasterAccountIds(runtime);
|
|
119
|
+
return ids.includes(DEFAULT_FARCASTER_ACCOUNT_ID) ? DEFAULT_FARCASTER_ACCOUNT_ID : ids[0];
|
|
120
|
+
}
|
|
121
|
+
function readFarcasterAccountId(...sources) {
|
|
122
|
+
for (const source of sources) {
|
|
123
|
+
if (!source || typeof source !== "object")
|
|
124
|
+
continue;
|
|
125
|
+
const record = source;
|
|
126
|
+
const parameters = record.parameters && typeof record.parameters === "object" ? record.parameters : {};
|
|
127
|
+
const data = record.data && typeof record.data === "object" ? record.data : {};
|
|
128
|
+
const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
|
|
129
|
+
const farcaster = data.farcaster && typeof data.farcaster === "object" ? data.farcaster : {};
|
|
130
|
+
const value = record.accountId ?? parameters.accountId ?? data.accountId ?? farcaster.accountId ?? metadata.accountId;
|
|
131
|
+
if (typeof value === "string" && value.trim())
|
|
132
|
+
return normalizeFarcasterAccountId(value);
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
function getFarcasterFid(runtime, accountId) {
|
|
137
|
+
const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
|
|
138
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
139
|
+
const base = characterConfig(runtime);
|
|
140
|
+
const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
141
|
+
const fidStr = rawField(account, ["FARCASTER_FID", "fid"]) ?? rawField(base, ["FARCASTER_FID", "fid"]) ?? (allowEnv ? stringSetting(runtime, "FARCASTER_FID") : undefined);
|
|
372
142
|
if (!fidStr)
|
|
373
143
|
return null;
|
|
374
144
|
const fid = Number.parseInt(fidStr, 10);
|
|
375
145
|
return Number.isNaN(fid) ? null : fid;
|
|
376
146
|
}
|
|
377
|
-
function hasFarcasterEnabled(runtime) {
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
const
|
|
147
|
+
function hasFarcasterEnabled(runtime, accountId) {
|
|
148
|
+
const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
|
|
149
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
150
|
+
const base = characterConfig(runtime);
|
|
151
|
+
const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
152
|
+
const fid = getFarcasterFid(runtime, normalizedAccountId);
|
|
153
|
+
const signerUuid = rawField(account, ["FARCASTER_SIGNER_UUID", "signerUuid"]) ?? rawField(base, ["FARCASTER_SIGNER_UUID", "signerUuid"]) ?? (allowEnv ? stringSetting(runtime, "FARCASTER_SIGNER_UUID") : undefined);
|
|
154
|
+
const apiKey = rawField(account, ["FARCASTER_NEYNAR_API_KEY", "neynarApiKey", "apiKey"]) ?? rawField(base, ["FARCASTER_NEYNAR_API_KEY", "neynarApiKey", "apiKey"]) ?? (allowEnv ? stringSetting(runtime, "FARCASTER_NEYNAR_API_KEY") : undefined);
|
|
381
155
|
runtime.logger.debug(`[hasFarcasterEnabled] FID: ${fid ? "Found" : "Missing"}`);
|
|
382
156
|
runtime.logger.debug(`[hasFarcasterEnabled] Signer UUID: ${signerUuid ? "Found" : "Missing"}`);
|
|
383
157
|
runtime.logger.debug(`[hasFarcasterEnabled] API Key: ${apiKey ? "Found" : "Missing"}`);
|
|
384
158
|
return !!(fid && signerUuid && apiKey);
|
|
385
159
|
}
|
|
386
|
-
function validateFarcasterConfig(runtime) {
|
|
387
|
-
const
|
|
160
|
+
function validateFarcasterConfig(runtime, accountId) {
|
|
161
|
+
const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
|
|
162
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
163
|
+
const base = characterConfig(runtime);
|
|
164
|
+
const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
165
|
+
const field = (keys, envKey) => rawField(account, keys) ?? rawField(base, keys) ?? (allowEnv && envKey ? stringSetting(runtime, envKey) : undefined);
|
|
166
|
+
const fid = getFarcasterFid(runtime, normalizedAccountId);
|
|
388
167
|
try {
|
|
389
168
|
const farcasterConfig = {
|
|
390
|
-
FARCASTER_DRY_RUN:
|
|
169
|
+
FARCASTER_DRY_RUN: field(["FARCASTER_DRY_RUN", "dryRun"], "FARCASTER_DRY_RUN") || parseBooleanFromText(env.FARCASTER_DRY_RUN || "false"),
|
|
391
170
|
FARCASTER_FID: fid ?? undefined,
|
|
392
|
-
MAX_CAST_LENGTH: safeParseInt(
|
|
393
|
-
FARCASTER_POLL_INTERVAL: safeParseInt(
|
|
394
|
-
ENABLE_CAST:
|
|
395
|
-
CAST_INTERVAL_MIN: safeParseInt(
|
|
396
|
-
CAST_INTERVAL_MAX: safeParseInt(
|
|
397
|
-
ENABLE_ACTION_PROCESSING:
|
|
398
|
-
ACTION_INTERVAL: safeParseInt(
|
|
399
|
-
CAST_IMMEDIATELY:
|
|
400
|
-
MAX_ACTIONS_PROCESSING: safeParseInt(
|
|
401
|
-
FARCASTER_SIGNER_UUID:
|
|
402
|
-
FARCASTER_NEYNAR_API_KEY:
|
|
403
|
-
FARCASTER_HUB_URL:
|
|
404
|
-
FARCASTER_MODE:
|
|
171
|
+
MAX_CAST_LENGTH: safeParseInt(field(["MAX_CAST_LENGTH", "maxCastLength"], "MAX_CAST_LENGTH"), DEFAULT_MAX_CAST_LENGTH),
|
|
172
|
+
FARCASTER_POLL_INTERVAL: safeParseInt(field(["FARCASTER_POLL_INTERVAL", "pollInterval"], "FARCASTER_POLL_INTERVAL"), DEFAULT_POLL_INTERVAL),
|
|
173
|
+
ENABLE_CAST: field(["ENABLE_CAST", "enableCast"], "ENABLE_CAST") || parseBooleanFromText(env.ENABLE_CAST || "true"),
|
|
174
|
+
CAST_INTERVAL_MIN: safeParseInt(field(["CAST_INTERVAL_MIN", "castIntervalMin"], "CAST_INTERVAL_MIN"), DEFAULT_CAST_INTERVAL_MIN),
|
|
175
|
+
CAST_INTERVAL_MAX: safeParseInt(field(["CAST_INTERVAL_MAX", "castIntervalMax"], "CAST_INTERVAL_MAX"), DEFAULT_CAST_INTERVAL_MAX),
|
|
176
|
+
ENABLE_ACTION_PROCESSING: field(["ENABLE_ACTION_PROCESSING", "enableActionProcessing"], "ENABLE_ACTION_PROCESSING") || parseBooleanFromText(env.ENABLE_ACTION_PROCESSING || "false"),
|
|
177
|
+
ACTION_INTERVAL: safeParseInt(field(["ACTION_INTERVAL", "actionInterval"], "ACTION_INTERVAL"), 5),
|
|
178
|
+
CAST_IMMEDIATELY: field(["CAST_IMMEDIATELY", "castImmediately"], "CAST_IMMEDIATELY") || parseBooleanFromText(env.CAST_IMMEDIATELY || "false"),
|
|
179
|
+
MAX_ACTIONS_PROCESSING: safeParseInt(field(["MAX_ACTIONS_PROCESSING", "maxActionsProcessing"], "MAX_ACTIONS_PROCESSING"), 1),
|
|
180
|
+
FARCASTER_SIGNER_UUID: field(["FARCASTER_SIGNER_UUID", "signerUuid"], "FARCASTER_SIGNER_UUID"),
|
|
181
|
+
FARCASTER_NEYNAR_API_KEY: field(["FARCASTER_NEYNAR_API_KEY", "neynarApiKey", "apiKey"], "FARCASTER_NEYNAR_API_KEY"),
|
|
182
|
+
FARCASTER_HUB_URL: field(["FARCASTER_HUB_URL", "hubUrl"], "FARCASTER_HUB_URL") || "hub.pinata.cloud",
|
|
183
|
+
FARCASTER_MODE: field(["FARCASTER_MODE", "mode"], "FARCASTER_MODE") || "polling"
|
|
405
184
|
};
|
|
406
185
|
runtime.logger.debug(`[validateFarcasterConfig] Resolved FID: ${farcasterConfig.FARCASTER_FID}`);
|
|
407
186
|
runtime.logger.debug(`[validateFarcasterConfig] Resolved Signer UUID: ${farcasterConfig.FARCASTER_SIGNER_UUID ? "Found" : "Missing"}`);
|
|
@@ -421,9 +200,9 @@ function validateFarcasterConfig(runtime) {
|
|
|
421
200
|
if (isDryRun) {
|
|
422
201
|
runtime.logger.info("Farcaster client initialized in dry run mode - no actual casts should be posted");
|
|
423
202
|
}
|
|
424
|
-
return config;
|
|
203
|
+
return { ...config, accountId: normalizedAccountId };
|
|
425
204
|
} catch (error) {
|
|
426
|
-
if (error instanceof
|
|
205
|
+
if (error instanceof z3.ZodError) {
|
|
427
206
|
const errorMessages = error.issues.map((err) => `${err.path.join(".")}: ${err.message}`).join(`
|
|
428
207
|
`);
|
|
429
208
|
throw new Error(`Farcaster configuration validation failed:
|
|
@@ -433,32 +212,143 @@ ${errorMessages}`);
|
|
|
433
212
|
}
|
|
434
213
|
}
|
|
435
214
|
|
|
215
|
+
// connector-account-provider.ts
|
|
216
|
+
var FARCASTER_PROVIDER_ID = "farcaster";
|
|
217
|
+
function toConnectorAccount(runtime, accountId) {
|
|
218
|
+
let connected = false;
|
|
219
|
+
let fid;
|
|
220
|
+
let hubUrl = "";
|
|
221
|
+
try {
|
|
222
|
+
const config = validateFarcasterConfig(runtime, accountId);
|
|
223
|
+
fid = config.FARCASTER_FID;
|
|
224
|
+
hubUrl = config.FARCASTER_HUB_URL ?? "";
|
|
225
|
+
connected = Boolean(fid && config.FARCASTER_SIGNER_UUID && config.FARCASTER_NEYNAR_API_KEY);
|
|
226
|
+
} catch {
|
|
227
|
+
connected = false;
|
|
228
|
+
}
|
|
229
|
+
const now = Date.now();
|
|
230
|
+
return {
|
|
231
|
+
id: accountId,
|
|
232
|
+
provider: FARCASTER_PROVIDER_ID,
|
|
233
|
+
label: fid ? `FID ${fid}` : accountId,
|
|
234
|
+
role: "OWNER",
|
|
235
|
+
purpose: ["posting", "reading"],
|
|
236
|
+
accessGate: "open",
|
|
237
|
+
status: connected ? "connected" : "disabled",
|
|
238
|
+
externalId: fid ? String(fid) : undefined,
|
|
239
|
+
displayHandle: fid ? String(fid) : undefined,
|
|
240
|
+
createdAt: now,
|
|
241
|
+
updatedAt: now,
|
|
242
|
+
metadata: {
|
|
243
|
+
hubUrl,
|
|
244
|
+
fid
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function createFarcasterConnectorAccountProvider(runtime) {
|
|
249
|
+
return {
|
|
250
|
+
provider: FARCASTER_PROVIDER_ID,
|
|
251
|
+
label: "Farcaster",
|
|
252
|
+
listAccounts: async (_manager) => {
|
|
253
|
+
const ids = listFarcasterAccountIds(runtime);
|
|
254
|
+
return ids.map((id) => toConnectorAccount(runtime, id));
|
|
255
|
+
},
|
|
256
|
+
createAccount: async (input, _manager) => {
|
|
257
|
+
return {
|
|
258
|
+
...input,
|
|
259
|
+
provider: FARCASTER_PROVIDER_ID,
|
|
260
|
+
role: input.role ?? "OWNER",
|
|
261
|
+
purpose: input.purpose ?? ["posting", "reading"],
|
|
262
|
+
accessGate: input.accessGate ?? "open",
|
|
263
|
+
status: input.status ?? "pending"
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
patchAccount: async (_accountId, patch, _manager) => {
|
|
267
|
+
return { ...patch, provider: FARCASTER_PROVIDER_ID };
|
|
268
|
+
},
|
|
269
|
+
deleteAccount: async (_accountId, _manager) => {}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// generated/specs/specs.ts
|
|
274
|
+
var coreActionsSpec = {
|
|
275
|
+
version: "1.0.0",
|
|
276
|
+
actions: []
|
|
277
|
+
};
|
|
278
|
+
var allActionsSpec = {
|
|
279
|
+
version: "1.0.0",
|
|
280
|
+
actions: []
|
|
281
|
+
};
|
|
282
|
+
var coreProvidersSpec = {
|
|
283
|
+
version: "1.0.0",
|
|
284
|
+
providers: [
|
|
285
|
+
{
|
|
286
|
+
name: "farcasterProfile",
|
|
287
|
+
description: "Provides information about the agent",
|
|
288
|
+
dynamic: true
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
};
|
|
292
|
+
var allProvidersSpec = {
|
|
293
|
+
version: "1.0.0",
|
|
294
|
+
providers: [
|
|
295
|
+
{
|
|
296
|
+
name: "farcasterProfile",
|
|
297
|
+
description: "Provides information about the agent",
|
|
298
|
+
dynamic: true
|
|
299
|
+
}
|
|
300
|
+
]
|
|
301
|
+
};
|
|
302
|
+
var coreActionDocs = coreActionsSpec.actions;
|
|
303
|
+
var allActionDocs = allActionsSpec.actions;
|
|
304
|
+
var coreProviderDocs = coreProvidersSpec.providers;
|
|
305
|
+
var allProviderDocs = allProvidersSpec.providers;
|
|
306
|
+
|
|
307
|
+
// generated/specs/spec-helpers.ts
|
|
308
|
+
var coreActionMap = new Map(coreActionDocs.map((doc) => [doc.name, doc]));
|
|
309
|
+
var allActionMap = new Map(allActionDocs.map((doc) => [doc.name, doc]));
|
|
310
|
+
var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
|
|
311
|
+
var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
|
|
312
|
+
function getProviderSpec(name) {
|
|
313
|
+
return coreProviderMap.get(name) ?? allProviderMap.get(name);
|
|
314
|
+
}
|
|
315
|
+
function requireProviderSpec(name) {
|
|
316
|
+
const spec = getProviderSpec(name);
|
|
317
|
+
if (!spec) {
|
|
318
|
+
throw new Error(`Provider spec not found: ${name}`);
|
|
319
|
+
}
|
|
320
|
+
return spec;
|
|
321
|
+
}
|
|
322
|
+
|
|
436
323
|
// providers/profileProvider.ts
|
|
437
|
-
var
|
|
324
|
+
var spec = requireProviderSpec("farcasterProfile");
|
|
325
|
+
var MAX_PROFILE_FIELD_LENGTH = 280;
|
|
326
|
+
function truncateProfileField(value) {
|
|
327
|
+
return value ? value.slice(0, MAX_PROFILE_FIELD_LENGTH) : value;
|
|
328
|
+
}
|
|
438
329
|
var farcasterProfileProvider = {
|
|
439
|
-
name:
|
|
330
|
+
name: spec.name,
|
|
440
331
|
description: "Provides information about the agent's Farcaster profile",
|
|
332
|
+
descriptionCompressed: "provide information agent Farcaster profile",
|
|
441
333
|
dynamic: true,
|
|
442
|
-
|
|
334
|
+
contexts: ["social_posting", "messaging", "connectors"],
|
|
335
|
+
contextGate: { anyOf: ["social_posting", "messaging", "connectors"] },
|
|
336
|
+
cacheStable: false,
|
|
337
|
+
cacheScope: "turn",
|
|
338
|
+
get: async (runtime, message, state) => {
|
|
443
339
|
try {
|
|
444
340
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
445
|
-
const
|
|
446
|
-
|
|
341
|
+
const accountId = readFarcasterAccountId(message, state);
|
|
342
|
+
const manager = service?.getManagerForAccount?.(accountId, runtime.agentId) ?? service?.getManagerForAccount?.(undefined, runtime.agentId);
|
|
343
|
+
if (!manager) {
|
|
447
344
|
runtime.logger.debug("[FarcasterProfileProvider] No managers available");
|
|
448
345
|
return {
|
|
449
346
|
text: "Farcaster profile not available.",
|
|
450
347
|
data: { available: false }
|
|
451
348
|
};
|
|
452
349
|
}
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
runtime.logger.debug("[FarcasterProfileProvider] No manager for this agent");
|
|
456
|
-
return {
|
|
457
|
-
text: "Farcaster profile not available for this agent.",
|
|
458
|
-
data: { available: false }
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
const fid = getFarcasterFid(runtime);
|
|
350
|
+
const selectedAccountId = manager.config.accountId;
|
|
351
|
+
const fid = getFarcasterFid(runtime, selectedAccountId);
|
|
462
352
|
if (!fid) {
|
|
463
353
|
runtime.logger.warn("[FarcasterProfileProvider] Invalid or missing FARCASTER_FID");
|
|
464
354
|
return {
|
|
@@ -468,18 +358,21 @@ var farcasterProfileProvider = {
|
|
|
468
358
|
}
|
|
469
359
|
try {
|
|
470
360
|
const profile = await manager.client.getProfile(fid);
|
|
361
|
+
const username = truncateProfileField(profile.username) ?? "";
|
|
362
|
+
const name = truncateProfileField(profile.name);
|
|
471
363
|
return {
|
|
472
|
-
text: `Your Farcaster profile: @${
|
|
364
|
+
text: `Your Farcaster profile: @${username} (FID: ${profile.fid}). ${name ? `Display name: ${name}` : ""}`,
|
|
473
365
|
data: {
|
|
474
366
|
available: true,
|
|
475
367
|
fid: profile.fid,
|
|
476
|
-
username
|
|
477
|
-
name
|
|
478
|
-
pfp: profile.pfp
|
|
368
|
+
username,
|
|
369
|
+
name,
|
|
370
|
+
pfp: profile.pfp,
|
|
371
|
+
accountId: selectedAccountId
|
|
479
372
|
},
|
|
480
373
|
values: {
|
|
481
374
|
fid: profile.fid,
|
|
482
|
-
username
|
|
375
|
+
username
|
|
483
376
|
}
|
|
484
377
|
};
|
|
485
378
|
} catch (error) {
|
|
@@ -499,165 +392,38 @@ var farcasterProfileProvider = {
|
|
|
499
392
|
}
|
|
500
393
|
};
|
|
501
394
|
|
|
502
|
-
// providers/threadProvider.ts
|
|
503
|
-
var formatTimestamp = (timestamp) => {
|
|
504
|
-
return new Date(timestamp).toLocaleString("en-US", {
|
|
505
|
-
hour: "2-digit",
|
|
506
|
-
minute: "2-digit",
|
|
507
|
-
month: "short",
|
|
508
|
-
day: "numeric"
|
|
509
|
-
});
|
|
510
|
-
};
|
|
511
|
-
var spec4 = requireProviderSpec("farcasterThread");
|
|
512
|
-
var farcasterThreadProvider = {
|
|
513
|
-
name: spec4.name,
|
|
514
|
-
description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
|
|
515
|
-
dynamic: true,
|
|
516
|
-
get: async (runtime, message, _state) => {
|
|
517
|
-
const contentForSource = message.content;
|
|
518
|
-
const source = typeof contentForSource.source === "string" ? contentForSource.source : undefined;
|
|
519
|
-
if (source !== FARCASTER_SOURCE) {
|
|
520
|
-
return {
|
|
521
|
-
text: "",
|
|
522
|
-
data: {
|
|
523
|
-
available: false,
|
|
524
|
-
reason: `Not a Farcaster message (source=${source ?? "unknown"})`
|
|
525
|
-
}
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
529
|
-
const messageService = service?.getMessageService(runtime.agentId);
|
|
530
|
-
if (!messageService) {
|
|
531
|
-
return {
|
|
532
|
-
text: "Farcaster message service not available.",
|
|
533
|
-
data: { available: false, error: "service_unavailable" }
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
const content = message.content;
|
|
537
|
-
const messageMetadata = message.metadata ?? {};
|
|
538
|
-
const contentMetadata = typeof content.metadata === "object" && content.metadata !== null && !Array.isArray(content.metadata) ? content.metadata : undefined;
|
|
539
|
-
const castHash = content.hash || content.castHash || contentMetadata?.castHash || messageMetadata.castHash || contentMetadata?.parentHash || messageMetadata.parentHash;
|
|
540
|
-
if (!castHash || typeof castHash !== "string") {
|
|
541
|
-
return {
|
|
542
|
-
text: "Unable to resolve Farcaster cast hash for this message.",
|
|
543
|
-
data: { available: false, error: "missing_cast_hash" }
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
const threadMessages = await messageService.getThread({
|
|
547
|
-
agentId: runtime.agentId,
|
|
548
|
-
castHash
|
|
549
|
-
});
|
|
550
|
-
if (!threadMessages || threadMessages.length === 0) {
|
|
551
|
-
runtime.logger.debug({ castHash }, "[FarcasterThreadProvider] No thread messages retrieved for cast.");
|
|
552
|
-
return {
|
|
553
|
-
text: "No Farcaster thread context available.",
|
|
554
|
-
data: { available: false, castHash, count: 0 }
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
const formattedThread = threadMessages.map((msg, index) => {
|
|
558
|
-
const time = formatTimestamp(msg.timestamp);
|
|
559
|
-
const username = msg.username || msg.userId || "unknown";
|
|
560
|
-
const marker = index === threadMessages.length - 1 ? "→" : "•";
|
|
561
|
-
const text = msg.text && msg.text.trim().length > 0 ? msg.text : "<no text>";
|
|
562
|
-
return `${marker} [${time}] @${username}: ${text}`;
|
|
563
|
-
}).join(`
|
|
564
|
-
`);
|
|
565
|
-
return {
|
|
566
|
-
text: `# Farcaster Thread Context
|
|
567
|
-
${formattedThread}`,
|
|
568
|
-
data: {
|
|
569
|
-
available: true,
|
|
570
|
-
castHash,
|
|
571
|
-
count: threadMessages.length
|
|
572
|
-
},
|
|
573
|
-
values: {
|
|
574
|
-
farcasterThread: formattedThread,
|
|
575
|
-
farcasterCastHash: castHash,
|
|
576
|
-
farcasterCurrentCastText: threadMessages[threadMessages.length - 1]?.text ?? "",
|
|
577
|
-
farcasterParentCastText: threadMessages.length > 1 ? threadMessages[threadMessages.length - 2]?.text ?? "" : ""
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
// providers/timelineProvider.ts
|
|
584
|
-
function getTimeAgo(date) {
|
|
585
|
-
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
586
|
-
if (seconds < 60)
|
|
587
|
-
return "just now";
|
|
588
|
-
if (seconds < 3600)
|
|
589
|
-
return `${Math.floor(seconds / 60)}m ago`;
|
|
590
|
-
if (seconds < 86400)
|
|
591
|
-
return `${Math.floor(seconds / 3600)}h ago`;
|
|
592
|
-
return `${Math.floor(seconds / 86400)}d ago`;
|
|
593
|
-
}
|
|
594
|
-
var spec5 = requireProviderSpec("farcasterTimeline");
|
|
595
|
-
var farcasterTimelineProvider = {
|
|
596
|
-
name: spec5.name,
|
|
597
|
-
description: "Provides recent casts from the agent's Farcaster timeline",
|
|
598
|
-
dynamic: true,
|
|
599
|
-
get: async (runtime, _message, _state) => {
|
|
600
|
-
try {
|
|
601
|
-
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
602
|
-
const castService = service?.getCastService(runtime.agentId);
|
|
603
|
-
if (!castService) {
|
|
604
|
-
return {
|
|
605
|
-
text: "Farcaster timeline not available.",
|
|
606
|
-
data: { available: false }
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
const casts = await castService.getCasts({
|
|
610
|
-
agentId: runtime.agentId,
|
|
611
|
-
limit: 5
|
|
612
|
-
});
|
|
613
|
-
if (!casts || casts.length === 0) {
|
|
614
|
-
return {
|
|
615
|
-
text: "No recent casts in your timeline.",
|
|
616
|
-
data: {
|
|
617
|
-
available: true,
|
|
618
|
-
casts: [],
|
|
619
|
-
count: 0
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
const formattedCasts = casts.map((cast, index) => {
|
|
624
|
-
const timeAgo = getTimeAgo(new Date(cast.timestamp));
|
|
625
|
-
return `${index + 1}. @${cast.username} (${timeAgo}): ${cast.text}`;
|
|
626
|
-
}).join(`
|
|
627
|
-
`);
|
|
628
|
-
return {
|
|
629
|
-
text: `Recent casts from your timeline:
|
|
630
|
-
${formattedCasts}`,
|
|
631
|
-
data: {
|
|
632
|
-
available: true,
|
|
633
|
-
castCount: casts.length
|
|
634
|
-
},
|
|
635
|
-
values: {
|
|
636
|
-
latestCastHash: String(casts[0]?.metadata?.castHash || ""),
|
|
637
|
-
latestCastText: casts[0]?.text || ""
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
} catch (error) {
|
|
641
|
-
runtime.logger.error("[FarcasterTimelineProvider] Error:", typeof error === "string" ? error : error.message);
|
|
642
|
-
return {
|
|
643
|
-
text: "Unable to fetch Farcaster timeline.",
|
|
644
|
-
data: {
|
|
645
|
-
available: false,
|
|
646
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
|
|
653
395
|
// providers/index.ts
|
|
654
|
-
var farcasterProviders = [
|
|
655
|
-
farcasterProfileProvider,
|
|
656
|
-
farcasterTimelineProvider,
|
|
657
|
-
farcasterThreadProvider
|
|
658
|
-
];
|
|
396
|
+
var farcasterProviders = [farcasterProfileProvider];
|
|
659
397
|
|
|
660
398
|
// routes/webhook.ts
|
|
399
|
+
function isRecord(value) {
|
|
400
|
+
return typeof value === "object" && value !== null;
|
|
401
|
+
}
|
|
402
|
+
function isNeynarWebhookData(value) {
|
|
403
|
+
if (!isRecord(value) || typeof value.type !== "string")
|
|
404
|
+
return false;
|
|
405
|
+
if (value.data === undefined)
|
|
406
|
+
return true;
|
|
407
|
+
if (!isRecord(value.data))
|
|
408
|
+
return false;
|
|
409
|
+
if (typeof value.data.hash !== "string")
|
|
410
|
+
return false;
|
|
411
|
+
if (!isRecord(value.data.author) || typeof value.data.author.fid !== "number")
|
|
412
|
+
return false;
|
|
413
|
+
if (value.data.text !== undefined && typeof value.data.text !== "string") {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
if (value.data.mentioned_profiles !== undefined && !(Array.isArray(value.data.mentioned_profiles) && value.data.mentioned_profiles.every((profile) => isRecord(profile) && typeof profile.fid === "number"))) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
if (value.data.parent_hash !== undefined && typeof value.data.parent_hash !== "string") {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
if (value.data.parent_author !== undefined && !(isRecord(value.data.parent_author) && typeof value.data.parent_author.fid === "number")) {
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
661
427
|
var farcasterWebhookRoutes = [
|
|
662
428
|
{
|
|
663
429
|
type: "POST",
|
|
@@ -665,16 +431,25 @@ var farcasterWebhookRoutes = [
|
|
|
665
431
|
path: "/webhook",
|
|
666
432
|
handler: async (req, res, runtime) => {
|
|
667
433
|
try {
|
|
434
|
+
if (!isNeynarWebhookData(req.body)) {
|
|
435
|
+
res.status(400).json({
|
|
436
|
+
success: false,
|
|
437
|
+
error: "Invalid webhook payload"
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
668
441
|
const webhookData = req.body;
|
|
669
442
|
const eventType = webhookData.type;
|
|
670
|
-
const farcasterService = runtime?.getService?.(
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
}
|
|
443
|
+
const farcasterService = runtime?.getService?.(FARCASTER_SERVICE_NAME);
|
|
444
|
+
const accountId = readFarcasterAccountId(webhookData);
|
|
445
|
+
if (farcasterService && accountId) {
|
|
446
|
+
const manager = farcasterService.getManagerForAccount?.(accountId, runtime.agentId);
|
|
447
|
+
if (manager?.interactions.mode === "webhook") {
|
|
448
|
+
await manager.interactions.processWebhookData(webhookData);
|
|
677
449
|
}
|
|
450
|
+
} else if (farcasterService) {
|
|
451
|
+
const managers = farcasterService.getManagersForAgent?.(runtime.agentId) ?? new Map;
|
|
452
|
+
await Promise.all(Array.from(managers.values()).filter((manager) => manager.interactions.mode === "webhook").map((manager) => manager.interactions.processWebhookData(webhookData)));
|
|
678
453
|
}
|
|
679
454
|
res.status(200).json({
|
|
680
455
|
success: true,
|
|
@@ -696,7 +471,10 @@ var farcasterWebhookRoutes = [
|
|
|
696
471
|
];
|
|
697
472
|
|
|
698
473
|
// services/FarcasterService.ts
|
|
699
|
-
import {
|
|
474
|
+
import {
|
|
475
|
+
ChannelType as ChannelType3,
|
|
476
|
+
Service
|
|
477
|
+
} from "@elizaos/core";
|
|
700
478
|
|
|
701
479
|
// managers/AgentManager.ts
|
|
702
480
|
import { Configuration, NeynarAPIClient } from "@neynar/nodejs-sdk";
|
|
@@ -822,7 +600,8 @@ function createCastMemory({
|
|
|
822
600
|
roomId,
|
|
823
601
|
senderId,
|
|
824
602
|
runtime,
|
|
825
|
-
cast
|
|
603
|
+
cast,
|
|
604
|
+
accountId
|
|
826
605
|
}) {
|
|
827
606
|
const inReplyTo = cast.inReplyTo ? castUuid({
|
|
828
607
|
hash: cast.inReplyTo.hash,
|
|
@@ -838,17 +617,45 @@ function createCastMemory({
|
|
|
838
617
|
content: {
|
|
839
618
|
text: cast.text,
|
|
840
619
|
source: FARCASTER_SOURCE,
|
|
620
|
+
...accountId ? { accountId } : {},
|
|
841
621
|
url: "",
|
|
842
622
|
inReplyTo,
|
|
843
623
|
hash: cast.hash,
|
|
844
624
|
threadId: cast.threadId,
|
|
845
625
|
attachments: cast.media && cast.media.length > 0 ? cast.media : undefined
|
|
846
626
|
},
|
|
627
|
+
metadata: {
|
|
628
|
+
...accountId ? { accountId } : {}
|
|
629
|
+
},
|
|
847
630
|
roomId
|
|
848
631
|
};
|
|
849
632
|
}
|
|
850
633
|
|
|
851
634
|
// client/FarcasterClient.ts
|
|
635
|
+
async function logNeynarCall(op, context, fn) {
|
|
636
|
+
const startedAt = Date.now();
|
|
637
|
+
elizaLogger.debug({ sdk: "neynar", op, ...context }, `[FarcasterClient] ${op} started`);
|
|
638
|
+
try {
|
|
639
|
+
const result = await fn();
|
|
640
|
+
elizaLogger.info({
|
|
641
|
+
sdk: "neynar",
|
|
642
|
+
op,
|
|
643
|
+
...context,
|
|
644
|
+
durationMs: Date.now() - startedAt
|
|
645
|
+
}, `[FarcasterClient] ${op} ok`);
|
|
646
|
+
return result;
|
|
647
|
+
} catch (error) {
|
|
648
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
649
|
+
elizaLogger.warn({
|
|
650
|
+
sdk: "neynar",
|
|
651
|
+
op,
|
|
652
|
+
...context,
|
|
653
|
+
durationMs: Date.now() - startedAt,
|
|
654
|
+
error: message
|
|
655
|
+
}, `[FarcasterClient] ${op} failed`);
|
|
656
|
+
throw error;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
852
659
|
var castCache = new LRUCache({
|
|
853
660
|
max: DEFAULT_CAST_CACHE_SIZE,
|
|
854
661
|
ttl: DEFAULT_CAST_CACHE_TTL
|
|
@@ -883,11 +690,11 @@ class FarcasterClient {
|
|
|
883
690
|
}
|
|
884
691
|
async publishCast(cast, parentCastId) {
|
|
885
692
|
try {
|
|
886
|
-
const result = await this.neynar.publishCast({
|
|
693
|
+
const result = await logNeynarCall("publishCast", { textLen: cast.length, parentHash: parentCastId?.hash ?? null }, async () => this.neynar.publishCast({
|
|
887
694
|
signerUuid: this.signerUuid,
|
|
888
695
|
text: cast,
|
|
889
696
|
parent: parentCastId?.hash
|
|
890
|
-
});
|
|
697
|
+
}));
|
|
891
698
|
if (result.success) {
|
|
892
699
|
return this.getCast(result.cast.hash);
|
|
893
700
|
}
|
|
@@ -897,7 +704,6 @@ class FarcasterClient {
|
|
|
897
704
|
elizaLogger.error(`Neynar error: ${JSON.stringify(err.response.data)}`);
|
|
898
705
|
throw err.response.data;
|
|
899
706
|
} else {
|
|
900
|
-
elizaLogger.error(`Error: ${JSON.stringify(err)}`);
|
|
901
707
|
throw err;
|
|
902
708
|
}
|
|
903
709
|
}
|
|
@@ -907,19 +713,19 @@ class FarcasterClient {
|
|
|
907
713
|
if (cachedCast) {
|
|
908
714
|
return cachedCast;
|
|
909
715
|
}
|
|
910
|
-
const response = await this.neynar.lookupCastByHashOrUrl({
|
|
716
|
+
const response = await logNeynarCall("lookupCastByHashOrUrl", { castHash }, async () => this.neynar.lookupCastByHashOrUrl({
|
|
911
717
|
identifier: castHash,
|
|
912
718
|
type: "hash"
|
|
913
|
-
});
|
|
719
|
+
}));
|
|
914
720
|
castCache.set(castHash, response.cast);
|
|
915
721
|
return response.cast;
|
|
916
722
|
}
|
|
917
723
|
async getMentions(request) {
|
|
918
|
-
const neynarMentionsResponse = await this.neynar.fetchAllNotifications({
|
|
724
|
+
const neynarMentionsResponse = await logNeynarCall("fetchAllNotifications", { fid: request.fid, pageSize: request.pageSize }, async () => this.neynar.fetchAllNotifications({
|
|
919
725
|
fid: request.fid,
|
|
920
726
|
type: ["mentions", "replies"],
|
|
921
727
|
limit: request.pageSize
|
|
922
|
-
});
|
|
728
|
+
}));
|
|
923
729
|
const mentions = [];
|
|
924
730
|
for (const notification of neynarMentionsResponse.notifications) {
|
|
925
731
|
const neynarCast = notification.cast;
|
|
@@ -933,35 +739,29 @@ class FarcasterClient {
|
|
|
933
739
|
if (profileCache.has(fid)) {
|
|
934
740
|
return profileCache.get(fid);
|
|
935
741
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
elizaLogger.error("Error fetching user by fid");
|
|
940
|
-
throw new Error("Profile fetch failed");
|
|
941
|
-
}
|
|
942
|
-
const neynarUserProfile = result.users[0];
|
|
943
|
-
const profile = {
|
|
944
|
-
fid,
|
|
945
|
-
name: "",
|
|
946
|
-
username: ""
|
|
947
|
-
};
|
|
948
|
-
profile.name = neynarUserProfile.display_name ?? "";
|
|
949
|
-
profile.username = neynarUserProfile.username;
|
|
950
|
-
profile.bio = neynarUserProfile.profile.bio.text;
|
|
951
|
-
profile.pfp = neynarUserProfile.pfp_url;
|
|
952
|
-
profileCache.set(fid, profile);
|
|
953
|
-
return profile;
|
|
954
|
-
} catch (error) {
|
|
955
|
-
elizaLogger.error(`Error fetching profile: ${JSON.stringify(error)}`);
|
|
956
|
-
throw error;
|
|
742
|
+
const result = await logNeynarCall("fetchBulkUsers", { fid }, async () => this.neynar.fetchBulkUsers({ fids: [fid] }));
|
|
743
|
+
if (result.users.length < 1) {
|
|
744
|
+
throw new Error("Profile fetch failed");
|
|
957
745
|
}
|
|
746
|
+
const neynarUserProfile = result.users[0];
|
|
747
|
+
const profile = {
|
|
748
|
+
fid,
|
|
749
|
+
name: "",
|
|
750
|
+
username: ""
|
|
751
|
+
};
|
|
752
|
+
profile.name = neynarUserProfile.display_name ?? "";
|
|
753
|
+
profile.username = neynarUserProfile.username;
|
|
754
|
+
profile.bio = neynarUserProfile.profile.bio.text;
|
|
755
|
+
profile.pfp = neynarUserProfile.pfp_url;
|
|
756
|
+
profileCache.set(fid, profile);
|
|
757
|
+
return profile;
|
|
958
758
|
}
|
|
959
759
|
async getTimeline(request) {
|
|
960
760
|
const timeline = [];
|
|
961
|
-
const response = await this.neynar.fetchCastsForUser({
|
|
761
|
+
const response = await logNeynarCall("fetchCastsForUser", { fid: request.fid, pageSize: request.pageSize }, async () => this.neynar.fetchCastsForUser({
|
|
962
762
|
fid: request.fid,
|
|
963
763
|
limit: request.pageSize
|
|
964
|
-
});
|
|
764
|
+
}));
|
|
965
765
|
for (const cast of response.casts) {
|
|
966
766
|
castCache.set(cast.hash, cast);
|
|
967
767
|
timeline.push(neynarCastToCast(cast));
|
|
@@ -978,42 +778,45 @@ class FarcasterClient {
|
|
|
978
778
|
}
|
|
979
779
|
async publishReaction(params) {
|
|
980
780
|
try {
|
|
981
|
-
const result = await this.neynar.publishReaction({
|
|
781
|
+
const result = await logNeynarCall("publishReaction", { reactionType: params.reactionType, target: params.target }, async () => this.neynar.publishReaction({
|
|
982
782
|
signerUuid: this.signerUuid,
|
|
983
783
|
reactionType: params.reactionType,
|
|
984
784
|
target: params.target
|
|
985
|
-
});
|
|
785
|
+
}));
|
|
986
786
|
return { success: result.success ?? false };
|
|
987
787
|
} catch (err) {
|
|
988
788
|
if (isApiErrorResponse(err)) {
|
|
989
789
|
elizaLogger.error(`Neynar error publishing reaction: ${JSON.stringify(err.response.data)}`);
|
|
990
790
|
throw err.response.data;
|
|
991
791
|
}
|
|
992
|
-
elizaLogger.error(`Error publishing reaction: ${JSON.stringify(err)}`);
|
|
993
792
|
throw err;
|
|
994
793
|
}
|
|
995
794
|
}
|
|
996
795
|
async deleteReaction(params) {
|
|
997
796
|
try {
|
|
998
|
-
const result = await this.neynar.deleteReaction({
|
|
797
|
+
const result = await logNeynarCall("deleteReaction", { reactionType: params.reactionType, target: params.target }, async () => this.neynar.deleteReaction({
|
|
999
798
|
signerUuid: this.signerUuid,
|
|
1000
799
|
reactionType: params.reactionType,
|
|
1001
800
|
target: params.target
|
|
1002
|
-
});
|
|
801
|
+
}));
|
|
1003
802
|
return { success: result.success ?? false };
|
|
1004
803
|
} catch (err) {
|
|
1005
804
|
if (isApiErrorResponse(err)) {
|
|
1006
805
|
elizaLogger.error(`Neynar error deleting reaction: ${JSON.stringify(err.response.data)}`);
|
|
1007
806
|
throw err.response.data;
|
|
1008
807
|
}
|
|
1009
|
-
elizaLogger.error(`Error deleting reaction: ${JSON.stringify(err)}`);
|
|
1010
808
|
throw err;
|
|
1011
809
|
}
|
|
1012
810
|
}
|
|
1013
811
|
}
|
|
1014
812
|
|
|
1015
813
|
// managers/CastManager.ts
|
|
1016
|
-
import {
|
|
814
|
+
import {
|
|
815
|
+
createUniqueUuid,
|
|
816
|
+
EventType,
|
|
817
|
+
setTrajectoryPurpose,
|
|
818
|
+
withStandaloneTrajectory
|
|
819
|
+
} from "@elizaos/core";
|
|
1017
820
|
|
|
1018
821
|
// utils/callbacks.ts
|
|
1019
822
|
function standardCastHandlerCallback({
|
|
@@ -1027,6 +830,7 @@ function standardCastHandlerCallback({
|
|
|
1027
830
|
}) {
|
|
1028
831
|
const callback = async (content) => {
|
|
1029
832
|
try {
|
|
833
|
+
const accountId = normalizeFarcasterAccountId(config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
|
|
1030
834
|
if (config.FARCASTER_DRY_RUN) {
|
|
1031
835
|
runtime.logger.info(`[Farcaster] Dry run: would have cast: ${content.text}`);
|
|
1032
836
|
return [];
|
|
@@ -1044,7 +848,8 @@ function standardCastHandlerCallback({
|
|
|
1044
848
|
roomId,
|
|
1045
849
|
senderId: runtime.agentId,
|
|
1046
850
|
runtime,
|
|
1047
|
-
cast: neynarCastToCast(cast)
|
|
851
|
+
cast: neynarCastToCast(cast),
|
|
852
|
+
accountId
|
|
1048
853
|
});
|
|
1049
854
|
if (i === 0) {
|
|
1050
855
|
memory.content.actions = content.actions;
|
|
@@ -1081,6 +886,9 @@ class FarcasterCastManager {
|
|
|
1081
886
|
this.config = opts.config;
|
|
1082
887
|
this.fid = this.config.FARCASTER_FID;
|
|
1083
888
|
}
|
|
889
|
+
getAccountId() {
|
|
890
|
+
return normalizeFarcasterAccountId(this.config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
|
|
891
|
+
}
|
|
1084
892
|
async start() {
|
|
1085
893
|
if (this.isRunning || !this.config.ENABLE_CAST) {
|
|
1086
894
|
return;
|
|
@@ -1123,45 +931,58 @@ class FarcasterCastManager {
|
|
|
1123
931
|
}
|
|
1124
932
|
}
|
|
1125
933
|
async generateNewCast() {
|
|
1126
|
-
this.runtime
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
934
|
+
await withStandaloneTrajectory(this.runtime, {
|
|
935
|
+
source: "plugin-farcaster:auto-cast",
|
|
936
|
+
metadata: {
|
|
937
|
+
platform: FARCASTER_SOURCE,
|
|
938
|
+
kind: "public_post_generation",
|
|
939
|
+
fid: this.fid,
|
|
940
|
+
accountId: this.getAccountId()
|
|
941
|
+
}
|
|
942
|
+
}, async () => {
|
|
943
|
+
setTrajectoryPurpose("background");
|
|
944
|
+
this.runtime.logger.info("Generating new cast");
|
|
945
|
+
try {
|
|
946
|
+
const worldId = createUniqueUuid(this.runtime, this.fid.toString());
|
|
947
|
+
const roomId = createUniqueUuid(this.runtime, `${this.fid}-home`);
|
|
948
|
+
const callback = standardCastHandlerCallback({
|
|
949
|
+
client: this.client,
|
|
950
|
+
runtime: this.runtime,
|
|
951
|
+
config: this.config,
|
|
952
|
+
roomId,
|
|
953
|
+
onCompletion: async (casts, _memories) => {
|
|
954
|
+
const lastCast = casts[casts.length - 1];
|
|
955
|
+
await this.runtime.setCache(lastCastCacheKey(this.fid), {
|
|
956
|
+
hash: lastCast.hash,
|
|
957
|
+
timestamp: new Date(lastCast.timestamp).getTime()
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
await this.runtime.emitEvent(EventType.POST_GENERATED, {
|
|
962
|
+
runtime: this.runtime,
|
|
963
|
+
callback,
|
|
964
|
+
worldId,
|
|
965
|
+
userId: this.runtime.agentId,
|
|
966
|
+
roomId,
|
|
967
|
+
source: FARCASTER_SOURCE,
|
|
968
|
+
accountId: this.getAccountId()
|
|
969
|
+
});
|
|
970
|
+
await this.runtime.emitEvent("FARCASTER_POST_GENERATED" /* POST_GENERATED */, {
|
|
971
|
+
runtime: this.runtime,
|
|
972
|
+
source: FARCASTER_SOURCE,
|
|
973
|
+
accountId: this.getAccountId()
|
|
974
|
+
});
|
|
975
|
+
} catch (error) {
|
|
976
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error generating new cast");
|
|
977
|
+
}
|
|
978
|
+
});
|
|
1158
979
|
}
|
|
1159
980
|
}
|
|
1160
981
|
|
|
1161
982
|
// managers/InteractionManager.ts
|
|
1162
983
|
import {
|
|
1163
984
|
ChannelType,
|
|
1164
|
-
createUniqueUuid as
|
|
985
|
+
createUniqueUuid as createUniqueUuid2,
|
|
1165
986
|
stringToUuid as stringToUuid2
|
|
1166
987
|
} from "@elizaos/core";
|
|
1167
988
|
|
|
@@ -1227,12 +1048,12 @@ class AsyncQueue {
|
|
|
1227
1048
|
}
|
|
1228
1049
|
|
|
1229
1050
|
// managers/EmbedManager.ts
|
|
1230
|
-
import { ModelType as
|
|
1051
|
+
import { ModelType, withStandaloneTrajectory as withStandaloneTrajectory2 } from "@elizaos/core";
|
|
1231
1052
|
function isEmbedUrl(embed) {
|
|
1232
|
-
return "url" in embed
|
|
1053
|
+
return "url" in embed;
|
|
1233
1054
|
}
|
|
1234
1055
|
function isEmbedCast(embed) {
|
|
1235
|
-
return "cast" in embed
|
|
1056
|
+
return "cast" in embed;
|
|
1236
1057
|
}
|
|
1237
1058
|
function getMediaTypeFromUrl(url, contentType) {
|
|
1238
1059
|
const lowerUrl = url.toLowerCase();
|
|
@@ -1326,10 +1147,10 @@ class EmbedManager {
|
|
|
1326
1147
|
let description = "An image attachment";
|
|
1327
1148
|
let title = "Image";
|
|
1328
1149
|
try {
|
|
1329
|
-
const result = await this.runtime.useModel(
|
|
1150
|
+
const result = await withStandaloneTrajectory2(this.runtime, { source: "farcaster-embed" }, async () => this.runtime.useModel(ModelType.IMAGE_DESCRIPTION, {
|
|
1330
1151
|
prompt: "Analyze this image and provide a concise title and description. Focus on the main subject and any notable details.",
|
|
1331
1152
|
imageUrl: url
|
|
1332
|
-
});
|
|
1153
|
+
}));
|
|
1333
1154
|
if (result && typeof result === "object") {
|
|
1334
1155
|
const typedResult = result;
|
|
1335
1156
|
description = typedResult.description || description;
|
|
@@ -1616,6 +1437,9 @@ class FarcasterInteractionManager {
|
|
|
1616
1437
|
});
|
|
1617
1438
|
this.runtime.logger.info(`Farcaster interaction mode: ${this.mode}`);
|
|
1618
1439
|
}
|
|
1440
|
+
getAccountId() {
|
|
1441
|
+
return normalizeFarcasterAccountId(this.config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
|
|
1442
|
+
}
|
|
1619
1443
|
async processMention(cast) {
|
|
1620
1444
|
const agentFid = this.config.FARCASTER_FID;
|
|
1621
1445
|
const agent = await this.client.getProfile(agentFid);
|
|
@@ -1710,9 +1534,9 @@ class FarcasterInteractionManager {
|
|
|
1710
1534
|
hash: cast.hash
|
|
1711
1535
|
});
|
|
1712
1536
|
const conversationId = cast.threadId ?? cast.inReplyTo?.hash ?? cast.hash;
|
|
1713
|
-
const entityId =
|
|
1714
|
-
const worldId =
|
|
1715
|
-
const roomId =
|
|
1537
|
+
const entityId = createUniqueUuid2(this.runtime, cast.authorFid.toString());
|
|
1538
|
+
const worldId = createUniqueUuid2(this.runtime, cast.authorFid.toString());
|
|
1539
|
+
const roomId = createUniqueUuid2(this.runtime, conversationId);
|
|
1716
1540
|
if (entityId !== this.runtime.agentId) {
|
|
1717
1541
|
await this.runtime.ensureConnection({
|
|
1718
1542
|
entityId,
|
|
@@ -1726,8 +1550,10 @@ class FarcasterInteractionManager {
|
|
|
1726
1550
|
messageServerId: stringToUuid2(cast.authorFid.toString()),
|
|
1727
1551
|
worldId,
|
|
1728
1552
|
metadata: {
|
|
1553
|
+
accountId: this.getAccountId(),
|
|
1729
1554
|
ownership: { ownerId: cast.authorFid.toString() },
|
|
1730
1555
|
farcaster: {
|
|
1556
|
+
accountId: this.getAccountId(),
|
|
1731
1557
|
username: cast.profile.username,
|
|
1732
1558
|
id: cast.authorFid.toString(),
|
|
1733
1559
|
name: cast.profile.name
|
|
@@ -1752,12 +1578,16 @@ class FarcasterInteractionManager {
|
|
|
1752
1578
|
hash: cast.inReplyTo.hash
|
|
1753
1579
|
}) : undefined,
|
|
1754
1580
|
source: FARCASTER_SOURCE,
|
|
1581
|
+
accountId: this.getAccountId(),
|
|
1755
1582
|
channelType: ChannelType.THREAD,
|
|
1756
1583
|
attachments: cast.media && cast.media.length > 0 ? cast.media : undefined
|
|
1757
1584
|
},
|
|
1758
1585
|
entityId,
|
|
1759
1586
|
roomId,
|
|
1760
|
-
createdAt: cast.timestamp.getTime()
|
|
1587
|
+
createdAt: cast.timestamp.getTime(),
|
|
1588
|
+
metadata: {
|
|
1589
|
+
accountId: this.getAccountId()
|
|
1590
|
+
}
|
|
1761
1591
|
};
|
|
1762
1592
|
return memory;
|
|
1763
1593
|
});
|
|
@@ -1786,7 +1616,8 @@ class FarcasterInteractionManager {
|
|
|
1786
1616
|
runtime,
|
|
1787
1617
|
memory: newMemory,
|
|
1788
1618
|
cast: currentCast,
|
|
1789
|
-
source: FARCASTER_SOURCE
|
|
1619
|
+
source: FARCASTER_SOURCE,
|
|
1620
|
+
accountId: self.getAccountId()
|
|
1790
1621
|
});
|
|
1791
1622
|
}
|
|
1792
1623
|
thread.unshift(currentCast);
|
|
@@ -1840,6 +1671,7 @@ class FarcasterInteractionManager {
|
|
|
1840
1671
|
memory,
|
|
1841
1672
|
cast,
|
|
1842
1673
|
source: FARCASTER_SOURCE,
|
|
1674
|
+
accountId: this.getAccountId(),
|
|
1843
1675
|
callback
|
|
1844
1676
|
};
|
|
1845
1677
|
this.runtime.emitEvent("FARCASTER_MENTION_RECEIVED" /* MENTION_RECEIVED */, mentionPayload);
|
|
@@ -1860,8 +1692,10 @@ class FarcasterAgentManager {
|
|
|
1860
1692
|
client;
|
|
1861
1693
|
casts;
|
|
1862
1694
|
interactions;
|
|
1695
|
+
config;
|
|
1863
1696
|
constructor(runtime, config) {
|
|
1864
1697
|
this.runtime = runtime;
|
|
1698
|
+
this.config = config;
|
|
1865
1699
|
const signerUuid = config.FARCASTER_SIGNER_UUID;
|
|
1866
1700
|
const neynarConfig = new Configuration({
|
|
1867
1701
|
apiKey: config.FARCASTER_NEYNAR_API_KEY
|
|
@@ -1886,18 +1720,44 @@ class FarcasterAgentManager {
|
|
|
1886
1720
|
}
|
|
1887
1721
|
|
|
1888
1722
|
// services/CastService.ts
|
|
1889
|
-
import {
|
|
1723
|
+
import {
|
|
1724
|
+
ChannelType as ChannelType2,
|
|
1725
|
+
createUniqueUuid as createUniqueUuid3,
|
|
1726
|
+
ModelType as ModelType2
|
|
1727
|
+
} from "@elizaos/core";
|
|
1728
|
+
function clampLimit(value, fallback, max) {
|
|
1729
|
+
if (!Number.isFinite(value)) {
|
|
1730
|
+
return fallback;
|
|
1731
|
+
}
|
|
1732
|
+
return Math.min(Math.max(1, Math.floor(value)), max);
|
|
1733
|
+
}
|
|
1734
|
+
function readContentString(content, keys) {
|
|
1735
|
+
const record = content;
|
|
1736
|
+
for (const key of keys) {
|
|
1737
|
+
const value = record[key];
|
|
1738
|
+
if (typeof value === "string" && value.trim()) {
|
|
1739
|
+
return value.trim();
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1890
1745
|
class FarcasterCastService {
|
|
1891
1746
|
client;
|
|
1892
1747
|
runtime;
|
|
1748
|
+
accountId;
|
|
1893
1749
|
static serviceType = "ICastService";
|
|
1894
|
-
constructor(client, runtime) {
|
|
1750
|
+
constructor(client, runtime, accountId = DEFAULT_FARCASTER_ACCOUNT_ID) {
|
|
1895
1751
|
this.client = client;
|
|
1896
1752
|
this.runtime = runtime;
|
|
1753
|
+
this.accountId = accountId;
|
|
1754
|
+
}
|
|
1755
|
+
getAccountId() {
|
|
1756
|
+
return normalizeFarcasterAccountId(this.accountId);
|
|
1897
1757
|
}
|
|
1898
1758
|
async getCasts(params) {
|
|
1899
1759
|
try {
|
|
1900
|
-
const fid = getFarcasterFid(this.runtime);
|
|
1760
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
1901
1761
|
if (!fid) {
|
|
1902
1762
|
this.runtime.logger.error("FARCASTER_FID is not configured");
|
|
1903
1763
|
return [];
|
|
@@ -1940,6 +1800,7 @@ class FarcasterCastService {
|
|
|
1940
1800
|
inReplyTo: params.replyTo?.hash,
|
|
1941
1801
|
media: [],
|
|
1942
1802
|
metadata: {
|
|
1803
|
+
accountId: this.getAccountId(),
|
|
1943
1804
|
castHash: cast.hash,
|
|
1944
1805
|
authorFid: cast.authorFid,
|
|
1945
1806
|
source: FARCASTER_SOURCE,
|
|
@@ -1953,6 +1814,58 @@ class FarcasterCastService {
|
|
|
1953
1814
|
throw error;
|
|
1954
1815
|
}
|
|
1955
1816
|
}
|
|
1817
|
+
async handleSendPost(runtime, content) {
|
|
1818
|
+
const requestedAccountId = normalizeFarcasterAccountId(readFarcasterAccountId(content) ?? this.getAccountId());
|
|
1819
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1820
|
+
throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
|
|
1821
|
+
}
|
|
1822
|
+
const text = typeof content.text === "string" ? content.text.trim() : "";
|
|
1823
|
+
if (!text) {
|
|
1824
|
+
throw new Error("Farcaster post connector requires non-empty text content.");
|
|
1825
|
+
}
|
|
1826
|
+
const parentHash = readContentString(content, ["parentHash", "replyTo", "replyToHash"]);
|
|
1827
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
1828
|
+
const cast = await this.createCast({
|
|
1829
|
+
agentId: runtime.agentId,
|
|
1830
|
+
roomId: createUniqueUuid3(runtime, `farcaster:feed:${fid ?? runtime.agentId}`),
|
|
1831
|
+
text,
|
|
1832
|
+
...parentHash && fid ? { replyTo: { hash: parentHash, fid } } : {}
|
|
1833
|
+
});
|
|
1834
|
+
return this.farcasterCastToMemory(runtime, cast);
|
|
1835
|
+
}
|
|
1836
|
+
async fetchFeed(context, params = {}) {
|
|
1837
|
+
const requestedAccountId = normalizeFarcasterAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
1838
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1839
|
+
throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
|
|
1840
|
+
}
|
|
1841
|
+
const casts = await this.getCasts({
|
|
1842
|
+
agentId: context.runtime.agentId,
|
|
1843
|
+
limit: clampLimit(params.limit, 25, 100),
|
|
1844
|
+
cursor: params.cursor
|
|
1845
|
+
});
|
|
1846
|
+
return casts.map((cast) => this.farcasterCastToMemory(context.runtime, cast));
|
|
1847
|
+
}
|
|
1848
|
+
async searchPosts(context, params) {
|
|
1849
|
+
const requestedAccountId = normalizeFarcasterAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
1850
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1851
|
+
throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
|
|
1852
|
+
}
|
|
1853
|
+
const query = params.query.trim().toLowerCase();
|
|
1854
|
+
if (!query) {
|
|
1855
|
+
return [];
|
|
1856
|
+
}
|
|
1857
|
+
const limit = clampLimit(params.limit, 25, 100);
|
|
1858
|
+
const casts = await this.getCasts({
|
|
1859
|
+
agentId: context.runtime.agentId,
|
|
1860
|
+
limit: 100,
|
|
1861
|
+
cursor: params.cursor
|
|
1862
|
+
});
|
|
1863
|
+
return casts.filter((cast) => {
|
|
1864
|
+
const text = cast.text.toLowerCase();
|
|
1865
|
+
const username = cast.username.toLowerCase();
|
|
1866
|
+
return text.includes(query) || username.includes(query);
|
|
1867
|
+
}).slice(0, limit).map((cast) => this.farcasterCastToMemory(context.runtime, cast));
|
|
1868
|
+
}
|
|
1956
1869
|
async deleteCast(params) {
|
|
1957
1870
|
this.runtime.logger.warn(`Cast deletion is not supported by the Farcaster API: ${JSON.stringify({ castHash: params.castHash })}`);
|
|
1958
1871
|
}
|
|
@@ -2018,7 +1931,7 @@ class FarcasterCastService {
|
|
|
2018
1931
|
}
|
|
2019
1932
|
async getMentions(params) {
|
|
2020
1933
|
try {
|
|
2021
|
-
const fid = getFarcasterFid(this.runtime);
|
|
1934
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
2022
1935
|
if (!fid) {
|
|
2023
1936
|
this.runtime.logger.error("FARCASTER_FID is not configured");
|
|
2024
1937
|
return [];
|
|
@@ -2039,7 +1952,7 @@ class FarcasterCastService {
|
|
|
2039
1952
|
async generateCastContent() {
|
|
2040
1953
|
const prompt = `Generate an interesting and engaging Farcaster cast. It should be conversational, authentic, and under 320 characters. Topics can include technology, AI, crypto, decentralized social media, or general observations about life.`;
|
|
2041
1954
|
try {
|
|
2042
|
-
const response = await this.runtime.useModel(
|
|
1955
|
+
const response = await this.runtime.useModel(ModelType2.TEXT_SMALL, {
|
|
2043
1956
|
prompt,
|
|
2044
1957
|
maxTokens: 100
|
|
2045
1958
|
});
|
|
@@ -2052,7 +1965,7 @@ class FarcasterCastService {
|
|
|
2052
1965
|
async truncateCast(text) {
|
|
2053
1966
|
const prompt = `Shorten this text to under 320 characters while keeping the main message intact: "${text}"`;
|
|
2054
1967
|
try {
|
|
2055
|
-
const response = await this.runtime.useModel(
|
|
1968
|
+
const response = await this.runtime.useModel(ModelType2.TEXT_SMALL, {
|
|
2056
1969
|
prompt,
|
|
2057
1970
|
maxTokens: 100
|
|
2058
1971
|
});
|
|
@@ -2068,9 +1981,9 @@ class FarcasterCastService {
|
|
|
2068
1981
|
}
|
|
2069
1982
|
async storeCastInMemory(roomId, cast) {
|
|
2070
1983
|
try {
|
|
2071
|
-
const entityId =
|
|
1984
|
+
const entityId = createUniqueUuid3(this.runtime, cast.userId);
|
|
2072
1985
|
const memory = {
|
|
2073
|
-
id:
|
|
1986
|
+
id: createUniqueUuid3(this.runtime, cast.id),
|
|
2074
1987
|
agentId: this.runtime.agentId,
|
|
2075
1988
|
entityId,
|
|
2076
1989
|
content: {
|
|
@@ -2078,7 +1991,11 @@ class FarcasterCastService {
|
|
|
2078
1991
|
castHash: String(cast.metadata?.castHash || ""),
|
|
2079
1992
|
castId: cast.id,
|
|
2080
1993
|
author: cast.username,
|
|
2081
|
-
timestamp: cast.timestamp
|
|
1994
|
+
timestamp: cast.timestamp,
|
|
1995
|
+
accountId: this.getAccountId()
|
|
1996
|
+
},
|
|
1997
|
+
metadata: {
|
|
1998
|
+
accountId: this.getAccountId()
|
|
2082
1999
|
},
|
|
2083
2000
|
roomId,
|
|
2084
2001
|
createdAt: Date.now()
|
|
@@ -2088,11 +2005,58 @@ class FarcasterCastService {
|
|
|
2088
2005
|
this.runtime.logger.error(`Failed to store cast in memory: ${JSON.stringify({ error })}`);
|
|
2089
2006
|
}
|
|
2090
2007
|
}
|
|
2008
|
+
farcasterCastToMemory(runtime, cast) {
|
|
2009
|
+
const authorId = cast.userId || "unknown";
|
|
2010
|
+
const entityId = authorId === runtime.agentId ? runtime.agentId : createUniqueUuid3(runtime, `farcaster:user:${authorId}`);
|
|
2011
|
+
const roomId = cast.roomId || createUniqueUuid3(runtime, `farcaster:feed:${authorId}`);
|
|
2012
|
+
const castHash = typeof cast.metadata?.castHash === "string" ? cast.metadata.castHash : cast.id;
|
|
2013
|
+
return {
|
|
2014
|
+
id: createUniqueUuid3(runtime, `farcaster:cast:${castHash}`),
|
|
2015
|
+
agentId: runtime.agentId,
|
|
2016
|
+
entityId,
|
|
2017
|
+
roomId,
|
|
2018
|
+
createdAt: cast.timestamp || Date.now(),
|
|
2019
|
+
content: {
|
|
2020
|
+
text: cast.text,
|
|
2021
|
+
source: FARCASTER_SOURCE,
|
|
2022
|
+
channelType: ChannelType2.FEED,
|
|
2023
|
+
...cast.inReplyTo ? { inReplyTo: createUniqueUuid3(runtime, `farcaster:cast:${cast.inReplyTo}`) } : {}
|
|
2024
|
+
},
|
|
2025
|
+
metadata: {
|
|
2026
|
+
type: "message",
|
|
2027
|
+
source: FARCASTER_SOURCE,
|
|
2028
|
+
accountId: this.getAccountId(),
|
|
2029
|
+
provider: FARCASTER_SOURCE,
|
|
2030
|
+
timestamp: cast.timestamp,
|
|
2031
|
+
fromBot: entityId === runtime.agentId,
|
|
2032
|
+
messageIdFull: castHash,
|
|
2033
|
+
chatType: ChannelType2.FEED,
|
|
2034
|
+
sender: {
|
|
2035
|
+
id: authorId,
|
|
2036
|
+
username: cast.username
|
|
2037
|
+
},
|
|
2038
|
+
farcaster: {
|
|
2039
|
+
accountId: this.getAccountId(),
|
|
2040
|
+
castId: cast.id,
|
|
2041
|
+
castHash,
|
|
2042
|
+
authorFid: cast.metadata?.authorFid,
|
|
2043
|
+
username: cast.username,
|
|
2044
|
+
inReplyTo: cast.inReplyTo,
|
|
2045
|
+
metrics: {
|
|
2046
|
+
recasts: cast.metadata?.recasts,
|
|
2047
|
+
replies: cast.metadata?.replies,
|
|
2048
|
+
likes: cast.metadata?.likes
|
|
2049
|
+
},
|
|
2050
|
+
...cast.metadata ?? {}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2091
2055
|
castToFarcasterCast(cast, agentId) {
|
|
2092
2056
|
return {
|
|
2093
2057
|
id: castUuid({ hash: cast.hash, agentId }),
|
|
2094
2058
|
agentId,
|
|
2095
|
-
roomId:
|
|
2059
|
+
roomId: createUniqueUuid3(this.runtime, cast.threadId || cast.hash),
|
|
2096
2060
|
userId: cast.profile.fid.toString(),
|
|
2097
2061
|
username: cast.profile.username,
|
|
2098
2062
|
text: cast.text,
|
|
@@ -2102,6 +2066,7 @@ class FarcasterCastService {
|
|
|
2102
2066
|
castHash: cast.hash,
|
|
2103
2067
|
authorFid: cast.authorFid,
|
|
2104
2068
|
source: FARCASTER_SOURCE,
|
|
2069
|
+
accountId: this.getAccountId(),
|
|
2105
2070
|
...cast.threadId ? { threadId: cast.threadId } : {},
|
|
2106
2071
|
...cast.stats ? {
|
|
2107
2072
|
recasts: cast.stats.recasts,
|
|
@@ -2114,19 +2079,24 @@ class FarcasterCastService {
|
|
|
2114
2079
|
}
|
|
2115
2080
|
|
|
2116
2081
|
// services/MessageService.ts
|
|
2117
|
-
import { createUniqueUuid as
|
|
2082
|
+
import { createUniqueUuid as createUniqueUuid4 } from "@elizaos/core";
|
|
2118
2083
|
class FarcasterMessageService {
|
|
2119
2084
|
client;
|
|
2120
2085
|
runtime;
|
|
2121
|
-
|
|
2086
|
+
accountId;
|
|
2087
|
+
constructor(client, runtime, accountId = DEFAULT_FARCASTER_ACCOUNT_ID) {
|
|
2122
2088
|
this.client = client;
|
|
2123
2089
|
this.runtime = runtime;
|
|
2090
|
+
this.accountId = accountId;
|
|
2091
|
+
}
|
|
2092
|
+
getAccountId() {
|
|
2093
|
+
return normalizeFarcasterAccountId(this.accountId);
|
|
2124
2094
|
}
|
|
2125
2095
|
castToMessage(cast, agentId, extraMetadata) {
|
|
2126
2096
|
return {
|
|
2127
2097
|
id: castUuid({ hash: cast.hash, agentId }),
|
|
2128
2098
|
agentId,
|
|
2129
|
-
roomId:
|
|
2099
|
+
roomId: createUniqueUuid4(this.runtime, cast.threadId || cast.hash),
|
|
2130
2100
|
userId: cast.profile.fid.toString(),
|
|
2131
2101
|
username: cast.profile.username,
|
|
2132
2102
|
text: cast.text,
|
|
@@ -2135,6 +2105,7 @@ class FarcasterMessageService {
|
|
|
2135
2105
|
inReplyTo: cast.inReplyTo ? castUuid({ hash: cast.inReplyTo.hash, agentId }) : undefined,
|
|
2136
2106
|
metadata: {
|
|
2137
2107
|
source: FARCASTER_SOURCE,
|
|
2108
|
+
accountId: this.getAccountId(),
|
|
2138
2109
|
castHash: cast.hash,
|
|
2139
2110
|
threadId: cast.threadId,
|
|
2140
2111
|
authorFid: cast.authorFid,
|
|
@@ -2145,7 +2116,7 @@ class FarcasterMessageService {
|
|
|
2145
2116
|
async getMessages(options) {
|
|
2146
2117
|
try {
|
|
2147
2118
|
const { agentId, roomId, limit = 20 } = options;
|
|
2148
|
-
const fid = getFarcasterFid(this.runtime);
|
|
2119
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
2149
2120
|
if (!fid) {
|
|
2150
2121
|
this.runtime.logger.error("[Farcaster] FARCASTER_FID is not configured");
|
|
2151
2122
|
return [];
|
|
@@ -2169,11 +2140,15 @@ class FarcasterMessageService {
|
|
|
2169
2140
|
}
|
|
2170
2141
|
async sendMessage(options) {
|
|
2171
2142
|
try {
|
|
2143
|
+
const requestedAccountId = normalizeFarcasterAccountId(options.accountId ?? readFarcasterAccountId(options.metadata) ?? this.getAccountId());
|
|
2144
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
2145
|
+
throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
|
|
2146
|
+
}
|
|
2172
2147
|
const { text, type, roomId, replyToId, agentId } = options;
|
|
2173
2148
|
let inReplyTo;
|
|
2174
2149
|
if (replyToId && type === "REPLY" /* REPLY */) {
|
|
2175
2150
|
const parentHash = typeof options.metadata?.parentHash === "string" ? options.metadata.parentHash : replyToId;
|
|
2176
|
-
const fid = getFarcasterFid(this.runtime);
|
|
2151
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
2177
2152
|
if (!fid) {
|
|
2178
2153
|
throw new Error("FARCASTER_FID is not configured");
|
|
2179
2154
|
}
|
|
@@ -2193,15 +2168,16 @@ class FarcasterMessageService {
|
|
|
2193
2168
|
const message = this.castToMessage(cast, agentId, options.metadata);
|
|
2194
2169
|
message.roomId = roomId;
|
|
2195
2170
|
message.type = type;
|
|
2196
|
-
const
|
|
2171
|
+
const postGeneratedPayload = {
|
|
2197
2172
|
runtime: this.runtime,
|
|
2198
2173
|
source: FARCASTER_SOURCE,
|
|
2199
2174
|
castHash: cast.hash,
|
|
2200
2175
|
...cast.threadId ? { threadId: cast.threadId } : {},
|
|
2201
2176
|
messageId: message.id,
|
|
2202
|
-
roomId
|
|
2177
|
+
roomId,
|
|
2178
|
+
accountId: this.getAccountId()
|
|
2203
2179
|
};
|
|
2204
|
-
await this.runtime.emitEvent("
|
|
2180
|
+
await this.runtime.emitEvent("FARCASTER_POST_GENERATED" /* POST_GENERATED */, postGeneratedPayload);
|
|
2205
2181
|
return message;
|
|
2206
2182
|
} catch (error) {
|
|
2207
2183
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2253,9 +2229,7 @@ class FarcasterMessageService {
|
|
|
2253
2229
|
// services/FarcasterService.ts
|
|
2254
2230
|
class FarcasterService extends Service {
|
|
2255
2231
|
static instance;
|
|
2256
|
-
|
|
2257
|
-
messageServices = new Map;
|
|
2258
|
-
castServices = new Map;
|
|
2232
|
+
agents = new Map;
|
|
2259
2233
|
static serviceType = FARCASTER_SERVICE_NAME;
|
|
2260
2234
|
description = "Farcaster integration service for sending and receiving casts";
|
|
2261
2235
|
capabilityDescription = "The agent is able to send and receive messages on farcaster";
|
|
@@ -2270,8 +2244,7 @@ class FarcasterService extends Service {
|
|
|
2270
2244
|
}
|
|
2271
2245
|
static async start(runtime) {
|
|
2272
2246
|
const service = FarcasterService.getInstance();
|
|
2273
|
-
|
|
2274
|
-
if (manager) {
|
|
2247
|
+
if (service.agents.has(runtime.agentId)) {
|
|
2275
2248
|
runtime.logger.warn({ agentId: runtime.agentId }, "Farcaster service already started");
|
|
2276
2249
|
return service;
|
|
2277
2250
|
}
|
|
@@ -2279,80 +2252,213 @@ class FarcasterService extends Service {
|
|
|
2279
2252
|
runtime.logger.debug({ agentId: runtime.agentId }, "Farcaster service not enabled");
|
|
2280
2253
|
return service;
|
|
2281
2254
|
}
|
|
2282
|
-
const
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
service.
|
|
2289
|
-
|
|
2290
|
-
|
|
2255
|
+
const accounts = {
|
|
2256
|
+
defaultAccountId: normalizeFarcasterAccountId(resolveDefaultFarcasterAccountId(runtime)),
|
|
2257
|
+
managers: new Map,
|
|
2258
|
+
messageServices: new Map,
|
|
2259
|
+
castServices: new Map
|
|
2260
|
+
};
|
|
2261
|
+
service.agents.set(runtime.agentId, accounts);
|
|
2262
|
+
for (const accountId of listFarcasterAccountIds(runtime)) {
|
|
2263
|
+
if (!hasFarcasterEnabled(runtime, accountId)) {
|
|
2264
|
+
continue;
|
|
2265
|
+
}
|
|
2266
|
+
const farcasterConfig = validateFarcasterConfig(runtime, accountId);
|
|
2267
|
+
const manager = new FarcasterAgentManager(runtime, farcasterConfig);
|
|
2268
|
+
accounts.managers.set(accountId, manager);
|
|
2269
|
+
accounts.messageServices.set(accountId, new FarcasterMessageService(manager.client, runtime, accountId));
|
|
2270
|
+
accounts.castServices.set(accountId, new FarcasterCastService(manager.client, runtime, accountId));
|
|
2271
|
+
await manager.start();
|
|
2272
|
+
runtime.logger.success({ agentId: runtime.agentId, accountId }, "Farcaster client started");
|
|
2273
|
+
}
|
|
2291
2274
|
return service;
|
|
2292
2275
|
}
|
|
2293
2276
|
static async stop(runtime) {
|
|
2294
2277
|
const service = FarcasterService.getInstance();
|
|
2295
|
-
const
|
|
2296
|
-
if (
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
service.
|
|
2278
|
+
const accounts = service.agents.get(runtime.agentId);
|
|
2279
|
+
if (accounts) {
|
|
2280
|
+
for (const manager of accounts.managers.values()) {
|
|
2281
|
+
await manager.stop();
|
|
2282
|
+
}
|
|
2283
|
+
service.agents.delete(runtime.agentId);
|
|
2301
2284
|
runtime.logger.info({ agentId: runtime.agentId }, "Farcaster client stopped");
|
|
2302
2285
|
} else {
|
|
2303
2286
|
runtime.logger.debug({ agentId: runtime.agentId }, "Farcaster service not running");
|
|
2304
2287
|
}
|
|
2305
2288
|
}
|
|
2289
|
+
static registerSendHandlers(runtime, serviceInstance) {
|
|
2290
|
+
const accounts = serviceInstance?.agents.get(runtime.agentId);
|
|
2291
|
+
if (!accounts || accounts.castServices.size === 0) {
|
|
2292
|
+
runtime.logger.warn({ src: "plugin:farcaster", agentId: runtime.agentId }, "Cannot register Farcaster post connector; cast service is not initialized");
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
const withPostConnector = runtime;
|
|
2296
|
+
if (typeof withPostConnector.registerPostConnector !== "function") {
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
for (const castService of accounts.castServices.values()) {
|
|
2300
|
+
const accountId = castService.getAccountId();
|
|
2301
|
+
withPostConnector.registerPostConnector({
|
|
2302
|
+
source: "farcaster",
|
|
2303
|
+
accountId,
|
|
2304
|
+
label: "Farcaster",
|
|
2305
|
+
description: "Farcaster public cast connector for publishing casts and reading or searching the authenticated account's recent feed.",
|
|
2306
|
+
capabilities: ["post", "fetch_feed", "search_posts"],
|
|
2307
|
+
contexts: ["social", "social_posting", "connectors"],
|
|
2308
|
+
metadata: {
|
|
2309
|
+
accountId,
|
|
2310
|
+
service: FARCASTER_SERVICE_NAME
|
|
2311
|
+
},
|
|
2312
|
+
postHandler: castService.handleSendPost.bind(castService),
|
|
2313
|
+
fetchFeed: castService.fetchFeed.bind(castService),
|
|
2314
|
+
searchPosts: castService.searchPosts.bind(castService),
|
|
2315
|
+
contentShaping: {
|
|
2316
|
+
systemPromptFragment: "For Farcaster casts, write a conversational public cast under 320 characters. If replying, keep enough context for a public thread.",
|
|
2317
|
+
constraints: {
|
|
2318
|
+
maxLength: 320,
|
|
2319
|
+
supportsMarkdown: false,
|
|
2320
|
+
channelType: ChannelType3.FEED
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
runtime.logger.info({ src: "plugin:farcaster", agentId: runtime.agentId }, "Registered Farcaster post connector");
|
|
2326
|
+
}
|
|
2306
2327
|
async stop() {
|
|
2307
|
-
for (const
|
|
2308
|
-
const
|
|
2309
|
-
|
|
2328
|
+
for (const [agentId, accounts] of Array.from(this.agents.entries())) {
|
|
2329
|
+
const runtime = accounts.managers.values().next().value?.runtime;
|
|
2330
|
+
runtime?.logger.debug("Stopping Farcaster service");
|
|
2310
2331
|
try {
|
|
2311
|
-
|
|
2332
|
+
if (runtime) {
|
|
2333
|
+
await FarcasterService.stop(runtime);
|
|
2334
|
+
} else {
|
|
2335
|
+
this.agents.delete(agentId);
|
|
2336
|
+
}
|
|
2312
2337
|
} catch (error) {
|
|
2313
|
-
|
|
2338
|
+
runtime?.logger.error({ agentId, error }, "Error stopping Farcaster service");
|
|
2314
2339
|
}
|
|
2315
2340
|
}
|
|
2316
2341
|
}
|
|
2317
|
-
getMessageService(agentId) {
|
|
2318
|
-
return this.
|
|
2342
|
+
getMessageService(agentId, accountId) {
|
|
2343
|
+
return this.getMessageServiceForAccount(accountId, agentId);
|
|
2319
2344
|
}
|
|
2320
|
-
getCastService(agentId) {
|
|
2321
|
-
return this.
|
|
2345
|
+
getCastService(agentId, accountId) {
|
|
2346
|
+
return this.getCastServiceForAccount(accountId, agentId);
|
|
2347
|
+
}
|
|
2348
|
+
getMessageServiceForAccount(accountId, agentId) {
|
|
2349
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2350
|
+
if (!resolvedAgentId)
|
|
2351
|
+
return;
|
|
2352
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
2353
|
+
if (!accounts)
|
|
2354
|
+
return;
|
|
2355
|
+
const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
|
|
2356
|
+
return accounts.messageServices.get(id);
|
|
2357
|
+
}
|
|
2358
|
+
getCastServiceForAccount(accountId, agentId) {
|
|
2359
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2360
|
+
if (!resolvedAgentId)
|
|
2361
|
+
return;
|
|
2362
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
2363
|
+
if (!accounts)
|
|
2364
|
+
return;
|
|
2365
|
+
const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
|
|
2366
|
+
return accounts.castServices.get(id);
|
|
2367
|
+
}
|
|
2368
|
+
getManagerForAccount(accountId, agentId) {
|
|
2369
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2370
|
+
if (!resolvedAgentId)
|
|
2371
|
+
return;
|
|
2372
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
2373
|
+
if (!accounts)
|
|
2374
|
+
return;
|
|
2375
|
+
const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
|
|
2376
|
+
return accounts.managers.get(id);
|
|
2377
|
+
}
|
|
2378
|
+
getDefaultAccountId(agentId) {
|
|
2379
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2380
|
+
return resolvedAgentId ? this.agents.get(resolvedAgentId)?.defaultAccountId : undefined;
|
|
2381
|
+
}
|
|
2382
|
+
listAccountIds(agentId) {
|
|
2383
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2384
|
+
const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
|
|
2385
|
+
return accounts ? Array.from(accounts.managers.keys()) : [];
|
|
2386
|
+
}
|
|
2387
|
+
getManagersForAgent(agentId) {
|
|
2388
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2389
|
+
const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
|
|
2390
|
+
return new Map(accounts?.managers ?? []);
|
|
2322
2391
|
}
|
|
2323
2392
|
async healthCheck() {
|
|
2324
2393
|
const managerStatuses = {};
|
|
2325
2394
|
let overallHealthy = true;
|
|
2326
|
-
for (const [agentId,
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2395
|
+
for (const [agentId, accounts] of Array.from(this.agents.entries())) {
|
|
2396
|
+
managerStatuses[agentId] = {};
|
|
2397
|
+
for (const [accountId, manager] of Array.from(accounts.managers.entries())) {
|
|
2398
|
+
try {
|
|
2399
|
+
const fid = getFarcasterFid(manager.runtime, accountId);
|
|
2400
|
+
if (!fid) {
|
|
2401
|
+
throw new Error("FARCASTER_FID not configured");
|
|
2402
|
+
}
|
|
2403
|
+
const profile = await manager.client.getProfile(fid);
|
|
2404
|
+
managerStatuses[agentId][accountId] = {
|
|
2405
|
+
status: "healthy",
|
|
2406
|
+
fid: profile.fid,
|
|
2407
|
+
username: profile.username
|
|
2408
|
+
};
|
|
2409
|
+
} catch (error) {
|
|
2410
|
+
managerStatuses[agentId][accountId] = {
|
|
2411
|
+
status: "unhealthy",
|
|
2412
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
2413
|
+
};
|
|
2414
|
+
overallHealthy = false;
|
|
2331
2415
|
}
|
|
2332
|
-
const profile = await manager.client.getProfile(fid);
|
|
2333
|
-
managerStatuses[agentId] = {
|
|
2334
|
-
status: "healthy",
|
|
2335
|
-
fid: profile.fid,
|
|
2336
|
-
username: profile.username
|
|
2337
|
-
};
|
|
2338
|
-
} catch (error) {
|
|
2339
|
-
managerStatuses[agentId] = {
|
|
2340
|
-
status: "unhealthy",
|
|
2341
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
2342
|
-
};
|
|
2343
|
-
overallHealthy = false;
|
|
2344
2416
|
}
|
|
2345
2417
|
}
|
|
2346
2418
|
return {
|
|
2347
2419
|
healthy: overallHealthy,
|
|
2348
2420
|
details: {
|
|
2349
|
-
activeManagers: this.managers.size,
|
|
2421
|
+
activeManagers: Array.from(this.agents.values()).reduce((total, accounts) => total + accounts.managers.size, 0),
|
|
2350
2422
|
managerStatuses
|
|
2351
2423
|
}
|
|
2352
2424
|
};
|
|
2353
2425
|
}
|
|
2354
2426
|
getActiveManagers() {
|
|
2355
|
-
return new Map(this.managers);
|
|
2427
|
+
return new Map(Array.from(this.agents.entries()).flatMap(([agentId, accounts]) => Array.from(accounts.managers.entries()).map(([accountId, manager]) => [`${agentId}:${accountId}`, manager])));
|
|
2428
|
+
}
|
|
2429
|
+
firstAgentId() {
|
|
2430
|
+
return this.agents.keys().next().value;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
// workflow-credential-provider.ts
|
|
2435
|
+
import { Service as Service2 } from "@elizaos/core";
|
|
2436
|
+
var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
|
|
2437
|
+
var SUPPORTED = ["httpHeaderAuth"];
|
|
2438
|
+
|
|
2439
|
+
class FarcasterWorkflowCredentialProvider extends Service2 {
|
|
2440
|
+
static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
|
|
2441
|
+
capabilityDescription = "Supplies Farcaster (Neynar API) credentials to the workflow plugin.";
|
|
2442
|
+
static async start(runtime) {
|
|
2443
|
+
return new FarcasterWorkflowCredentialProvider(runtime);
|
|
2444
|
+
}
|
|
2445
|
+
async stop() {}
|
|
2446
|
+
async resolve(_userId, credType) {
|
|
2447
|
+
if (credType !== "httpHeaderAuth")
|
|
2448
|
+
return null;
|
|
2449
|
+
const neynarApiKey = this.runtime.getSetting("FARCASTER_NEYNAR_API_KEY");
|
|
2450
|
+
if (!neynarApiKey?.trim())
|
|
2451
|
+
return null;
|
|
2452
|
+
return {
|
|
2453
|
+
status: "credential_data",
|
|
2454
|
+
data: { name: "api_key", value: neynarApiKey.trim() }
|
|
2455
|
+
};
|
|
2456
|
+
}
|
|
2457
|
+
checkCredentialTypes(credTypes) {
|
|
2458
|
+
return {
|
|
2459
|
+
supported: credTypes.filter((t) => SUPPORTED.includes(t)),
|
|
2460
|
+
unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
|
|
2461
|
+
};
|
|
2356
2462
|
}
|
|
2357
2463
|
}
|
|
2358
2464
|
|
|
@@ -2360,20 +2466,40 @@ class FarcasterService extends Service {
|
|
|
2360
2466
|
var farcasterPlugin = {
|
|
2361
2467
|
name: "farcaster",
|
|
2362
2468
|
description: "Farcaster client plugin for sending and receiving casts",
|
|
2363
|
-
services: [FarcasterService],
|
|
2364
|
-
actions:
|
|
2469
|
+
services: [FarcasterService, FarcasterWorkflowCredentialProvider],
|
|
2470
|
+
actions: [],
|
|
2365
2471
|
providers: farcasterProviders,
|
|
2366
|
-
routes: farcasterWebhookRoutes
|
|
2472
|
+
routes: farcasterWebhookRoutes,
|
|
2473
|
+
autoEnable: {
|
|
2474
|
+
connectorKeys: ["farcaster"]
|
|
2475
|
+
},
|
|
2476
|
+
async init(_config, runtime) {
|
|
2477
|
+
try {
|
|
2478
|
+
const manager = getConnectorAccountManager(runtime);
|
|
2479
|
+
manager.registerProvider(createFarcasterConnectorAccountProvider(runtime));
|
|
2480
|
+
} catch (err) {
|
|
2481
|
+
logger.warn({
|
|
2482
|
+
src: "plugin:farcaster",
|
|
2483
|
+
err: err instanceof Error ? err.message : String(err)
|
|
2484
|
+
}, "Failed to register Farcaster provider with ConnectorAccountManager");
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2367
2487
|
};
|
|
2368
|
-
var typescript_default = farcasterPlugin;
|
|
2369
2488
|
export {
|
|
2489
|
+
resolveDefaultFarcasterAccountId,
|
|
2490
|
+
readFarcasterAccountId,
|
|
2491
|
+
normalizeFarcasterAccountId,
|
|
2492
|
+
listFarcasterAccountIds,
|
|
2370
2493
|
isEmbedUrl,
|
|
2371
2494
|
isEmbedCast,
|
|
2372
2495
|
farcasterPlugin,
|
|
2373
|
-
|
|
2496
|
+
default2 as default,
|
|
2497
|
+
createFarcasterConnectorAccountProvider,
|
|
2374
2498
|
FarcasterService,
|
|
2375
2499
|
FarcasterClient,
|
|
2376
|
-
|
|
2500
|
+
FARCASTER_PROVIDER_ID,
|
|
2501
|
+
EmbedManager,
|
|
2502
|
+
DEFAULT_FARCASTER_ACCOUNT_ID
|
|
2377
2503
|
};
|
|
2378
2504
|
|
|
2379
|
-
//# debugId=
|
|
2505
|
+
//# debugId=8F6A0088AE903E9C64756E2164756E21
|