@elizaos/plugin-farcaster 2.0.0-alpha.7 → 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/cjs/index.node.cjs
CHANGED
|
@@ -1,397 +1,123 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
1
3
|
var __defProp = Object.defineProperty;
|
|
2
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
6
32
|
var __toCommonJS = (from) => {
|
|
7
|
-
var entry = __moduleCache.get(from), desc;
|
|
33
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
8
34
|
if (entry)
|
|
9
35
|
return entry;
|
|
10
36
|
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
37
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
38
|
+
for (var key of __getOwnPropNames(from))
|
|
39
|
+
if (!__hasOwnProp.call(entry, key))
|
|
40
|
+
__defProp(entry, key, {
|
|
41
|
+
get: __accessProp.bind(from, key),
|
|
42
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
43
|
+
});
|
|
44
|
+
}
|
|
16
45
|
__moduleCache.set(from, entry);
|
|
17
46
|
return entry;
|
|
18
47
|
};
|
|
48
|
+
var __moduleCache;
|
|
49
|
+
var __returnValue = (v) => v;
|
|
50
|
+
function __exportSetter(name, newValue) {
|
|
51
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
52
|
+
}
|
|
19
53
|
var __export = (target, all) => {
|
|
20
54
|
for (var name in all)
|
|
21
55
|
__defProp(target, name, {
|
|
22
56
|
get: all[name],
|
|
23
57
|
enumerable: true,
|
|
24
58
|
configurable: true,
|
|
25
|
-
set: (
|
|
59
|
+
set: __exportSetter.bind(all, name)
|
|
26
60
|
});
|
|
27
61
|
};
|
|
28
62
|
|
|
29
63
|
// index.node.ts
|
|
30
64
|
var exports_index_node = {};
|
|
31
65
|
__export(exports_index_node, {
|
|
66
|
+
resolveDefaultFarcasterAccountId: () => resolveDefaultFarcasterAccountId,
|
|
67
|
+
readFarcasterAccountId: () => readFarcasterAccountId,
|
|
68
|
+
normalizeFarcasterAccountId: () => normalizeFarcasterAccountId,
|
|
69
|
+
listFarcasterAccountIds: () => listFarcasterAccountIds,
|
|
32
70
|
isEmbedUrl: () => isEmbedUrl,
|
|
33
71
|
isEmbedCast: () => isEmbedCast,
|
|
34
72
|
farcasterPlugin: () => farcasterPlugin,
|
|
35
|
-
default: () =>
|
|
73
|
+
default: () => import__2.default,
|
|
74
|
+
createFarcasterConnectorAccountProvider: () => createFarcasterConnectorAccountProvider,
|
|
36
75
|
FarcasterService: () => FarcasterService,
|
|
37
76
|
FarcasterClient: () => FarcasterClient,
|
|
38
|
-
|
|
77
|
+
FARCASTER_PROVIDER_ID: () => FARCASTER_PROVIDER_ID,
|
|
78
|
+
EmbedManager: () => EmbedManager,
|
|
79
|
+
DEFAULT_FARCASTER_ACCOUNT_ID: () => DEFAULT_FARCASTER_ACCOUNT_ID
|
|
39
80
|
});
|
|
40
81
|
module.exports = __toCommonJS(exports_index_node);
|
|
41
82
|
|
|
42
|
-
//
|
|
43
|
-
var
|
|
44
|
-
|
|
45
|
-
// generated/specs/specs.ts
|
|
46
|
-
var coreActionsSpec = {
|
|
47
|
-
version: "1.0.0",
|
|
48
|
-
actions: [
|
|
49
|
-
{
|
|
50
|
-
name: "SEND_CAST",
|
|
51
|
-
description: "Posts a cast (message) on Farcaster",
|
|
52
|
-
similes: ["POST_CAST", "FARCASTER_POST", "CAST", "SHARE_ON_FARCASTER", "ANNOUNCE"],
|
|
53
|
-
parameters: []
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: "REPLY_TO_CAST",
|
|
57
|
-
description: "Replies to a cast on Farcaster",
|
|
58
|
-
similes: ["REPLY_CAST", "RESPOND_CAST", "ANSWER_CAST", "COMMENT_CAST"],
|
|
59
|
-
parameters: []
|
|
60
|
-
}
|
|
61
|
-
]
|
|
62
|
-
};
|
|
63
|
-
var allActionsSpec = {
|
|
64
|
-
version: "1.0.0",
|
|
65
|
-
actions: [
|
|
66
|
-
{
|
|
67
|
-
name: "SEND_CAST",
|
|
68
|
-
description: "Posts a cast (message) on Farcaster",
|
|
69
|
-
similes: ["POST_CAST", "FARCASTER_POST", "CAST", "SHARE_ON_FARCASTER", "ANNOUNCE"],
|
|
70
|
-
parameters: []
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: "REPLY_TO_CAST",
|
|
74
|
-
description: "Replies to a cast on Farcaster",
|
|
75
|
-
similes: ["REPLY_CAST", "RESPOND_CAST", "ANSWER_CAST", "COMMENT_CAST"],
|
|
76
|
-
parameters: []
|
|
77
|
-
}
|
|
78
|
-
]
|
|
79
|
-
};
|
|
80
|
-
var coreProvidersSpec = {
|
|
81
|
-
version: "1.0.0",
|
|
82
|
-
providers: [
|
|
83
|
-
{
|
|
84
|
-
name: "farcasterProfile",
|
|
85
|
-
description: "Provides information about the agent",
|
|
86
|
-
dynamic: true
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
name: "farcasterThread",
|
|
90
|
-
description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
|
|
91
|
-
dynamic: true
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
name: "farcasterTimeline",
|
|
95
|
-
description: "Provides recent casts from the agent",
|
|
96
|
-
dynamic: true
|
|
97
|
-
}
|
|
98
|
-
]
|
|
99
|
-
};
|
|
100
|
-
var allProvidersSpec = {
|
|
101
|
-
version: "1.0.0",
|
|
102
|
-
providers: [
|
|
103
|
-
{
|
|
104
|
-
name: "farcasterProfile",
|
|
105
|
-
description: "Provides information about the agent",
|
|
106
|
-
dynamic: true
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: "farcasterThread",
|
|
110
|
-
description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
|
|
111
|
-
dynamic: true
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: "farcasterTimeline",
|
|
115
|
-
description: "Provides recent casts from the agent",
|
|
116
|
-
dynamic: true
|
|
117
|
-
}
|
|
118
|
-
]
|
|
119
|
-
};
|
|
120
|
-
var coreEvaluatorsSpec = {
|
|
121
|
-
version: "1.0.0",
|
|
122
|
-
evaluators: []
|
|
123
|
-
};
|
|
124
|
-
var allEvaluatorsSpec = {
|
|
125
|
-
version: "1.0.0",
|
|
126
|
-
evaluators: []
|
|
127
|
-
};
|
|
128
|
-
var coreActionDocs = coreActionsSpec.actions;
|
|
129
|
-
var allActionDocs = allActionsSpec.actions;
|
|
130
|
-
var coreProviderDocs = coreProvidersSpec.providers;
|
|
131
|
-
var allProviderDocs = allProvidersSpec.providers;
|
|
132
|
-
var coreEvaluatorDocs = coreEvaluatorsSpec.evaluators;
|
|
133
|
-
var allEvaluatorDocs = allEvaluatorsSpec.evaluators;
|
|
83
|
+
// index.ts
|
|
84
|
+
var import_core11 = require("@elizaos/core");
|
|
134
85
|
|
|
135
|
-
//
|
|
136
|
-
var
|
|
137
|
-
var
|
|
138
|
-
var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
|
|
139
|
-
var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
|
|
140
|
-
var coreEvaluatorMap = new Map(coreEvaluatorDocs.map((doc) => [doc.name, doc]));
|
|
141
|
-
var allEvaluatorMap = new Map(allEvaluatorDocs.map((doc) => [doc.name, doc]));
|
|
142
|
-
function getActionSpec(name) {
|
|
143
|
-
return coreActionMap.get(name) ?? allActionMap.get(name);
|
|
144
|
-
}
|
|
145
|
-
function requireActionSpec(name) {
|
|
146
|
-
const spec = getActionSpec(name);
|
|
147
|
-
if (!spec) {
|
|
148
|
-
throw new Error(`Action spec not found: ${name}`);
|
|
149
|
-
}
|
|
150
|
-
return spec;
|
|
151
|
-
}
|
|
152
|
-
function getProviderSpec(name) {
|
|
153
|
-
return coreProviderMap.get(name) ?? allProviderMap.get(name);
|
|
154
|
-
}
|
|
155
|
-
function requireProviderSpec(name) {
|
|
156
|
-
const spec = getProviderSpec(name);
|
|
157
|
-
if (!spec) {
|
|
158
|
-
throw new Error(`Provider spec not found: ${name}`);
|
|
159
|
-
}
|
|
160
|
-
return spec;
|
|
161
|
-
}
|
|
86
|
+
// utils/config.ts
|
|
87
|
+
var import_core = require("@elizaos/core");
|
|
88
|
+
var import_zod = require("zod");
|
|
162
89
|
|
|
163
90
|
// types/index.ts
|
|
164
|
-
var
|
|
91
|
+
var zod = __toESM(require("zod"));
|
|
92
|
+
var z2 = zod.z ?? zod;
|
|
165
93
|
var DEFAULT_MAX_CAST_LENGTH = 320;
|
|
166
94
|
var DEFAULT_POLL_INTERVAL = 120;
|
|
167
95
|
var DEFAULT_CAST_INTERVAL_MIN = 90;
|
|
168
96
|
var DEFAULT_CAST_INTERVAL_MAX = 180;
|
|
169
97
|
var DEFAULT_CAST_CACHE_TTL = 1000 * 30 * 60;
|
|
170
98
|
var DEFAULT_CAST_CACHE_SIZE = 9000;
|
|
171
|
-
var FarcasterConfigSchema =
|
|
172
|
-
FARCASTER_DRY_RUN:
|
|
173
|
-
FARCASTER_FID:
|
|
174
|
-
MAX_CAST_LENGTH:
|
|
175
|
-
FARCASTER_POLL_INTERVAL:
|
|
176
|
-
FARCASTER_MODE:
|
|
177
|
-
ENABLE_CAST:
|
|
178
|
-
CAST_INTERVAL_MIN:
|
|
179
|
-
CAST_INTERVAL_MAX:
|
|
180
|
-
ENABLE_ACTION_PROCESSING:
|
|
181
|
-
ACTION_INTERVAL:
|
|
182
|
-
CAST_IMMEDIATELY:
|
|
183
|
-
MAX_ACTIONS_PROCESSING:
|
|
184
|
-
FARCASTER_SIGNER_UUID:
|
|
185
|
-
FARCASTER_NEYNAR_API_KEY:
|
|
186
|
-
FARCASTER_HUB_URL:
|
|
99
|
+
var FarcasterConfigSchema = z2.object({
|
|
100
|
+
FARCASTER_DRY_RUN: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
101
|
+
FARCASTER_FID: z2.number().int().min(1, "Farcaster fid is required"),
|
|
102
|
+
MAX_CAST_LENGTH: z2.number().int().default(DEFAULT_MAX_CAST_LENGTH),
|
|
103
|
+
FARCASTER_POLL_INTERVAL: z2.number().int().default(DEFAULT_POLL_INTERVAL),
|
|
104
|
+
FARCASTER_MODE: z2.enum(["polling", "webhook"]).default("polling"),
|
|
105
|
+
ENABLE_CAST: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
106
|
+
CAST_INTERVAL_MIN: z2.number().int(),
|
|
107
|
+
CAST_INTERVAL_MAX: z2.number().int(),
|
|
108
|
+
ENABLE_ACTION_PROCESSING: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
109
|
+
ACTION_INTERVAL: z2.number().int(),
|
|
110
|
+
CAST_IMMEDIATELY: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
111
|
+
MAX_ACTIONS_PROCESSING: z2.number().int(),
|
|
112
|
+
FARCASTER_SIGNER_UUID: z2.string().min(1, "FARCASTER_SIGNER_UUID is not set"),
|
|
113
|
+
FARCASTER_NEYNAR_API_KEY: z2.string().min(1, "FARCASTER_NEYNAR_API_KEY is not set"),
|
|
114
|
+
FARCASTER_HUB_URL: z2.string().min(1, "FARCASTER_HUB_URL is not set")
|
|
187
115
|
});
|
|
188
116
|
var FARCASTER_SERVICE_NAME = "farcaster";
|
|
189
117
|
var FARCASTER_SOURCE = "farcaster";
|
|
190
118
|
|
|
191
|
-
// actions/replyCast.ts
|
|
192
|
-
var spec = requireActionSpec("REPLY_TO_CAST");
|
|
193
|
-
var replyCastAction = {
|
|
194
|
-
name: spec.name,
|
|
195
|
-
description: spec.description,
|
|
196
|
-
examples: [
|
|
197
|
-
[
|
|
198
|
-
{
|
|
199
|
-
name: "user",
|
|
200
|
-
content: {
|
|
201
|
-
text: "Someone asked about ElizaOS on Farcaster, can you reply?"
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: "assistant",
|
|
206
|
-
content: {
|
|
207
|
-
text: "I'll reply to their question about ElizaOS.",
|
|
208
|
-
actions: ["REPLY_TO_CAST"]
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
],
|
|
212
|
-
[
|
|
213
|
-
{
|
|
214
|
-
name: "user",
|
|
215
|
-
content: { text: "Reply to that cast and thank them for the feedback" }
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
name: "assistant",
|
|
219
|
-
content: {
|
|
220
|
-
text: "I'll reply with a thank you message.",
|
|
221
|
-
actions: ["REPLY_TO_CAST"]
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
]
|
|
225
|
-
],
|
|
226
|
-
validate: async (runtime, message, state, options) => {
|
|
227
|
-
const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
|
|
228
|
-
const __avText = __avTextRaw.toLowerCase();
|
|
229
|
-
const __avKeywords = ["reply", "cast"];
|
|
230
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
231
|
-
const __avRegex = /\b(?:reply|cast)\b/i;
|
|
232
|
-
const __avRegexOk = __avRegex.test(__avText);
|
|
233
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
234
|
-
const __avExpectedSource = "farcaster";
|
|
235
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
236
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
237
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
238
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
const __avLegacyValidate = async (runtime2, message2) => {
|
|
242
|
-
const text = message2.content.text?.toLowerCase() || "";
|
|
243
|
-
const keywords = ["reply", "respond", "answer", "comment"];
|
|
244
|
-
const hasKeyword = keywords.some((keyword) => text.includes(keyword));
|
|
245
|
-
const hasParentCast = !!(message2.content.metadata && message2.content.metadata.parentCastHash);
|
|
246
|
-
const service = runtime2.getService(FARCASTER_SERVICE_NAME);
|
|
247
|
-
const isServiceAvailable = !!service?.getMessageService(runtime2.agentId);
|
|
248
|
-
return hasKeyword && (hasParentCast || isServiceAvailable);
|
|
249
|
-
};
|
|
250
|
-
try {
|
|
251
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
252
|
-
} catch {
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
},
|
|
256
|
-
handler: async (runtime, message, state) => {
|
|
257
|
-
try {
|
|
258
|
-
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
259
|
-
const messageService = service?.getMessageService(runtime.agentId);
|
|
260
|
-
if (!messageService) {
|
|
261
|
-
runtime.logger.error("[REPLY_TO_CAST] MessageService not available");
|
|
262
|
-
return { success: false, error: "MessageService not available" };
|
|
263
|
-
}
|
|
264
|
-
const metadata = message.content.metadata;
|
|
265
|
-
const stateParentCastHash = typeof state?.parentCastHash === "string" ? state.parentCastHash : undefined;
|
|
266
|
-
const parentCastHash = metadata?.parentCastHash ?? stateParentCastHash;
|
|
267
|
-
if (!parentCastHash) {
|
|
268
|
-
runtime.logger.error("[REPLY_TO_CAST] No parent cast to reply to");
|
|
269
|
-
return { success: false, error: "No parent cast to reply to" };
|
|
270
|
-
}
|
|
271
|
-
let replyContent = "";
|
|
272
|
-
if (state?.replyContent) {
|
|
273
|
-
replyContent = state.replyContent;
|
|
274
|
-
} else {
|
|
275
|
-
const prompt = `Based on this request: "${message.content.text}", generate a helpful and engaging reply for a Farcaster cast (max 320 characters).`;
|
|
276
|
-
const response = await runtime.useModel(import_core.ModelType.TEXT_LARGE, {
|
|
277
|
-
prompt
|
|
278
|
-
});
|
|
279
|
-
replyContent = typeof response === "string" ? response : String(response);
|
|
280
|
-
}
|
|
281
|
-
if (replyContent.length > 320) {
|
|
282
|
-
replyContent = `${replyContent.substring(0, 317)}...`;
|
|
283
|
-
}
|
|
284
|
-
const reply = await messageService.sendMessage({
|
|
285
|
-
agentId: runtime.agentId,
|
|
286
|
-
roomId: message.roomId,
|
|
287
|
-
text: replyContent,
|
|
288
|
-
type: "REPLY" /* REPLY */,
|
|
289
|
-
replyToId: parentCastHash,
|
|
290
|
-
metadata: {
|
|
291
|
-
parentHash: parentCastHash
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
runtime.logger.info(`[REPLY_TO_CAST] Successfully replied to cast: ${reply.id}`);
|
|
295
|
-
return { success: true, text: `Replied to cast: ${reply.id}` };
|
|
296
|
-
} catch (error) {
|
|
297
|
-
runtime.logger.error("[REPLY_TO_CAST] Error replying to cast:", typeof error === "string" ? error : error.message);
|
|
298
|
-
throw error;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
// actions/sendCast.ts
|
|
304
|
-
var import_core2 = require("@elizaos/core");
|
|
305
|
-
var spec2 = requireActionSpec("SEND_CAST");
|
|
306
|
-
var sendCastAction = {
|
|
307
|
-
name: spec2.name,
|
|
308
|
-
similes: spec2.similes ? [...spec2.similes] : [],
|
|
309
|
-
description: spec2.description,
|
|
310
|
-
examples: spec2.examples ?? [],
|
|
311
|
-
validate: async (runtime, message, state, options) => {
|
|
312
|
-
const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
|
|
313
|
-
const __avText = __avTextRaw.toLowerCase();
|
|
314
|
-
const __avKeywords = ["send", "cast"];
|
|
315
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
316
|
-
const __avRegex = /\b(?:send|cast)\b/i;
|
|
317
|
-
const __avRegexOk = __avRegex.test(__avText);
|
|
318
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
319
|
-
const __avExpectedSource = "farcaster";
|
|
320
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
321
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
322
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
323
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
324
|
-
return false;
|
|
325
|
-
}
|
|
326
|
-
const __avLegacyValidate = async (runtime2, message2) => {
|
|
327
|
-
const text = message2.content.text?.toLowerCase() || "";
|
|
328
|
-
const keywords = ["post", "cast", "share", "announce", "farcaster"];
|
|
329
|
-
const hasKeyword = keywords.some((keyword) => text.includes(keyword));
|
|
330
|
-
const service = runtime2.getService(FARCASTER_SERVICE_NAME);
|
|
331
|
-
const isServiceAvailable = !!service?.getCastService(runtime2.agentId);
|
|
332
|
-
return hasKeyword && isServiceAvailable;
|
|
333
|
-
};
|
|
334
|
-
try {
|
|
335
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
336
|
-
} catch {
|
|
337
|
-
return false;
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
|
-
handler: async (runtime, message, state) => {
|
|
341
|
-
try {
|
|
342
|
-
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
343
|
-
const postService = service?.getCastService(runtime.agentId);
|
|
344
|
-
if (!postService) {
|
|
345
|
-
runtime.logger.error("[SEND_CAST] PostService not available");
|
|
346
|
-
return { success: false, error: "PostService not available" };
|
|
347
|
-
}
|
|
348
|
-
let castContent = "";
|
|
349
|
-
if (state?.castContent) {
|
|
350
|
-
castContent = state.castContent;
|
|
351
|
-
} else {
|
|
352
|
-
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.`;
|
|
353
|
-
const response = await runtime.useModel(import_core2.ModelType.TEXT_LARGE, {
|
|
354
|
-
prompt
|
|
355
|
-
});
|
|
356
|
-
castContent = typeof response === "string" ? response : String(response);
|
|
357
|
-
}
|
|
358
|
-
if (castContent.length > 320) {
|
|
359
|
-
castContent = `${castContent.substring(0, 317)}...`;
|
|
360
|
-
}
|
|
361
|
-
const cast = await postService.createCast({
|
|
362
|
-
agentId: runtime.agentId,
|
|
363
|
-
roomId: import_core2.createUniqueUuid(runtime, "farcaster-timeline"),
|
|
364
|
-
text: castContent
|
|
365
|
-
});
|
|
366
|
-
runtime.logger.info(`[SEND_CAST] Successfully posted cast: ${cast.id}`);
|
|
367
|
-
await runtime.createMemory({
|
|
368
|
-
agentId: runtime.agentId,
|
|
369
|
-
roomId: cast.roomId,
|
|
370
|
-
entityId: runtime.agentId,
|
|
371
|
-
content: {
|
|
372
|
-
text: castContent,
|
|
373
|
-
source: "farcaster",
|
|
374
|
-
metadata: {
|
|
375
|
-
castHash: String(cast.metadata?.castHash || ""),
|
|
376
|
-
action: "SEND_CAST"
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
createdAt: cast.timestamp
|
|
380
|
-
}, "messages");
|
|
381
|
-
return { success: true, text: `Posted cast: ${cast.id}` };
|
|
382
|
-
} catch (error) {
|
|
383
|
-
runtime.logger.error("[SEND_CAST] Error posting cast:", typeof error === "string" ? error : error.message);
|
|
384
|
-
return { success: false, error: error.message };
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
|
|
389
|
-
// actions/index.ts
|
|
390
|
-
var farcasterActions = [sendCastAction, replyCastAction];
|
|
391
|
-
|
|
392
119
|
// utils/config.ts
|
|
393
|
-
var
|
|
394
|
-
var import_zod2 = require("zod");
|
|
120
|
+
var DEFAULT_FARCASTER_ACCOUNT_ID = "default";
|
|
395
121
|
function getProcessEnv() {
|
|
396
122
|
if (typeof process === "undefined") {
|
|
397
123
|
return {};
|
|
@@ -405,41 +131,138 @@ function safeParseInt(value, defaultValue) {
|
|
|
405
131
|
const parsed = Number.parseInt(value, 10);
|
|
406
132
|
return Number.isNaN(parsed) ? defaultValue : Math.max(1, parsed);
|
|
407
133
|
}
|
|
408
|
-
function
|
|
409
|
-
const
|
|
134
|
+
function stringSetting(runtime, key) {
|
|
135
|
+
const value = runtime.getSetting(key);
|
|
136
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
137
|
+
}
|
|
138
|
+
function characterConfig(runtime) {
|
|
139
|
+
const settings = runtime.character?.settings;
|
|
140
|
+
const raw = settings?.farcaster;
|
|
141
|
+
return raw && typeof raw === "object" ? raw : {};
|
|
142
|
+
}
|
|
143
|
+
function parseAccountsJson(runtime) {
|
|
144
|
+
const raw = stringSetting(runtime, "FARCASTER_ACCOUNTS");
|
|
145
|
+
if (!raw)
|
|
146
|
+
return {};
|
|
147
|
+
try {
|
|
148
|
+
const parsed = JSON.parse(raw);
|
|
149
|
+
if (Array.isArray(parsed)) {
|
|
150
|
+
return Object.fromEntries(parsed.filter((item) => Boolean(item) && typeof item === "object").map((item) => [normalizeFarcasterAccountId(item.accountId ?? item.id), item]));
|
|
151
|
+
}
|
|
152
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
153
|
+
} catch {
|
|
154
|
+
return {};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function allAccountConfigs(runtime) {
|
|
158
|
+
return {
|
|
159
|
+
...characterConfig(runtime).accounts ?? {},
|
|
160
|
+
...parseAccountsJson(runtime)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function accountConfig(runtime, accountId) {
|
|
164
|
+
const accounts = allAccountConfigs(runtime);
|
|
165
|
+
return accounts[accountId] ?? accounts[normalizeFarcasterAccountId(accountId)] ?? {};
|
|
166
|
+
}
|
|
167
|
+
function rawField(record, keys) {
|
|
168
|
+
if (!record)
|
|
169
|
+
return;
|
|
170
|
+
for (const key of keys) {
|
|
171
|
+
const value = record[key];
|
|
172
|
+
if (typeof value === "string" && value.trim())
|
|
173
|
+
return value.trim();
|
|
174
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
175
|
+
return String(value);
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
function normalizeFarcasterAccountId(accountId) {
|
|
180
|
+
if (typeof accountId !== "string")
|
|
181
|
+
return DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
182
|
+
const trimmed = accountId.trim();
|
|
183
|
+
return trimmed || DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
184
|
+
}
|
|
185
|
+
function listFarcasterAccountIds(runtime) {
|
|
186
|
+
const ids = new Set;
|
|
187
|
+
const config = characterConfig(runtime);
|
|
188
|
+
if (stringSetting(runtime, "FARCASTER_FID") || config.FARCASTER_FID && config.FARCASTER_SIGNER_UUID) {
|
|
189
|
+
ids.add(DEFAULT_FARCASTER_ACCOUNT_ID);
|
|
190
|
+
}
|
|
191
|
+
for (const id of Object.keys(allAccountConfigs(runtime))) {
|
|
192
|
+
ids.add(normalizeFarcasterAccountId(id));
|
|
193
|
+
}
|
|
194
|
+
return Array.from(ids.size ? ids : new Set([DEFAULT_FARCASTER_ACCOUNT_ID])).sort((a, b) => a.localeCompare(b));
|
|
195
|
+
}
|
|
196
|
+
function resolveDefaultFarcasterAccountId(runtime) {
|
|
197
|
+
const requested = stringSetting(runtime, "FARCASTER_DEFAULT_ACCOUNT_ID") ?? stringSetting(runtime, "FARCASTER_ACCOUNT_ID");
|
|
198
|
+
if (requested)
|
|
199
|
+
return normalizeFarcasterAccountId(requested);
|
|
200
|
+
const ids = listFarcasterAccountIds(runtime);
|
|
201
|
+
return ids.includes(DEFAULT_FARCASTER_ACCOUNT_ID) ? DEFAULT_FARCASTER_ACCOUNT_ID : ids[0];
|
|
202
|
+
}
|
|
203
|
+
function readFarcasterAccountId(...sources) {
|
|
204
|
+
for (const source of sources) {
|
|
205
|
+
if (!source || typeof source !== "object")
|
|
206
|
+
continue;
|
|
207
|
+
const record = source;
|
|
208
|
+
const parameters = record.parameters && typeof record.parameters === "object" ? record.parameters : {};
|
|
209
|
+
const data = record.data && typeof record.data === "object" ? record.data : {};
|
|
210
|
+
const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
|
|
211
|
+
const farcaster = data.farcaster && typeof data.farcaster === "object" ? data.farcaster : {};
|
|
212
|
+
const value = record.accountId ?? parameters.accountId ?? data.accountId ?? farcaster.accountId ?? metadata.accountId;
|
|
213
|
+
if (typeof value === "string" && value.trim())
|
|
214
|
+
return normalizeFarcasterAccountId(value);
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
function getFarcasterFid(runtime, accountId) {
|
|
219
|
+
const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
|
|
220
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
221
|
+
const base = characterConfig(runtime);
|
|
222
|
+
const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
223
|
+
const fidStr = rawField(account, ["FARCASTER_FID", "fid"]) ?? rawField(base, ["FARCASTER_FID", "fid"]) ?? (allowEnv ? stringSetting(runtime, "FARCASTER_FID") : undefined);
|
|
410
224
|
if (!fidStr)
|
|
411
225
|
return null;
|
|
412
226
|
const fid = Number.parseInt(fidStr, 10);
|
|
413
227
|
return Number.isNaN(fid) ? null : fid;
|
|
414
228
|
}
|
|
415
|
-
function hasFarcasterEnabled(runtime) {
|
|
416
|
-
const
|
|
417
|
-
const
|
|
418
|
-
const
|
|
229
|
+
function hasFarcasterEnabled(runtime, accountId) {
|
|
230
|
+
const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
|
|
231
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
232
|
+
const base = characterConfig(runtime);
|
|
233
|
+
const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
234
|
+
const fid = getFarcasterFid(runtime, normalizedAccountId);
|
|
235
|
+
const signerUuid = rawField(account, ["FARCASTER_SIGNER_UUID", "signerUuid"]) ?? rawField(base, ["FARCASTER_SIGNER_UUID", "signerUuid"]) ?? (allowEnv ? stringSetting(runtime, "FARCASTER_SIGNER_UUID") : undefined);
|
|
236
|
+
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);
|
|
419
237
|
runtime.logger.debug(`[hasFarcasterEnabled] FID: ${fid ? "Found" : "Missing"}`);
|
|
420
238
|
runtime.logger.debug(`[hasFarcasterEnabled] Signer UUID: ${signerUuid ? "Found" : "Missing"}`);
|
|
421
239
|
runtime.logger.debug(`[hasFarcasterEnabled] API Key: ${apiKey ? "Found" : "Missing"}`);
|
|
422
240
|
return !!(fid && signerUuid && apiKey);
|
|
423
241
|
}
|
|
424
|
-
function validateFarcasterConfig(runtime) {
|
|
425
|
-
const
|
|
242
|
+
function validateFarcasterConfig(runtime, accountId) {
|
|
243
|
+
const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
|
|
244
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
245
|
+
const base = characterConfig(runtime);
|
|
246
|
+
const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
|
|
247
|
+
const field = (keys, envKey) => rawField(account, keys) ?? rawField(base, keys) ?? (allowEnv && envKey ? stringSetting(runtime, envKey) : undefined);
|
|
248
|
+
const fid = getFarcasterFid(runtime, normalizedAccountId);
|
|
426
249
|
try {
|
|
427
250
|
const farcasterConfig = {
|
|
428
|
-
FARCASTER_DRY_RUN:
|
|
251
|
+
FARCASTER_DRY_RUN: field(["FARCASTER_DRY_RUN", "dryRun"], "FARCASTER_DRY_RUN") || import_core.parseBooleanFromText(env.FARCASTER_DRY_RUN || "false"),
|
|
429
252
|
FARCASTER_FID: fid ?? undefined,
|
|
430
|
-
MAX_CAST_LENGTH: safeParseInt(
|
|
431
|
-
FARCASTER_POLL_INTERVAL: safeParseInt(
|
|
432
|
-
ENABLE_CAST:
|
|
433
|
-
CAST_INTERVAL_MIN: safeParseInt(
|
|
434
|
-
CAST_INTERVAL_MAX: safeParseInt(
|
|
435
|
-
ENABLE_ACTION_PROCESSING:
|
|
436
|
-
ACTION_INTERVAL: safeParseInt(
|
|
437
|
-
CAST_IMMEDIATELY:
|
|
438
|
-
MAX_ACTIONS_PROCESSING: safeParseInt(
|
|
439
|
-
FARCASTER_SIGNER_UUID:
|
|
440
|
-
FARCASTER_NEYNAR_API_KEY:
|
|
441
|
-
FARCASTER_HUB_URL:
|
|
442
|
-
FARCASTER_MODE:
|
|
253
|
+
MAX_CAST_LENGTH: safeParseInt(field(["MAX_CAST_LENGTH", "maxCastLength"], "MAX_CAST_LENGTH"), DEFAULT_MAX_CAST_LENGTH),
|
|
254
|
+
FARCASTER_POLL_INTERVAL: safeParseInt(field(["FARCASTER_POLL_INTERVAL", "pollInterval"], "FARCASTER_POLL_INTERVAL"), DEFAULT_POLL_INTERVAL),
|
|
255
|
+
ENABLE_CAST: field(["ENABLE_CAST", "enableCast"], "ENABLE_CAST") || import_core.parseBooleanFromText(env.ENABLE_CAST || "true"),
|
|
256
|
+
CAST_INTERVAL_MIN: safeParseInt(field(["CAST_INTERVAL_MIN", "castIntervalMin"], "CAST_INTERVAL_MIN"), DEFAULT_CAST_INTERVAL_MIN),
|
|
257
|
+
CAST_INTERVAL_MAX: safeParseInt(field(["CAST_INTERVAL_MAX", "castIntervalMax"], "CAST_INTERVAL_MAX"), DEFAULT_CAST_INTERVAL_MAX),
|
|
258
|
+
ENABLE_ACTION_PROCESSING: field(["ENABLE_ACTION_PROCESSING", "enableActionProcessing"], "ENABLE_ACTION_PROCESSING") || import_core.parseBooleanFromText(env.ENABLE_ACTION_PROCESSING || "false"),
|
|
259
|
+
ACTION_INTERVAL: safeParseInt(field(["ACTION_INTERVAL", "actionInterval"], "ACTION_INTERVAL"), 5),
|
|
260
|
+
CAST_IMMEDIATELY: field(["CAST_IMMEDIATELY", "castImmediately"], "CAST_IMMEDIATELY") || import_core.parseBooleanFromText(env.CAST_IMMEDIATELY || "false"),
|
|
261
|
+
MAX_ACTIONS_PROCESSING: safeParseInt(field(["MAX_ACTIONS_PROCESSING", "maxActionsProcessing"], "MAX_ACTIONS_PROCESSING"), 1),
|
|
262
|
+
FARCASTER_SIGNER_UUID: field(["FARCASTER_SIGNER_UUID", "signerUuid"], "FARCASTER_SIGNER_UUID"),
|
|
263
|
+
FARCASTER_NEYNAR_API_KEY: field(["FARCASTER_NEYNAR_API_KEY", "neynarApiKey", "apiKey"], "FARCASTER_NEYNAR_API_KEY"),
|
|
264
|
+
FARCASTER_HUB_URL: field(["FARCASTER_HUB_URL", "hubUrl"], "FARCASTER_HUB_URL") || "hub.pinata.cloud",
|
|
265
|
+
FARCASTER_MODE: field(["FARCASTER_MODE", "mode"], "FARCASTER_MODE") || "polling"
|
|
443
266
|
};
|
|
444
267
|
runtime.logger.debug(`[validateFarcasterConfig] Resolved FID: ${farcasterConfig.FARCASTER_FID}`);
|
|
445
268
|
runtime.logger.debug(`[validateFarcasterConfig] Resolved Signer UUID: ${farcasterConfig.FARCASTER_SIGNER_UUID ? "Found" : "Missing"}`);
|
|
@@ -459,9 +282,9 @@ function validateFarcasterConfig(runtime) {
|
|
|
459
282
|
if (isDryRun) {
|
|
460
283
|
runtime.logger.info("Farcaster client initialized in dry run mode - no actual casts should be posted");
|
|
461
284
|
}
|
|
462
|
-
return config;
|
|
285
|
+
return { ...config, accountId: normalizedAccountId };
|
|
463
286
|
} catch (error) {
|
|
464
|
-
if (error instanceof
|
|
287
|
+
if (error instanceof import_zod.z.ZodError) {
|
|
465
288
|
const errorMessages = error.issues.map((err) => `${err.path.join(".")}: ${err.message}`).join(`
|
|
466
289
|
`);
|
|
467
290
|
throw new Error(`Farcaster configuration validation failed:
|
|
@@ -471,32 +294,143 @@ ${errorMessages}`);
|
|
|
471
294
|
}
|
|
472
295
|
}
|
|
473
296
|
|
|
297
|
+
// connector-account-provider.ts
|
|
298
|
+
var FARCASTER_PROVIDER_ID = "farcaster";
|
|
299
|
+
function toConnectorAccount(runtime, accountId) {
|
|
300
|
+
let connected = false;
|
|
301
|
+
let fid;
|
|
302
|
+
let hubUrl = "";
|
|
303
|
+
try {
|
|
304
|
+
const config = validateFarcasterConfig(runtime, accountId);
|
|
305
|
+
fid = config.FARCASTER_FID;
|
|
306
|
+
hubUrl = config.FARCASTER_HUB_URL ?? "";
|
|
307
|
+
connected = Boolean(fid && config.FARCASTER_SIGNER_UUID && config.FARCASTER_NEYNAR_API_KEY);
|
|
308
|
+
} catch {
|
|
309
|
+
connected = false;
|
|
310
|
+
}
|
|
311
|
+
const now = Date.now();
|
|
312
|
+
return {
|
|
313
|
+
id: accountId,
|
|
314
|
+
provider: FARCASTER_PROVIDER_ID,
|
|
315
|
+
label: fid ? `FID ${fid}` : accountId,
|
|
316
|
+
role: "OWNER",
|
|
317
|
+
purpose: ["posting", "reading"],
|
|
318
|
+
accessGate: "open",
|
|
319
|
+
status: connected ? "connected" : "disabled",
|
|
320
|
+
externalId: fid ? String(fid) : undefined,
|
|
321
|
+
displayHandle: fid ? String(fid) : undefined,
|
|
322
|
+
createdAt: now,
|
|
323
|
+
updatedAt: now,
|
|
324
|
+
metadata: {
|
|
325
|
+
hubUrl,
|
|
326
|
+
fid
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function createFarcasterConnectorAccountProvider(runtime) {
|
|
331
|
+
return {
|
|
332
|
+
provider: FARCASTER_PROVIDER_ID,
|
|
333
|
+
label: "Farcaster",
|
|
334
|
+
listAccounts: async (_manager) => {
|
|
335
|
+
const ids = listFarcasterAccountIds(runtime);
|
|
336
|
+
return ids.map((id) => toConnectorAccount(runtime, id));
|
|
337
|
+
},
|
|
338
|
+
createAccount: async (input, _manager) => {
|
|
339
|
+
return {
|
|
340
|
+
...input,
|
|
341
|
+
provider: FARCASTER_PROVIDER_ID,
|
|
342
|
+
role: input.role ?? "OWNER",
|
|
343
|
+
purpose: input.purpose ?? ["posting", "reading"],
|
|
344
|
+
accessGate: input.accessGate ?? "open",
|
|
345
|
+
status: input.status ?? "pending"
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
patchAccount: async (_accountId, patch, _manager) => {
|
|
349
|
+
return { ...patch, provider: FARCASTER_PROVIDER_ID };
|
|
350
|
+
},
|
|
351
|
+
deleteAccount: async (_accountId, _manager) => {}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// generated/specs/specs.ts
|
|
356
|
+
var coreActionsSpec = {
|
|
357
|
+
version: "1.0.0",
|
|
358
|
+
actions: []
|
|
359
|
+
};
|
|
360
|
+
var allActionsSpec = {
|
|
361
|
+
version: "1.0.0",
|
|
362
|
+
actions: []
|
|
363
|
+
};
|
|
364
|
+
var coreProvidersSpec = {
|
|
365
|
+
version: "1.0.0",
|
|
366
|
+
providers: [
|
|
367
|
+
{
|
|
368
|
+
name: "farcasterProfile",
|
|
369
|
+
description: "Provides information about the agent",
|
|
370
|
+
dynamic: true
|
|
371
|
+
}
|
|
372
|
+
]
|
|
373
|
+
};
|
|
374
|
+
var allProvidersSpec = {
|
|
375
|
+
version: "1.0.0",
|
|
376
|
+
providers: [
|
|
377
|
+
{
|
|
378
|
+
name: "farcasterProfile",
|
|
379
|
+
description: "Provides information about the agent",
|
|
380
|
+
dynamic: true
|
|
381
|
+
}
|
|
382
|
+
]
|
|
383
|
+
};
|
|
384
|
+
var coreActionDocs = coreActionsSpec.actions;
|
|
385
|
+
var allActionDocs = allActionsSpec.actions;
|
|
386
|
+
var coreProviderDocs = coreProvidersSpec.providers;
|
|
387
|
+
var allProviderDocs = allProvidersSpec.providers;
|
|
388
|
+
|
|
389
|
+
// generated/specs/spec-helpers.ts
|
|
390
|
+
var coreActionMap = new Map(coreActionDocs.map((doc) => [doc.name, doc]));
|
|
391
|
+
var allActionMap = new Map(allActionDocs.map((doc) => [doc.name, doc]));
|
|
392
|
+
var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
|
|
393
|
+
var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
|
|
394
|
+
function getProviderSpec(name) {
|
|
395
|
+
return coreProviderMap.get(name) ?? allProviderMap.get(name);
|
|
396
|
+
}
|
|
397
|
+
function requireProviderSpec(name) {
|
|
398
|
+
const spec = getProviderSpec(name);
|
|
399
|
+
if (!spec) {
|
|
400
|
+
throw new Error(`Provider spec not found: ${name}`);
|
|
401
|
+
}
|
|
402
|
+
return spec;
|
|
403
|
+
}
|
|
404
|
+
|
|
474
405
|
// providers/profileProvider.ts
|
|
475
|
-
var
|
|
406
|
+
var spec = requireProviderSpec("farcasterProfile");
|
|
407
|
+
var MAX_PROFILE_FIELD_LENGTH = 280;
|
|
408
|
+
function truncateProfileField(value) {
|
|
409
|
+
return value ? value.slice(0, MAX_PROFILE_FIELD_LENGTH) : value;
|
|
410
|
+
}
|
|
476
411
|
var farcasterProfileProvider = {
|
|
477
|
-
name:
|
|
412
|
+
name: spec.name,
|
|
478
413
|
description: "Provides information about the agent's Farcaster profile",
|
|
414
|
+
descriptionCompressed: "provide information agent Farcaster profile",
|
|
479
415
|
dynamic: true,
|
|
480
|
-
|
|
416
|
+
contexts: ["social_posting", "messaging", "connectors"],
|
|
417
|
+
contextGate: { anyOf: ["social_posting", "messaging", "connectors"] },
|
|
418
|
+
cacheStable: false,
|
|
419
|
+
cacheScope: "turn",
|
|
420
|
+
get: async (runtime, message, state) => {
|
|
481
421
|
try {
|
|
482
422
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
483
|
-
const
|
|
484
|
-
|
|
423
|
+
const accountId = readFarcasterAccountId(message, state);
|
|
424
|
+
const manager = service?.getManagerForAccount?.(accountId, runtime.agentId) ?? service?.getManagerForAccount?.(undefined, runtime.agentId);
|
|
425
|
+
if (!manager) {
|
|
485
426
|
runtime.logger.debug("[FarcasterProfileProvider] No managers available");
|
|
486
427
|
return {
|
|
487
428
|
text: "Farcaster profile not available.",
|
|
488
429
|
data: { available: false }
|
|
489
430
|
};
|
|
490
431
|
}
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
runtime.logger.debug("[FarcasterProfileProvider] No manager for this agent");
|
|
494
|
-
return {
|
|
495
|
-
text: "Farcaster profile not available for this agent.",
|
|
496
|
-
data: { available: false }
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
const fid = getFarcasterFid(runtime);
|
|
432
|
+
const selectedAccountId = manager.config.accountId;
|
|
433
|
+
const fid = getFarcasterFid(runtime, selectedAccountId);
|
|
500
434
|
if (!fid) {
|
|
501
435
|
runtime.logger.warn("[FarcasterProfileProvider] Invalid or missing FARCASTER_FID");
|
|
502
436
|
return {
|
|
@@ -506,18 +440,21 @@ var farcasterProfileProvider = {
|
|
|
506
440
|
}
|
|
507
441
|
try {
|
|
508
442
|
const profile = await manager.client.getProfile(fid);
|
|
443
|
+
const username = truncateProfileField(profile.username) ?? "";
|
|
444
|
+
const name = truncateProfileField(profile.name);
|
|
509
445
|
return {
|
|
510
|
-
text: `Your Farcaster profile: @${
|
|
446
|
+
text: `Your Farcaster profile: @${username} (FID: ${profile.fid}). ${name ? `Display name: ${name}` : ""}`,
|
|
511
447
|
data: {
|
|
512
448
|
available: true,
|
|
513
449
|
fid: profile.fid,
|
|
514
|
-
username
|
|
515
|
-
name
|
|
516
|
-
pfp: profile.pfp
|
|
450
|
+
username,
|
|
451
|
+
name,
|
|
452
|
+
pfp: profile.pfp,
|
|
453
|
+
accountId: selectedAccountId
|
|
517
454
|
},
|
|
518
455
|
values: {
|
|
519
456
|
fid: profile.fid,
|
|
520
|
-
username
|
|
457
|
+
username
|
|
521
458
|
}
|
|
522
459
|
};
|
|
523
460
|
} catch (error) {
|
|
@@ -537,165 +474,38 @@ var farcasterProfileProvider = {
|
|
|
537
474
|
}
|
|
538
475
|
};
|
|
539
476
|
|
|
540
|
-
// providers/threadProvider.ts
|
|
541
|
-
var formatTimestamp = (timestamp) => {
|
|
542
|
-
return new Date(timestamp).toLocaleString("en-US", {
|
|
543
|
-
hour: "2-digit",
|
|
544
|
-
minute: "2-digit",
|
|
545
|
-
month: "short",
|
|
546
|
-
day: "numeric"
|
|
547
|
-
});
|
|
548
|
-
};
|
|
549
|
-
var spec4 = requireProviderSpec("farcasterThread");
|
|
550
|
-
var farcasterThreadProvider = {
|
|
551
|
-
name: spec4.name,
|
|
552
|
-
description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
|
|
553
|
-
dynamic: true,
|
|
554
|
-
get: async (runtime, message, _state) => {
|
|
555
|
-
const contentForSource = message.content;
|
|
556
|
-
const source = typeof contentForSource.source === "string" ? contentForSource.source : undefined;
|
|
557
|
-
if (source !== FARCASTER_SOURCE) {
|
|
558
|
-
return {
|
|
559
|
-
text: "",
|
|
560
|
-
data: {
|
|
561
|
-
available: false,
|
|
562
|
-
reason: `Not a Farcaster message (source=${source ?? "unknown"})`
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
567
|
-
const messageService = service?.getMessageService(runtime.agentId);
|
|
568
|
-
if (!messageService) {
|
|
569
|
-
return {
|
|
570
|
-
text: "Farcaster message service not available.",
|
|
571
|
-
data: { available: false, error: "service_unavailable" }
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
const content = message.content;
|
|
575
|
-
const messageMetadata = message.metadata ?? {};
|
|
576
|
-
const contentMetadata = typeof content.metadata === "object" && content.metadata !== null && !Array.isArray(content.metadata) ? content.metadata : undefined;
|
|
577
|
-
const castHash = content.hash || content.castHash || contentMetadata?.castHash || messageMetadata.castHash || contentMetadata?.parentHash || messageMetadata.parentHash;
|
|
578
|
-
if (!castHash || typeof castHash !== "string") {
|
|
579
|
-
return {
|
|
580
|
-
text: "Unable to resolve Farcaster cast hash for this message.",
|
|
581
|
-
data: { available: false, error: "missing_cast_hash" }
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
const threadMessages = await messageService.getThread({
|
|
585
|
-
agentId: runtime.agentId,
|
|
586
|
-
castHash
|
|
587
|
-
});
|
|
588
|
-
if (!threadMessages || threadMessages.length === 0) {
|
|
589
|
-
runtime.logger.debug({ castHash }, "[FarcasterThreadProvider] No thread messages retrieved for cast.");
|
|
590
|
-
return {
|
|
591
|
-
text: "No Farcaster thread context available.",
|
|
592
|
-
data: { available: false, castHash, count: 0 }
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
const formattedThread = threadMessages.map((msg, index) => {
|
|
596
|
-
const time = formatTimestamp(msg.timestamp);
|
|
597
|
-
const username = msg.username || msg.userId || "unknown";
|
|
598
|
-
const marker = index === threadMessages.length - 1 ? "→" : "•";
|
|
599
|
-
const text = msg.text && msg.text.trim().length > 0 ? msg.text : "<no text>";
|
|
600
|
-
return `${marker} [${time}] @${username}: ${text}`;
|
|
601
|
-
}).join(`
|
|
602
|
-
`);
|
|
603
|
-
return {
|
|
604
|
-
text: `# Farcaster Thread Context
|
|
605
|
-
${formattedThread}`,
|
|
606
|
-
data: {
|
|
607
|
-
available: true,
|
|
608
|
-
castHash,
|
|
609
|
-
count: threadMessages.length
|
|
610
|
-
},
|
|
611
|
-
values: {
|
|
612
|
-
farcasterThread: formattedThread,
|
|
613
|
-
farcasterCastHash: castHash,
|
|
614
|
-
farcasterCurrentCastText: threadMessages[threadMessages.length - 1]?.text ?? "",
|
|
615
|
-
farcasterParentCastText: threadMessages.length > 1 ? threadMessages[threadMessages.length - 2]?.text ?? "" : ""
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
|
|
621
|
-
// providers/timelineProvider.ts
|
|
622
|
-
function getTimeAgo(date) {
|
|
623
|
-
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
624
|
-
if (seconds < 60)
|
|
625
|
-
return "just now";
|
|
626
|
-
if (seconds < 3600)
|
|
627
|
-
return `${Math.floor(seconds / 60)}m ago`;
|
|
628
|
-
if (seconds < 86400)
|
|
629
|
-
return `${Math.floor(seconds / 3600)}h ago`;
|
|
630
|
-
return `${Math.floor(seconds / 86400)}d ago`;
|
|
631
|
-
}
|
|
632
|
-
var spec5 = requireProviderSpec("farcasterTimeline");
|
|
633
|
-
var farcasterTimelineProvider = {
|
|
634
|
-
name: spec5.name,
|
|
635
|
-
description: "Provides recent casts from the agent's Farcaster timeline",
|
|
636
|
-
dynamic: true,
|
|
637
|
-
get: async (runtime, _message, _state) => {
|
|
638
|
-
try {
|
|
639
|
-
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
640
|
-
const castService = service?.getCastService(runtime.agentId);
|
|
641
|
-
if (!castService) {
|
|
642
|
-
return {
|
|
643
|
-
text: "Farcaster timeline not available.",
|
|
644
|
-
data: { available: false }
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
const casts = await castService.getCasts({
|
|
648
|
-
agentId: runtime.agentId,
|
|
649
|
-
limit: 5
|
|
650
|
-
});
|
|
651
|
-
if (!casts || casts.length === 0) {
|
|
652
|
-
return {
|
|
653
|
-
text: "No recent casts in your timeline.",
|
|
654
|
-
data: {
|
|
655
|
-
available: true,
|
|
656
|
-
casts: [],
|
|
657
|
-
count: 0
|
|
658
|
-
}
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
const formattedCasts = casts.map((cast, index) => {
|
|
662
|
-
const timeAgo = getTimeAgo(new Date(cast.timestamp));
|
|
663
|
-
return `${index + 1}. @${cast.username} (${timeAgo}): ${cast.text}`;
|
|
664
|
-
}).join(`
|
|
665
|
-
`);
|
|
666
|
-
return {
|
|
667
|
-
text: `Recent casts from your timeline:
|
|
668
|
-
${formattedCasts}`,
|
|
669
|
-
data: {
|
|
670
|
-
available: true,
|
|
671
|
-
castCount: casts.length
|
|
672
|
-
},
|
|
673
|
-
values: {
|
|
674
|
-
latestCastHash: String(casts[0]?.metadata?.castHash || ""),
|
|
675
|
-
latestCastText: casts[0]?.text || ""
|
|
676
|
-
}
|
|
677
|
-
};
|
|
678
|
-
} catch (error) {
|
|
679
|
-
runtime.logger.error("[FarcasterTimelineProvider] Error:", typeof error === "string" ? error : error.message);
|
|
680
|
-
return {
|
|
681
|
-
text: "Unable to fetch Farcaster timeline.",
|
|
682
|
-
data: {
|
|
683
|
-
available: false,
|
|
684
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
685
|
-
}
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
};
|
|
690
|
-
|
|
691
477
|
// providers/index.ts
|
|
692
|
-
var farcasterProviders = [
|
|
693
|
-
farcasterProfileProvider,
|
|
694
|
-
farcasterTimelineProvider,
|
|
695
|
-
farcasterThreadProvider
|
|
696
|
-
];
|
|
478
|
+
var farcasterProviders = [farcasterProfileProvider];
|
|
697
479
|
|
|
698
480
|
// routes/webhook.ts
|
|
481
|
+
function isRecord(value) {
|
|
482
|
+
return typeof value === "object" && value !== null;
|
|
483
|
+
}
|
|
484
|
+
function isNeynarWebhookData(value) {
|
|
485
|
+
if (!isRecord(value) || typeof value.type !== "string")
|
|
486
|
+
return false;
|
|
487
|
+
if (value.data === undefined)
|
|
488
|
+
return true;
|
|
489
|
+
if (!isRecord(value.data))
|
|
490
|
+
return false;
|
|
491
|
+
if (typeof value.data.hash !== "string")
|
|
492
|
+
return false;
|
|
493
|
+
if (!isRecord(value.data.author) || typeof value.data.author.fid !== "number")
|
|
494
|
+
return false;
|
|
495
|
+
if (value.data.text !== undefined && typeof value.data.text !== "string") {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
if (value.data.mentioned_profiles !== undefined && !(Array.isArray(value.data.mentioned_profiles) && value.data.mentioned_profiles.every((profile) => isRecord(profile) && typeof profile.fid === "number"))) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
if (value.data.parent_hash !== undefined && typeof value.data.parent_hash !== "string") {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
if (value.data.parent_author !== undefined && !(isRecord(value.data.parent_author) && typeof value.data.parent_author.fid === "number")) {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
699
509
|
var farcasterWebhookRoutes = [
|
|
700
510
|
{
|
|
701
511
|
type: "POST",
|
|
@@ -703,16 +513,25 @@ var farcasterWebhookRoutes = [
|
|
|
703
513
|
path: "/webhook",
|
|
704
514
|
handler: async (req, res, runtime) => {
|
|
705
515
|
try {
|
|
516
|
+
if (!isNeynarWebhookData(req.body)) {
|
|
517
|
+
res.status(400).json({
|
|
518
|
+
success: false,
|
|
519
|
+
error: "Invalid webhook payload"
|
|
520
|
+
});
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
706
523
|
const webhookData = req.body;
|
|
707
524
|
const eventType = webhookData.type;
|
|
708
|
-
const farcasterService = runtime?.getService?.(
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
525
|
+
const farcasterService = runtime?.getService?.(FARCASTER_SERVICE_NAME);
|
|
526
|
+
const accountId = readFarcasterAccountId(webhookData);
|
|
527
|
+
if (farcasterService && accountId) {
|
|
528
|
+
const manager = farcasterService.getManagerForAccount?.(accountId, runtime.agentId);
|
|
529
|
+
if (manager?.interactions.mode === "webhook") {
|
|
530
|
+
await manager.interactions.processWebhookData(webhookData);
|
|
715
531
|
}
|
|
532
|
+
} else if (farcasterService) {
|
|
533
|
+
const managers = farcasterService.getManagersForAgent?.(runtime.agentId) ?? new Map;
|
|
534
|
+
await Promise.all(Array.from(managers.values()).filter((manager) => manager.interactions.mode === "webhook").map((manager) => manager.interactions.processWebhookData(webhookData)));
|
|
716
535
|
}
|
|
717
536
|
res.status(200).json({
|
|
718
537
|
success: true,
|
|
@@ -734,24 +553,24 @@ var farcasterWebhookRoutes = [
|
|
|
734
553
|
];
|
|
735
554
|
|
|
736
555
|
// services/FarcasterService.ts
|
|
737
|
-
var
|
|
556
|
+
var import_core9 = require("@elizaos/core");
|
|
738
557
|
|
|
739
558
|
// managers/AgentManager.ts
|
|
740
559
|
var import_nodejs_sdk2 = require("@neynar/nodejs-sdk");
|
|
741
560
|
|
|
742
561
|
// client/FarcasterClient.ts
|
|
743
|
-
var
|
|
562
|
+
var import_core3 = require("@elizaos/core");
|
|
744
563
|
var import_nodejs_sdk = require("@neynar/nodejs-sdk");
|
|
745
564
|
var import_lru_cache = require("lru-cache");
|
|
746
565
|
|
|
747
566
|
// utils/index.ts
|
|
748
|
-
var
|
|
567
|
+
var import_core2 = require("@elizaos/core");
|
|
749
568
|
var MAX_CAST_LENGTH = 1024;
|
|
750
569
|
function castId({ hash, agentId }) {
|
|
751
570
|
return `${hash}-${agentId}`;
|
|
752
571
|
}
|
|
753
572
|
function castUuid(props) {
|
|
754
|
-
return
|
|
573
|
+
return import_core2.stringToUuid(castId(props));
|
|
755
574
|
}
|
|
756
575
|
function splitPostContent(content, maxLength = MAX_CAST_LENGTH) {
|
|
757
576
|
const paragraphs = content.split(`
|
|
@@ -860,7 +679,8 @@ function createCastMemory({
|
|
|
860
679
|
roomId,
|
|
861
680
|
senderId,
|
|
862
681
|
runtime,
|
|
863
|
-
cast
|
|
682
|
+
cast,
|
|
683
|
+
accountId
|
|
864
684
|
}) {
|
|
865
685
|
const inReplyTo = cast.inReplyTo ? castUuid({
|
|
866
686
|
hash: cast.inReplyTo.hash,
|
|
@@ -876,17 +696,45 @@ function createCastMemory({
|
|
|
876
696
|
content: {
|
|
877
697
|
text: cast.text,
|
|
878
698
|
source: FARCASTER_SOURCE,
|
|
699
|
+
...accountId ? { accountId } : {},
|
|
879
700
|
url: "",
|
|
880
701
|
inReplyTo,
|
|
881
702
|
hash: cast.hash,
|
|
882
703
|
threadId: cast.threadId,
|
|
883
704
|
attachments: cast.media && cast.media.length > 0 ? cast.media : undefined
|
|
884
705
|
},
|
|
706
|
+
metadata: {
|
|
707
|
+
...accountId ? { accountId } : {}
|
|
708
|
+
},
|
|
885
709
|
roomId
|
|
886
710
|
};
|
|
887
711
|
}
|
|
888
712
|
|
|
889
713
|
// client/FarcasterClient.ts
|
|
714
|
+
async function logNeynarCall(op, context, fn) {
|
|
715
|
+
const startedAt = Date.now();
|
|
716
|
+
import_core3.elizaLogger.debug({ sdk: "neynar", op, ...context }, `[FarcasterClient] ${op} started`);
|
|
717
|
+
try {
|
|
718
|
+
const result = await fn();
|
|
719
|
+
import_core3.elizaLogger.info({
|
|
720
|
+
sdk: "neynar",
|
|
721
|
+
op,
|
|
722
|
+
...context,
|
|
723
|
+
durationMs: Date.now() - startedAt
|
|
724
|
+
}, `[FarcasterClient] ${op} ok`);
|
|
725
|
+
return result;
|
|
726
|
+
} catch (error) {
|
|
727
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
728
|
+
import_core3.elizaLogger.warn({
|
|
729
|
+
sdk: "neynar",
|
|
730
|
+
op,
|
|
731
|
+
...context,
|
|
732
|
+
durationMs: Date.now() - startedAt,
|
|
733
|
+
error: message
|
|
734
|
+
}, `[FarcasterClient] ${op} failed`);
|
|
735
|
+
throw error;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
890
738
|
var castCache = new import_lru_cache.LRUCache({
|
|
891
739
|
max: DEFAULT_CAST_CACHE_SIZE,
|
|
892
740
|
ttl: DEFAULT_CAST_CACHE_TTL
|
|
@@ -921,21 +769,20 @@ class FarcasterClient {
|
|
|
921
769
|
}
|
|
922
770
|
async publishCast(cast, parentCastId) {
|
|
923
771
|
try {
|
|
924
|
-
const result = await this.neynar.publishCast({
|
|
772
|
+
const result = await logNeynarCall("publishCast", { textLen: cast.length, parentHash: parentCastId?.hash ?? null }, async () => this.neynar.publishCast({
|
|
925
773
|
signerUuid: this.signerUuid,
|
|
926
774
|
text: cast,
|
|
927
775
|
parent: parentCastId?.hash
|
|
928
|
-
});
|
|
776
|
+
}));
|
|
929
777
|
if (result.success) {
|
|
930
778
|
return this.getCast(result.cast.hash);
|
|
931
779
|
}
|
|
932
780
|
throw new Error(`[Farcaster] Error publishing [${cast}] parentCastId: [${parentCastId}]`);
|
|
933
781
|
} catch (err) {
|
|
934
782
|
if (import_nodejs_sdk.isApiErrorResponse(err)) {
|
|
935
|
-
|
|
783
|
+
import_core3.elizaLogger.error(`Neynar error: ${JSON.stringify(err.response.data)}`);
|
|
936
784
|
throw err.response.data;
|
|
937
785
|
} else {
|
|
938
|
-
import_core5.elizaLogger.error(`Error: ${JSON.stringify(err)}`);
|
|
939
786
|
throw err;
|
|
940
787
|
}
|
|
941
788
|
}
|
|
@@ -945,19 +792,19 @@ class FarcasterClient {
|
|
|
945
792
|
if (cachedCast) {
|
|
946
793
|
return cachedCast;
|
|
947
794
|
}
|
|
948
|
-
const response = await this.neynar.lookupCastByHashOrUrl({
|
|
795
|
+
const response = await logNeynarCall("lookupCastByHashOrUrl", { castHash }, async () => this.neynar.lookupCastByHashOrUrl({
|
|
949
796
|
identifier: castHash,
|
|
950
797
|
type: "hash"
|
|
951
|
-
});
|
|
798
|
+
}));
|
|
952
799
|
castCache.set(castHash, response.cast);
|
|
953
800
|
return response.cast;
|
|
954
801
|
}
|
|
955
802
|
async getMentions(request) {
|
|
956
|
-
const neynarMentionsResponse = await this.neynar.fetchAllNotifications({
|
|
803
|
+
const neynarMentionsResponse = await logNeynarCall("fetchAllNotifications", { fid: request.fid, pageSize: request.pageSize }, async () => this.neynar.fetchAllNotifications({
|
|
957
804
|
fid: request.fid,
|
|
958
805
|
type: ["mentions", "replies"],
|
|
959
806
|
limit: request.pageSize
|
|
960
|
-
});
|
|
807
|
+
}));
|
|
961
808
|
const mentions = [];
|
|
962
809
|
for (const notification of neynarMentionsResponse.notifications) {
|
|
963
810
|
const neynarCast = notification.cast;
|
|
@@ -971,35 +818,29 @@ class FarcasterClient {
|
|
|
971
818
|
if (profileCache.has(fid)) {
|
|
972
819
|
return profileCache.get(fid);
|
|
973
820
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
import_core5.elizaLogger.error("Error fetching user by fid");
|
|
978
|
-
throw new Error("Profile fetch failed");
|
|
979
|
-
}
|
|
980
|
-
const neynarUserProfile = result.users[0];
|
|
981
|
-
const profile = {
|
|
982
|
-
fid,
|
|
983
|
-
name: "",
|
|
984
|
-
username: ""
|
|
985
|
-
};
|
|
986
|
-
profile.name = neynarUserProfile.display_name ?? "";
|
|
987
|
-
profile.username = neynarUserProfile.username;
|
|
988
|
-
profile.bio = neynarUserProfile.profile.bio.text;
|
|
989
|
-
profile.pfp = neynarUserProfile.pfp_url;
|
|
990
|
-
profileCache.set(fid, profile);
|
|
991
|
-
return profile;
|
|
992
|
-
} catch (error) {
|
|
993
|
-
import_core5.elizaLogger.error(`Error fetching profile: ${JSON.stringify(error)}`);
|
|
994
|
-
throw error;
|
|
821
|
+
const result = await logNeynarCall("fetchBulkUsers", { fid }, async () => this.neynar.fetchBulkUsers({ fids: [fid] }));
|
|
822
|
+
if (result.users.length < 1) {
|
|
823
|
+
throw new Error("Profile fetch failed");
|
|
995
824
|
}
|
|
825
|
+
const neynarUserProfile = result.users[0];
|
|
826
|
+
const profile = {
|
|
827
|
+
fid,
|
|
828
|
+
name: "",
|
|
829
|
+
username: ""
|
|
830
|
+
};
|
|
831
|
+
profile.name = neynarUserProfile.display_name ?? "";
|
|
832
|
+
profile.username = neynarUserProfile.username;
|
|
833
|
+
profile.bio = neynarUserProfile.profile.bio.text;
|
|
834
|
+
profile.pfp = neynarUserProfile.pfp_url;
|
|
835
|
+
profileCache.set(fid, profile);
|
|
836
|
+
return profile;
|
|
996
837
|
}
|
|
997
838
|
async getTimeline(request) {
|
|
998
839
|
const timeline = [];
|
|
999
|
-
const response = await this.neynar.fetchCastsForUser({
|
|
840
|
+
const response = await logNeynarCall("fetchCastsForUser", { fid: request.fid, pageSize: request.pageSize }, async () => this.neynar.fetchCastsForUser({
|
|
1000
841
|
fid: request.fid,
|
|
1001
842
|
limit: request.pageSize
|
|
1002
|
-
});
|
|
843
|
+
}));
|
|
1003
844
|
for (const cast of response.casts) {
|
|
1004
845
|
castCache.set(cast.hash, cast);
|
|
1005
846
|
timeline.push(neynarCastToCast(cast));
|
|
@@ -1016,42 +857,40 @@ class FarcasterClient {
|
|
|
1016
857
|
}
|
|
1017
858
|
async publishReaction(params) {
|
|
1018
859
|
try {
|
|
1019
|
-
const result = await this.neynar.publishReaction({
|
|
860
|
+
const result = await logNeynarCall("publishReaction", { reactionType: params.reactionType, target: params.target }, async () => this.neynar.publishReaction({
|
|
1020
861
|
signerUuid: this.signerUuid,
|
|
1021
862
|
reactionType: params.reactionType,
|
|
1022
863
|
target: params.target
|
|
1023
|
-
});
|
|
864
|
+
}));
|
|
1024
865
|
return { success: result.success ?? false };
|
|
1025
866
|
} catch (err) {
|
|
1026
867
|
if (import_nodejs_sdk.isApiErrorResponse(err)) {
|
|
1027
|
-
|
|
868
|
+
import_core3.elizaLogger.error(`Neynar error publishing reaction: ${JSON.stringify(err.response.data)}`);
|
|
1028
869
|
throw err.response.data;
|
|
1029
870
|
}
|
|
1030
|
-
import_core5.elizaLogger.error(`Error publishing reaction: ${JSON.stringify(err)}`);
|
|
1031
871
|
throw err;
|
|
1032
872
|
}
|
|
1033
873
|
}
|
|
1034
874
|
async deleteReaction(params) {
|
|
1035
875
|
try {
|
|
1036
|
-
const result = await this.neynar.deleteReaction({
|
|
876
|
+
const result = await logNeynarCall("deleteReaction", { reactionType: params.reactionType, target: params.target }, async () => this.neynar.deleteReaction({
|
|
1037
877
|
signerUuid: this.signerUuid,
|
|
1038
878
|
reactionType: params.reactionType,
|
|
1039
879
|
target: params.target
|
|
1040
|
-
});
|
|
880
|
+
}));
|
|
1041
881
|
return { success: result.success ?? false };
|
|
1042
882
|
} catch (err) {
|
|
1043
883
|
if (import_nodejs_sdk.isApiErrorResponse(err)) {
|
|
1044
|
-
|
|
884
|
+
import_core3.elizaLogger.error(`Neynar error deleting reaction: ${JSON.stringify(err.response.data)}`);
|
|
1045
885
|
throw err.response.data;
|
|
1046
886
|
}
|
|
1047
|
-
import_core5.elizaLogger.error(`Error deleting reaction: ${JSON.stringify(err)}`);
|
|
1048
887
|
throw err;
|
|
1049
888
|
}
|
|
1050
889
|
}
|
|
1051
890
|
}
|
|
1052
891
|
|
|
1053
892
|
// managers/CastManager.ts
|
|
1054
|
-
var
|
|
893
|
+
var import_core4 = require("@elizaos/core");
|
|
1055
894
|
|
|
1056
895
|
// utils/callbacks.ts
|
|
1057
896
|
function standardCastHandlerCallback({
|
|
@@ -1065,6 +904,7 @@ function standardCastHandlerCallback({
|
|
|
1065
904
|
}) {
|
|
1066
905
|
const callback = async (content) => {
|
|
1067
906
|
try {
|
|
907
|
+
const accountId = normalizeFarcasterAccountId(config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
|
|
1068
908
|
if (config.FARCASTER_DRY_RUN) {
|
|
1069
909
|
runtime.logger.info(`[Farcaster] Dry run: would have cast: ${content.text}`);
|
|
1070
910
|
return [];
|
|
@@ -1082,7 +922,8 @@ function standardCastHandlerCallback({
|
|
|
1082
922
|
roomId,
|
|
1083
923
|
senderId: runtime.agentId,
|
|
1084
924
|
runtime,
|
|
1085
|
-
cast: neynarCastToCast(cast)
|
|
925
|
+
cast: neynarCastToCast(cast),
|
|
926
|
+
accountId
|
|
1086
927
|
});
|
|
1087
928
|
if (i === 0) {
|
|
1088
929
|
memory.content.actions = content.actions;
|
|
@@ -1119,6 +960,9 @@ class FarcasterCastManager {
|
|
|
1119
960
|
this.config = opts.config;
|
|
1120
961
|
this.fid = this.config.FARCASTER_FID;
|
|
1121
962
|
}
|
|
963
|
+
getAccountId() {
|
|
964
|
+
return normalizeFarcasterAccountId(this.config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
|
|
965
|
+
}
|
|
1122
966
|
async start() {
|
|
1123
967
|
if (this.isRunning || !this.config.ENABLE_CAST) {
|
|
1124
968
|
return;
|
|
@@ -1161,43 +1005,56 @@ class FarcasterCastManager {
|
|
|
1161
1005
|
}
|
|
1162
1006
|
}
|
|
1163
1007
|
async generateNewCast() {
|
|
1164
|
-
this.runtime
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1008
|
+
await import_core4.withStandaloneTrajectory(this.runtime, {
|
|
1009
|
+
source: "plugin-farcaster:auto-cast",
|
|
1010
|
+
metadata: {
|
|
1011
|
+
platform: FARCASTER_SOURCE,
|
|
1012
|
+
kind: "public_post_generation",
|
|
1013
|
+
fid: this.fid,
|
|
1014
|
+
accountId: this.getAccountId()
|
|
1015
|
+
}
|
|
1016
|
+
}, async () => {
|
|
1017
|
+
import_core4.setTrajectoryPurpose("background");
|
|
1018
|
+
this.runtime.logger.info("Generating new cast");
|
|
1019
|
+
try {
|
|
1020
|
+
const worldId = import_core4.createUniqueUuid(this.runtime, this.fid.toString());
|
|
1021
|
+
const roomId = import_core4.createUniqueUuid(this.runtime, `${this.fid}-home`);
|
|
1022
|
+
const callback = standardCastHandlerCallback({
|
|
1023
|
+
client: this.client,
|
|
1024
|
+
runtime: this.runtime,
|
|
1025
|
+
config: this.config,
|
|
1026
|
+
roomId,
|
|
1027
|
+
onCompletion: async (casts, _memories) => {
|
|
1028
|
+
const lastCast = casts[casts.length - 1];
|
|
1029
|
+
await this.runtime.setCache(lastCastCacheKey(this.fid), {
|
|
1030
|
+
hash: lastCast.hash,
|
|
1031
|
+
timestamp: new Date(lastCast.timestamp).getTime()
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
await this.runtime.emitEvent(import_core4.EventType.POST_GENERATED, {
|
|
1036
|
+
runtime: this.runtime,
|
|
1037
|
+
callback,
|
|
1038
|
+
worldId,
|
|
1039
|
+
userId: this.runtime.agentId,
|
|
1040
|
+
roomId,
|
|
1041
|
+
source: FARCASTER_SOURCE,
|
|
1042
|
+
accountId: this.getAccountId()
|
|
1043
|
+
});
|
|
1044
|
+
await this.runtime.emitEvent("FARCASTER_POST_GENERATED" /* POST_GENERATED */, {
|
|
1045
|
+
runtime: this.runtime,
|
|
1046
|
+
source: FARCASTER_SOURCE,
|
|
1047
|
+
accountId: this.getAccountId()
|
|
1048
|
+
});
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error generating new cast");
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1196
1053
|
}
|
|
1197
1054
|
}
|
|
1198
1055
|
|
|
1199
1056
|
// managers/InteractionManager.ts
|
|
1200
|
-
var
|
|
1057
|
+
var import_core6 = require("@elizaos/core");
|
|
1201
1058
|
|
|
1202
1059
|
// utils/asyncqueue.ts
|
|
1203
1060
|
class AsyncQueue {
|
|
@@ -1261,12 +1118,12 @@ class AsyncQueue {
|
|
|
1261
1118
|
}
|
|
1262
1119
|
|
|
1263
1120
|
// managers/EmbedManager.ts
|
|
1264
|
-
var
|
|
1121
|
+
var import_core5 = require("@elizaos/core");
|
|
1265
1122
|
function isEmbedUrl(embed) {
|
|
1266
|
-
return "url" in embed
|
|
1123
|
+
return "url" in embed;
|
|
1267
1124
|
}
|
|
1268
1125
|
function isEmbedCast(embed) {
|
|
1269
|
-
return "cast" in embed
|
|
1126
|
+
return "cast" in embed;
|
|
1270
1127
|
}
|
|
1271
1128
|
function getMediaTypeFromUrl(url, contentType) {
|
|
1272
1129
|
const lowerUrl = url.toLowerCase();
|
|
@@ -1360,10 +1217,10 @@ class EmbedManager {
|
|
|
1360
1217
|
let description = "An image attachment";
|
|
1361
1218
|
let title = "Image";
|
|
1362
1219
|
try {
|
|
1363
|
-
const result = await this.runtime.useModel(
|
|
1220
|
+
const result = await import_core5.withStandaloneTrajectory(this.runtime, { source: "farcaster-embed" }, async () => this.runtime.useModel(import_core5.ModelType.IMAGE_DESCRIPTION, {
|
|
1364
1221
|
prompt: "Analyze this image and provide a concise title and description. Focus on the main subject and any notable details.",
|
|
1365
1222
|
imageUrl: url
|
|
1366
|
-
});
|
|
1223
|
+
}));
|
|
1367
1224
|
if (result && typeof result === "object") {
|
|
1368
1225
|
const typedResult = result;
|
|
1369
1226
|
description = typedResult.description || description;
|
|
@@ -1650,6 +1507,9 @@ class FarcasterInteractionManager {
|
|
|
1650
1507
|
});
|
|
1651
1508
|
this.runtime.logger.info(`Farcaster interaction mode: ${this.mode}`);
|
|
1652
1509
|
}
|
|
1510
|
+
getAccountId() {
|
|
1511
|
+
return normalizeFarcasterAccountId(this.config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
|
|
1512
|
+
}
|
|
1653
1513
|
async processMention(cast) {
|
|
1654
1514
|
const agentFid = this.config.FARCASTER_FID;
|
|
1655
1515
|
const agent = await this.client.getProfile(agentFid);
|
|
@@ -1744,9 +1604,9 @@ class FarcasterInteractionManager {
|
|
|
1744
1604
|
hash: cast.hash
|
|
1745
1605
|
});
|
|
1746
1606
|
const conversationId = cast.threadId ?? cast.inReplyTo?.hash ?? cast.hash;
|
|
1747
|
-
const entityId =
|
|
1748
|
-
const worldId =
|
|
1749
|
-
const roomId =
|
|
1607
|
+
const entityId = import_core6.createUniqueUuid(this.runtime, cast.authorFid.toString());
|
|
1608
|
+
const worldId = import_core6.createUniqueUuid(this.runtime, cast.authorFid.toString());
|
|
1609
|
+
const roomId = import_core6.createUniqueUuid(this.runtime, conversationId);
|
|
1750
1610
|
if (entityId !== this.runtime.agentId) {
|
|
1751
1611
|
await this.runtime.ensureConnection({
|
|
1752
1612
|
entityId,
|
|
@@ -1755,13 +1615,15 @@ class FarcasterInteractionManager {
|
|
|
1755
1615
|
userName: cast.profile.username,
|
|
1756
1616
|
name: cast.profile.name,
|
|
1757
1617
|
source: FARCASTER_SOURCE,
|
|
1758
|
-
type:
|
|
1618
|
+
type: import_core6.ChannelType.THREAD,
|
|
1759
1619
|
channelId: conversationId,
|
|
1760
|
-
messageServerId:
|
|
1620
|
+
messageServerId: import_core6.stringToUuid(cast.authorFid.toString()),
|
|
1761
1621
|
worldId,
|
|
1762
1622
|
metadata: {
|
|
1623
|
+
accountId: this.getAccountId(),
|
|
1763
1624
|
ownership: { ownerId: cast.authorFid.toString() },
|
|
1764
1625
|
farcaster: {
|
|
1626
|
+
accountId: this.getAccountId(),
|
|
1765
1627
|
username: cast.profile.username,
|
|
1766
1628
|
id: cast.authorFid.toString(),
|
|
1767
1629
|
name: cast.profile.name
|
|
@@ -1786,12 +1648,16 @@ class FarcasterInteractionManager {
|
|
|
1786
1648
|
hash: cast.inReplyTo.hash
|
|
1787
1649
|
}) : undefined,
|
|
1788
1650
|
source: FARCASTER_SOURCE,
|
|
1789
|
-
|
|
1651
|
+
accountId: this.getAccountId(),
|
|
1652
|
+
channelType: import_core6.ChannelType.THREAD,
|
|
1790
1653
|
attachments: cast.media && cast.media.length > 0 ? cast.media : undefined
|
|
1791
1654
|
},
|
|
1792
1655
|
entityId,
|
|
1793
1656
|
roomId,
|
|
1794
|
-
createdAt: cast.timestamp.getTime()
|
|
1657
|
+
createdAt: cast.timestamp.getTime(),
|
|
1658
|
+
metadata: {
|
|
1659
|
+
accountId: this.getAccountId()
|
|
1660
|
+
}
|
|
1795
1661
|
};
|
|
1796
1662
|
return memory;
|
|
1797
1663
|
});
|
|
@@ -1820,7 +1686,8 @@ class FarcasterInteractionManager {
|
|
|
1820
1686
|
runtime,
|
|
1821
1687
|
memory: newMemory,
|
|
1822
1688
|
cast: currentCast,
|
|
1823
|
-
source: FARCASTER_SOURCE
|
|
1689
|
+
source: FARCASTER_SOURCE,
|
|
1690
|
+
accountId: self.getAccountId()
|
|
1824
1691
|
});
|
|
1825
1692
|
}
|
|
1826
1693
|
thread.unshift(currentCast);
|
|
@@ -1874,6 +1741,7 @@ class FarcasterInteractionManager {
|
|
|
1874
1741
|
memory,
|
|
1875
1742
|
cast,
|
|
1876
1743
|
source: FARCASTER_SOURCE,
|
|
1744
|
+
accountId: this.getAccountId(),
|
|
1877
1745
|
callback
|
|
1878
1746
|
};
|
|
1879
1747
|
this.runtime.emitEvent("FARCASTER_MENTION_RECEIVED" /* MENTION_RECEIVED */, mentionPayload);
|
|
@@ -1894,8 +1762,10 @@ class FarcasterAgentManager {
|
|
|
1894
1762
|
client;
|
|
1895
1763
|
casts;
|
|
1896
1764
|
interactions;
|
|
1765
|
+
config;
|
|
1897
1766
|
constructor(runtime, config) {
|
|
1898
1767
|
this.runtime = runtime;
|
|
1768
|
+
this.config = config;
|
|
1899
1769
|
const signerUuid = config.FARCASTER_SIGNER_UUID;
|
|
1900
1770
|
const neynarConfig = new import_nodejs_sdk2.Configuration({
|
|
1901
1771
|
apiKey: config.FARCASTER_NEYNAR_API_KEY
|
|
@@ -1920,18 +1790,40 @@ class FarcasterAgentManager {
|
|
|
1920
1790
|
}
|
|
1921
1791
|
|
|
1922
1792
|
// services/CastService.ts
|
|
1923
|
-
var
|
|
1793
|
+
var import_core7 = require("@elizaos/core");
|
|
1794
|
+
function clampLimit(value, fallback, max) {
|
|
1795
|
+
if (!Number.isFinite(value)) {
|
|
1796
|
+
return fallback;
|
|
1797
|
+
}
|
|
1798
|
+
return Math.min(Math.max(1, Math.floor(value)), max);
|
|
1799
|
+
}
|
|
1800
|
+
function readContentString(content, keys) {
|
|
1801
|
+
const record = content;
|
|
1802
|
+
for (const key of keys) {
|
|
1803
|
+
const value = record[key];
|
|
1804
|
+
if (typeof value === "string" && value.trim()) {
|
|
1805
|
+
return value.trim();
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1924
1811
|
class FarcasterCastService {
|
|
1925
1812
|
client;
|
|
1926
1813
|
runtime;
|
|
1814
|
+
accountId;
|
|
1927
1815
|
static serviceType = "ICastService";
|
|
1928
|
-
constructor(client, runtime) {
|
|
1816
|
+
constructor(client, runtime, accountId = DEFAULT_FARCASTER_ACCOUNT_ID) {
|
|
1929
1817
|
this.client = client;
|
|
1930
1818
|
this.runtime = runtime;
|
|
1819
|
+
this.accountId = accountId;
|
|
1820
|
+
}
|
|
1821
|
+
getAccountId() {
|
|
1822
|
+
return normalizeFarcasterAccountId(this.accountId);
|
|
1931
1823
|
}
|
|
1932
1824
|
async getCasts(params) {
|
|
1933
1825
|
try {
|
|
1934
|
-
const fid = getFarcasterFid(this.runtime);
|
|
1826
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
1935
1827
|
if (!fid) {
|
|
1936
1828
|
this.runtime.logger.error("FARCASTER_FID is not configured");
|
|
1937
1829
|
return [];
|
|
@@ -1974,6 +1866,7 @@ class FarcasterCastService {
|
|
|
1974
1866
|
inReplyTo: params.replyTo?.hash,
|
|
1975
1867
|
media: [],
|
|
1976
1868
|
metadata: {
|
|
1869
|
+
accountId: this.getAccountId(),
|
|
1977
1870
|
castHash: cast.hash,
|
|
1978
1871
|
authorFid: cast.authorFid,
|
|
1979
1872
|
source: FARCASTER_SOURCE,
|
|
@@ -1987,6 +1880,58 @@ class FarcasterCastService {
|
|
|
1987
1880
|
throw error;
|
|
1988
1881
|
}
|
|
1989
1882
|
}
|
|
1883
|
+
async handleSendPost(runtime, content) {
|
|
1884
|
+
const requestedAccountId = normalizeFarcasterAccountId(readFarcasterAccountId(content) ?? this.getAccountId());
|
|
1885
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1886
|
+
throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
|
|
1887
|
+
}
|
|
1888
|
+
const text = typeof content.text === "string" ? content.text.trim() : "";
|
|
1889
|
+
if (!text) {
|
|
1890
|
+
throw new Error("Farcaster post connector requires non-empty text content.");
|
|
1891
|
+
}
|
|
1892
|
+
const parentHash = readContentString(content, ["parentHash", "replyTo", "replyToHash"]);
|
|
1893
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
1894
|
+
const cast = await this.createCast({
|
|
1895
|
+
agentId: runtime.agentId,
|
|
1896
|
+
roomId: import_core7.createUniqueUuid(runtime, `farcaster:feed:${fid ?? runtime.agentId}`),
|
|
1897
|
+
text,
|
|
1898
|
+
...parentHash && fid ? { replyTo: { hash: parentHash, fid } } : {}
|
|
1899
|
+
});
|
|
1900
|
+
return this.farcasterCastToMemory(runtime, cast);
|
|
1901
|
+
}
|
|
1902
|
+
async fetchFeed(context, params = {}) {
|
|
1903
|
+
const requestedAccountId = normalizeFarcasterAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
1904
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1905
|
+
throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
|
|
1906
|
+
}
|
|
1907
|
+
const casts = await this.getCasts({
|
|
1908
|
+
agentId: context.runtime.agentId,
|
|
1909
|
+
limit: clampLimit(params.limit, 25, 100),
|
|
1910
|
+
cursor: params.cursor
|
|
1911
|
+
});
|
|
1912
|
+
return casts.map((cast) => this.farcasterCastToMemory(context.runtime, cast));
|
|
1913
|
+
}
|
|
1914
|
+
async searchPosts(context, params) {
|
|
1915
|
+
const requestedAccountId = normalizeFarcasterAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
1916
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1917
|
+
throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
|
|
1918
|
+
}
|
|
1919
|
+
const query = params.query.trim().toLowerCase();
|
|
1920
|
+
if (!query) {
|
|
1921
|
+
return [];
|
|
1922
|
+
}
|
|
1923
|
+
const limit = clampLimit(params.limit, 25, 100);
|
|
1924
|
+
const casts = await this.getCasts({
|
|
1925
|
+
agentId: context.runtime.agentId,
|
|
1926
|
+
limit: 100,
|
|
1927
|
+
cursor: params.cursor
|
|
1928
|
+
});
|
|
1929
|
+
return casts.filter((cast) => {
|
|
1930
|
+
const text = cast.text.toLowerCase();
|
|
1931
|
+
const username = cast.username.toLowerCase();
|
|
1932
|
+
return text.includes(query) || username.includes(query);
|
|
1933
|
+
}).slice(0, limit).map((cast) => this.farcasterCastToMemory(context.runtime, cast));
|
|
1934
|
+
}
|
|
1990
1935
|
async deleteCast(params) {
|
|
1991
1936
|
this.runtime.logger.warn(`Cast deletion is not supported by the Farcaster API: ${JSON.stringify({ castHash: params.castHash })}`);
|
|
1992
1937
|
}
|
|
@@ -2052,7 +1997,7 @@ class FarcasterCastService {
|
|
|
2052
1997
|
}
|
|
2053
1998
|
async getMentions(params) {
|
|
2054
1999
|
try {
|
|
2055
|
-
const fid = getFarcasterFid(this.runtime);
|
|
2000
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
2056
2001
|
if (!fid) {
|
|
2057
2002
|
this.runtime.logger.error("FARCASTER_FID is not configured");
|
|
2058
2003
|
return [];
|
|
@@ -2073,7 +2018,7 @@ class FarcasterCastService {
|
|
|
2073
2018
|
async generateCastContent() {
|
|
2074
2019
|
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.`;
|
|
2075
2020
|
try {
|
|
2076
|
-
const response = await this.runtime.useModel(
|
|
2021
|
+
const response = await this.runtime.useModel(import_core7.ModelType.TEXT_SMALL, {
|
|
2077
2022
|
prompt,
|
|
2078
2023
|
maxTokens: 100
|
|
2079
2024
|
});
|
|
@@ -2086,7 +2031,7 @@ class FarcasterCastService {
|
|
|
2086
2031
|
async truncateCast(text) {
|
|
2087
2032
|
const prompt = `Shorten this text to under 320 characters while keeping the main message intact: "${text}"`;
|
|
2088
2033
|
try {
|
|
2089
|
-
const response = await this.runtime.useModel(
|
|
2034
|
+
const response = await this.runtime.useModel(import_core7.ModelType.TEXT_SMALL, {
|
|
2090
2035
|
prompt,
|
|
2091
2036
|
maxTokens: 100
|
|
2092
2037
|
});
|
|
@@ -2102,9 +2047,9 @@ class FarcasterCastService {
|
|
|
2102
2047
|
}
|
|
2103
2048
|
async storeCastInMemory(roomId, cast) {
|
|
2104
2049
|
try {
|
|
2105
|
-
const entityId =
|
|
2050
|
+
const entityId = import_core7.createUniqueUuid(this.runtime, cast.userId);
|
|
2106
2051
|
const memory = {
|
|
2107
|
-
id:
|
|
2052
|
+
id: import_core7.createUniqueUuid(this.runtime, cast.id),
|
|
2108
2053
|
agentId: this.runtime.agentId,
|
|
2109
2054
|
entityId,
|
|
2110
2055
|
content: {
|
|
@@ -2112,7 +2057,11 @@ class FarcasterCastService {
|
|
|
2112
2057
|
castHash: String(cast.metadata?.castHash || ""),
|
|
2113
2058
|
castId: cast.id,
|
|
2114
2059
|
author: cast.username,
|
|
2115
|
-
timestamp: cast.timestamp
|
|
2060
|
+
timestamp: cast.timestamp,
|
|
2061
|
+
accountId: this.getAccountId()
|
|
2062
|
+
},
|
|
2063
|
+
metadata: {
|
|
2064
|
+
accountId: this.getAccountId()
|
|
2116
2065
|
},
|
|
2117
2066
|
roomId,
|
|
2118
2067
|
createdAt: Date.now()
|
|
@@ -2122,11 +2071,58 @@ class FarcasterCastService {
|
|
|
2122
2071
|
this.runtime.logger.error(`Failed to store cast in memory: ${JSON.stringify({ error })}`);
|
|
2123
2072
|
}
|
|
2124
2073
|
}
|
|
2074
|
+
farcasterCastToMemory(runtime, cast) {
|
|
2075
|
+
const authorId = cast.userId || "unknown";
|
|
2076
|
+
const entityId = authorId === runtime.agentId ? runtime.agentId : import_core7.createUniqueUuid(runtime, `farcaster:user:${authorId}`);
|
|
2077
|
+
const roomId = cast.roomId || import_core7.createUniqueUuid(runtime, `farcaster:feed:${authorId}`);
|
|
2078
|
+
const castHash = typeof cast.metadata?.castHash === "string" ? cast.metadata.castHash : cast.id;
|
|
2079
|
+
return {
|
|
2080
|
+
id: import_core7.createUniqueUuid(runtime, `farcaster:cast:${castHash}`),
|
|
2081
|
+
agentId: runtime.agentId,
|
|
2082
|
+
entityId,
|
|
2083
|
+
roomId,
|
|
2084
|
+
createdAt: cast.timestamp || Date.now(),
|
|
2085
|
+
content: {
|
|
2086
|
+
text: cast.text,
|
|
2087
|
+
source: FARCASTER_SOURCE,
|
|
2088
|
+
channelType: import_core7.ChannelType.FEED,
|
|
2089
|
+
...cast.inReplyTo ? { inReplyTo: import_core7.createUniqueUuid(runtime, `farcaster:cast:${cast.inReplyTo}`) } : {}
|
|
2090
|
+
},
|
|
2091
|
+
metadata: {
|
|
2092
|
+
type: "message",
|
|
2093
|
+
source: FARCASTER_SOURCE,
|
|
2094
|
+
accountId: this.getAccountId(),
|
|
2095
|
+
provider: FARCASTER_SOURCE,
|
|
2096
|
+
timestamp: cast.timestamp,
|
|
2097
|
+
fromBot: entityId === runtime.agentId,
|
|
2098
|
+
messageIdFull: castHash,
|
|
2099
|
+
chatType: import_core7.ChannelType.FEED,
|
|
2100
|
+
sender: {
|
|
2101
|
+
id: authorId,
|
|
2102
|
+
username: cast.username
|
|
2103
|
+
},
|
|
2104
|
+
farcaster: {
|
|
2105
|
+
accountId: this.getAccountId(),
|
|
2106
|
+
castId: cast.id,
|
|
2107
|
+
castHash,
|
|
2108
|
+
authorFid: cast.metadata?.authorFid,
|
|
2109
|
+
username: cast.username,
|
|
2110
|
+
inReplyTo: cast.inReplyTo,
|
|
2111
|
+
metrics: {
|
|
2112
|
+
recasts: cast.metadata?.recasts,
|
|
2113
|
+
replies: cast.metadata?.replies,
|
|
2114
|
+
likes: cast.metadata?.likes
|
|
2115
|
+
},
|
|
2116
|
+
...cast.metadata ?? {}
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2125
2121
|
castToFarcasterCast(cast, agentId) {
|
|
2126
2122
|
return {
|
|
2127
2123
|
id: castUuid({ hash: cast.hash, agentId }),
|
|
2128
2124
|
agentId,
|
|
2129
|
-
roomId:
|
|
2125
|
+
roomId: import_core7.createUniqueUuid(this.runtime, cast.threadId || cast.hash),
|
|
2130
2126
|
userId: cast.profile.fid.toString(),
|
|
2131
2127
|
username: cast.profile.username,
|
|
2132
2128
|
text: cast.text,
|
|
@@ -2136,6 +2132,7 @@ class FarcasterCastService {
|
|
|
2136
2132
|
castHash: cast.hash,
|
|
2137
2133
|
authorFid: cast.authorFid,
|
|
2138
2134
|
source: FARCASTER_SOURCE,
|
|
2135
|
+
accountId: this.getAccountId(),
|
|
2139
2136
|
...cast.threadId ? { threadId: cast.threadId } : {},
|
|
2140
2137
|
...cast.stats ? {
|
|
2141
2138
|
recasts: cast.stats.recasts,
|
|
@@ -2148,19 +2145,24 @@ class FarcasterCastService {
|
|
|
2148
2145
|
}
|
|
2149
2146
|
|
|
2150
2147
|
// services/MessageService.ts
|
|
2151
|
-
var
|
|
2148
|
+
var import_core8 = require("@elizaos/core");
|
|
2152
2149
|
class FarcasterMessageService {
|
|
2153
2150
|
client;
|
|
2154
2151
|
runtime;
|
|
2155
|
-
|
|
2152
|
+
accountId;
|
|
2153
|
+
constructor(client, runtime, accountId = DEFAULT_FARCASTER_ACCOUNT_ID) {
|
|
2156
2154
|
this.client = client;
|
|
2157
2155
|
this.runtime = runtime;
|
|
2156
|
+
this.accountId = accountId;
|
|
2157
|
+
}
|
|
2158
|
+
getAccountId() {
|
|
2159
|
+
return normalizeFarcasterAccountId(this.accountId);
|
|
2158
2160
|
}
|
|
2159
2161
|
castToMessage(cast, agentId, extraMetadata) {
|
|
2160
2162
|
return {
|
|
2161
2163
|
id: castUuid({ hash: cast.hash, agentId }),
|
|
2162
2164
|
agentId,
|
|
2163
|
-
roomId:
|
|
2165
|
+
roomId: import_core8.createUniqueUuid(this.runtime, cast.threadId || cast.hash),
|
|
2164
2166
|
userId: cast.profile.fid.toString(),
|
|
2165
2167
|
username: cast.profile.username,
|
|
2166
2168
|
text: cast.text,
|
|
@@ -2169,6 +2171,7 @@ class FarcasterMessageService {
|
|
|
2169
2171
|
inReplyTo: cast.inReplyTo ? castUuid({ hash: cast.inReplyTo.hash, agentId }) : undefined,
|
|
2170
2172
|
metadata: {
|
|
2171
2173
|
source: FARCASTER_SOURCE,
|
|
2174
|
+
accountId: this.getAccountId(),
|
|
2172
2175
|
castHash: cast.hash,
|
|
2173
2176
|
threadId: cast.threadId,
|
|
2174
2177
|
authorFid: cast.authorFid,
|
|
@@ -2179,7 +2182,7 @@ class FarcasterMessageService {
|
|
|
2179
2182
|
async getMessages(options) {
|
|
2180
2183
|
try {
|
|
2181
2184
|
const { agentId, roomId, limit = 20 } = options;
|
|
2182
|
-
const fid = getFarcasterFid(this.runtime);
|
|
2185
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
2183
2186
|
if (!fid) {
|
|
2184
2187
|
this.runtime.logger.error("[Farcaster] FARCASTER_FID is not configured");
|
|
2185
2188
|
return [];
|
|
@@ -2203,11 +2206,15 @@ class FarcasterMessageService {
|
|
|
2203
2206
|
}
|
|
2204
2207
|
async sendMessage(options) {
|
|
2205
2208
|
try {
|
|
2209
|
+
const requestedAccountId = normalizeFarcasterAccountId(options.accountId ?? readFarcasterAccountId(options.metadata) ?? this.getAccountId());
|
|
2210
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
2211
|
+
throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
|
|
2212
|
+
}
|
|
2206
2213
|
const { text, type, roomId, replyToId, agentId } = options;
|
|
2207
2214
|
let inReplyTo;
|
|
2208
2215
|
if (replyToId && type === "REPLY" /* REPLY */) {
|
|
2209
2216
|
const parentHash = typeof options.metadata?.parentHash === "string" ? options.metadata.parentHash : replyToId;
|
|
2210
|
-
const fid = getFarcasterFid(this.runtime);
|
|
2217
|
+
const fid = getFarcasterFid(this.runtime, this.getAccountId());
|
|
2211
2218
|
if (!fid) {
|
|
2212
2219
|
throw new Error("FARCASTER_FID is not configured");
|
|
2213
2220
|
}
|
|
@@ -2227,15 +2234,16 @@ class FarcasterMessageService {
|
|
|
2227
2234
|
const message = this.castToMessage(cast, agentId, options.metadata);
|
|
2228
2235
|
message.roomId = roomId;
|
|
2229
2236
|
message.type = type;
|
|
2230
|
-
const
|
|
2237
|
+
const postGeneratedPayload = {
|
|
2231
2238
|
runtime: this.runtime,
|
|
2232
2239
|
source: FARCASTER_SOURCE,
|
|
2233
2240
|
castHash: cast.hash,
|
|
2234
2241
|
...cast.threadId ? { threadId: cast.threadId } : {},
|
|
2235
2242
|
messageId: message.id,
|
|
2236
|
-
roomId
|
|
2243
|
+
roomId,
|
|
2244
|
+
accountId: this.getAccountId()
|
|
2237
2245
|
};
|
|
2238
|
-
await this.runtime.emitEvent("
|
|
2246
|
+
await this.runtime.emitEvent("FARCASTER_POST_GENERATED" /* POST_GENERATED */, postGeneratedPayload);
|
|
2239
2247
|
return message;
|
|
2240
2248
|
} catch (error) {
|
|
2241
2249
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2285,11 +2293,9 @@ class FarcasterMessageService {
|
|
|
2285
2293
|
}
|
|
2286
2294
|
|
|
2287
2295
|
// services/FarcasterService.ts
|
|
2288
|
-
class FarcasterService extends
|
|
2296
|
+
class FarcasterService extends import_core9.Service {
|
|
2289
2297
|
static instance;
|
|
2290
|
-
|
|
2291
|
-
messageServices = new Map;
|
|
2292
|
-
castServices = new Map;
|
|
2298
|
+
agents = new Map;
|
|
2293
2299
|
static serviceType = FARCASTER_SERVICE_NAME;
|
|
2294
2300
|
description = "Farcaster integration service for sending and receiving casts";
|
|
2295
2301
|
capabilityDescription = "The agent is able to send and receive messages on farcaster";
|
|
@@ -2304,8 +2310,7 @@ class FarcasterService extends import_core11.Service {
|
|
|
2304
2310
|
}
|
|
2305
2311
|
static async start(runtime) {
|
|
2306
2312
|
const service = FarcasterService.getInstance();
|
|
2307
|
-
|
|
2308
|
-
if (manager) {
|
|
2313
|
+
if (service.agents.has(runtime.agentId)) {
|
|
2309
2314
|
runtime.logger.warn({ agentId: runtime.agentId }, "Farcaster service already started");
|
|
2310
2315
|
return service;
|
|
2311
2316
|
}
|
|
@@ -2313,80 +2318,213 @@ class FarcasterService extends import_core11.Service {
|
|
|
2313
2318
|
runtime.logger.debug({ agentId: runtime.agentId }, "Farcaster service not enabled");
|
|
2314
2319
|
return service;
|
|
2315
2320
|
}
|
|
2316
|
-
const
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
service.
|
|
2323
|
-
|
|
2324
|
-
|
|
2321
|
+
const accounts = {
|
|
2322
|
+
defaultAccountId: normalizeFarcasterAccountId(resolveDefaultFarcasterAccountId(runtime)),
|
|
2323
|
+
managers: new Map,
|
|
2324
|
+
messageServices: new Map,
|
|
2325
|
+
castServices: new Map
|
|
2326
|
+
};
|
|
2327
|
+
service.agents.set(runtime.agentId, accounts);
|
|
2328
|
+
for (const accountId of listFarcasterAccountIds(runtime)) {
|
|
2329
|
+
if (!hasFarcasterEnabled(runtime, accountId)) {
|
|
2330
|
+
continue;
|
|
2331
|
+
}
|
|
2332
|
+
const farcasterConfig = validateFarcasterConfig(runtime, accountId);
|
|
2333
|
+
const manager = new FarcasterAgentManager(runtime, farcasterConfig);
|
|
2334
|
+
accounts.managers.set(accountId, manager);
|
|
2335
|
+
accounts.messageServices.set(accountId, new FarcasterMessageService(manager.client, runtime, accountId));
|
|
2336
|
+
accounts.castServices.set(accountId, new FarcasterCastService(manager.client, runtime, accountId));
|
|
2337
|
+
await manager.start();
|
|
2338
|
+
runtime.logger.success({ agentId: runtime.agentId, accountId }, "Farcaster client started");
|
|
2339
|
+
}
|
|
2325
2340
|
return service;
|
|
2326
2341
|
}
|
|
2327
2342
|
static async stop(runtime) {
|
|
2328
2343
|
const service = FarcasterService.getInstance();
|
|
2329
|
-
const
|
|
2330
|
-
if (
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
service.
|
|
2344
|
+
const accounts = service.agents.get(runtime.agentId);
|
|
2345
|
+
if (accounts) {
|
|
2346
|
+
for (const manager of accounts.managers.values()) {
|
|
2347
|
+
await manager.stop();
|
|
2348
|
+
}
|
|
2349
|
+
service.agents.delete(runtime.agentId);
|
|
2335
2350
|
runtime.logger.info({ agentId: runtime.agentId }, "Farcaster client stopped");
|
|
2336
2351
|
} else {
|
|
2337
2352
|
runtime.logger.debug({ agentId: runtime.agentId }, "Farcaster service not running");
|
|
2338
2353
|
}
|
|
2339
2354
|
}
|
|
2355
|
+
static registerSendHandlers(runtime, serviceInstance) {
|
|
2356
|
+
const accounts = serviceInstance?.agents.get(runtime.agentId);
|
|
2357
|
+
if (!accounts || accounts.castServices.size === 0) {
|
|
2358
|
+
runtime.logger.warn({ src: "plugin:farcaster", agentId: runtime.agentId }, "Cannot register Farcaster post connector; cast service is not initialized");
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
const withPostConnector = runtime;
|
|
2362
|
+
if (typeof withPostConnector.registerPostConnector !== "function") {
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2365
|
+
for (const castService of accounts.castServices.values()) {
|
|
2366
|
+
const accountId = castService.getAccountId();
|
|
2367
|
+
withPostConnector.registerPostConnector({
|
|
2368
|
+
source: "farcaster",
|
|
2369
|
+
accountId,
|
|
2370
|
+
label: "Farcaster",
|
|
2371
|
+
description: "Farcaster public cast connector for publishing casts and reading or searching the authenticated account's recent feed.",
|
|
2372
|
+
capabilities: ["post", "fetch_feed", "search_posts"],
|
|
2373
|
+
contexts: ["social", "social_posting", "connectors"],
|
|
2374
|
+
metadata: {
|
|
2375
|
+
accountId,
|
|
2376
|
+
service: FARCASTER_SERVICE_NAME
|
|
2377
|
+
},
|
|
2378
|
+
postHandler: castService.handleSendPost.bind(castService),
|
|
2379
|
+
fetchFeed: castService.fetchFeed.bind(castService),
|
|
2380
|
+
searchPosts: castService.searchPosts.bind(castService),
|
|
2381
|
+
contentShaping: {
|
|
2382
|
+
systemPromptFragment: "For Farcaster casts, write a conversational public cast under 320 characters. If replying, keep enough context for a public thread.",
|
|
2383
|
+
constraints: {
|
|
2384
|
+
maxLength: 320,
|
|
2385
|
+
supportsMarkdown: false,
|
|
2386
|
+
channelType: import_core9.ChannelType.FEED
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
runtime.logger.info({ src: "plugin:farcaster", agentId: runtime.agentId }, "Registered Farcaster post connector");
|
|
2392
|
+
}
|
|
2340
2393
|
async stop() {
|
|
2341
|
-
for (const
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2394
|
+
for (const [agentId, accounts] of Array.from(this.agents.entries())) {
|
|
2395
|
+
const runtime = accounts.managers.values().next().value?.runtime;
|
|
2396
|
+
runtime?.logger.debug("Stopping Farcaster service");
|
|
2344
2397
|
try {
|
|
2345
|
-
|
|
2398
|
+
if (runtime) {
|
|
2399
|
+
await FarcasterService.stop(runtime);
|
|
2400
|
+
} else {
|
|
2401
|
+
this.agents.delete(agentId);
|
|
2402
|
+
}
|
|
2346
2403
|
} catch (error) {
|
|
2347
|
-
|
|
2404
|
+
runtime?.logger.error({ agentId, error }, "Error stopping Farcaster service");
|
|
2348
2405
|
}
|
|
2349
2406
|
}
|
|
2350
2407
|
}
|
|
2351
|
-
getMessageService(agentId) {
|
|
2352
|
-
return this.
|
|
2408
|
+
getMessageService(agentId, accountId) {
|
|
2409
|
+
return this.getMessageServiceForAccount(accountId, agentId);
|
|
2410
|
+
}
|
|
2411
|
+
getCastService(agentId, accountId) {
|
|
2412
|
+
return this.getCastServiceForAccount(accountId, agentId);
|
|
2413
|
+
}
|
|
2414
|
+
getMessageServiceForAccount(accountId, agentId) {
|
|
2415
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2416
|
+
if (!resolvedAgentId)
|
|
2417
|
+
return;
|
|
2418
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
2419
|
+
if (!accounts)
|
|
2420
|
+
return;
|
|
2421
|
+
const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
|
|
2422
|
+
return accounts.messageServices.get(id);
|
|
2423
|
+
}
|
|
2424
|
+
getCastServiceForAccount(accountId, agentId) {
|
|
2425
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2426
|
+
if (!resolvedAgentId)
|
|
2427
|
+
return;
|
|
2428
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
2429
|
+
if (!accounts)
|
|
2430
|
+
return;
|
|
2431
|
+
const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
|
|
2432
|
+
return accounts.castServices.get(id);
|
|
2433
|
+
}
|
|
2434
|
+
getManagerForAccount(accountId, agentId) {
|
|
2435
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2436
|
+
if (!resolvedAgentId)
|
|
2437
|
+
return;
|
|
2438
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
2439
|
+
if (!accounts)
|
|
2440
|
+
return;
|
|
2441
|
+
const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
|
|
2442
|
+
return accounts.managers.get(id);
|
|
2353
2443
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2444
|
+
getDefaultAccountId(agentId) {
|
|
2445
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2446
|
+
return resolvedAgentId ? this.agents.get(resolvedAgentId)?.defaultAccountId : undefined;
|
|
2447
|
+
}
|
|
2448
|
+
listAccountIds(agentId) {
|
|
2449
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2450
|
+
const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
|
|
2451
|
+
return accounts ? Array.from(accounts.managers.keys()) : [];
|
|
2452
|
+
}
|
|
2453
|
+
getManagersForAgent(agentId) {
|
|
2454
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
2455
|
+
const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
|
|
2456
|
+
return new Map(accounts?.managers ?? []);
|
|
2356
2457
|
}
|
|
2357
2458
|
async healthCheck() {
|
|
2358
2459
|
const managerStatuses = {};
|
|
2359
2460
|
let overallHealthy = true;
|
|
2360
|
-
for (const [agentId,
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2461
|
+
for (const [agentId, accounts] of Array.from(this.agents.entries())) {
|
|
2462
|
+
managerStatuses[agentId] = {};
|
|
2463
|
+
for (const [accountId, manager] of Array.from(accounts.managers.entries())) {
|
|
2464
|
+
try {
|
|
2465
|
+
const fid = getFarcasterFid(manager.runtime, accountId);
|
|
2466
|
+
if (!fid) {
|
|
2467
|
+
throw new Error("FARCASTER_FID not configured");
|
|
2468
|
+
}
|
|
2469
|
+
const profile = await manager.client.getProfile(fid);
|
|
2470
|
+
managerStatuses[agentId][accountId] = {
|
|
2471
|
+
status: "healthy",
|
|
2472
|
+
fid: profile.fid,
|
|
2473
|
+
username: profile.username
|
|
2474
|
+
};
|
|
2475
|
+
} catch (error) {
|
|
2476
|
+
managerStatuses[agentId][accountId] = {
|
|
2477
|
+
status: "unhealthy",
|
|
2478
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
2479
|
+
};
|
|
2480
|
+
overallHealthy = false;
|
|
2365
2481
|
}
|
|
2366
|
-
const profile = await manager.client.getProfile(fid);
|
|
2367
|
-
managerStatuses[agentId] = {
|
|
2368
|
-
status: "healthy",
|
|
2369
|
-
fid: profile.fid,
|
|
2370
|
-
username: profile.username
|
|
2371
|
-
};
|
|
2372
|
-
} catch (error) {
|
|
2373
|
-
managerStatuses[agentId] = {
|
|
2374
|
-
status: "unhealthy",
|
|
2375
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
2376
|
-
};
|
|
2377
|
-
overallHealthy = false;
|
|
2378
2482
|
}
|
|
2379
2483
|
}
|
|
2380
2484
|
return {
|
|
2381
2485
|
healthy: overallHealthy,
|
|
2382
2486
|
details: {
|
|
2383
|
-
activeManagers: this.managers.size,
|
|
2487
|
+
activeManagers: Array.from(this.agents.values()).reduce((total, accounts) => total + accounts.managers.size, 0),
|
|
2384
2488
|
managerStatuses
|
|
2385
2489
|
}
|
|
2386
2490
|
};
|
|
2387
2491
|
}
|
|
2388
2492
|
getActiveManagers() {
|
|
2389
|
-
return new Map(this.managers);
|
|
2493
|
+
return new Map(Array.from(this.agents.entries()).flatMap(([agentId, accounts]) => Array.from(accounts.managers.entries()).map(([accountId, manager]) => [`${agentId}:${accountId}`, manager])));
|
|
2494
|
+
}
|
|
2495
|
+
firstAgentId() {
|
|
2496
|
+
return this.agents.keys().next().value;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
// workflow-credential-provider.ts
|
|
2501
|
+
var import_core10 = require("@elizaos/core");
|
|
2502
|
+
var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
|
|
2503
|
+
var SUPPORTED = ["httpHeaderAuth"];
|
|
2504
|
+
|
|
2505
|
+
class FarcasterWorkflowCredentialProvider extends import_core10.Service {
|
|
2506
|
+
static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
|
|
2507
|
+
capabilityDescription = "Supplies Farcaster (Neynar API) credentials to the workflow plugin.";
|
|
2508
|
+
static async start(runtime) {
|
|
2509
|
+
return new FarcasterWorkflowCredentialProvider(runtime);
|
|
2510
|
+
}
|
|
2511
|
+
async stop() {}
|
|
2512
|
+
async resolve(_userId, credType) {
|
|
2513
|
+
if (credType !== "httpHeaderAuth")
|
|
2514
|
+
return null;
|
|
2515
|
+
const neynarApiKey = this.runtime.getSetting("FARCASTER_NEYNAR_API_KEY");
|
|
2516
|
+
if (!neynarApiKey?.trim())
|
|
2517
|
+
return null;
|
|
2518
|
+
return {
|
|
2519
|
+
status: "credential_data",
|
|
2520
|
+
data: { name: "api_key", value: neynarApiKey.trim() }
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
checkCredentialTypes(credTypes) {
|
|
2524
|
+
return {
|
|
2525
|
+
supported: credTypes.filter((t) => SUPPORTED.includes(t)),
|
|
2526
|
+
unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
|
|
2527
|
+
};
|
|
2390
2528
|
}
|
|
2391
2529
|
}
|
|
2392
2530
|
|
|
@@ -2394,11 +2532,24 @@ class FarcasterService extends import_core11.Service {
|
|
|
2394
2532
|
var farcasterPlugin = {
|
|
2395
2533
|
name: "farcaster",
|
|
2396
2534
|
description: "Farcaster client plugin for sending and receiving casts",
|
|
2397
|
-
services: [FarcasterService],
|
|
2398
|
-
actions:
|
|
2535
|
+
services: [FarcasterService, FarcasterWorkflowCredentialProvider],
|
|
2536
|
+
actions: [],
|
|
2399
2537
|
providers: farcasterProviders,
|
|
2400
|
-
routes: farcasterWebhookRoutes
|
|
2538
|
+
routes: farcasterWebhookRoutes,
|
|
2539
|
+
autoEnable: {
|
|
2540
|
+
connectorKeys: ["farcaster"]
|
|
2541
|
+
},
|
|
2542
|
+
async init(_config, runtime) {
|
|
2543
|
+
try {
|
|
2544
|
+
const manager = import_core11.getConnectorAccountManager(runtime);
|
|
2545
|
+
manager.registerProvider(createFarcasterConnectorAccountProvider(runtime));
|
|
2546
|
+
} catch (err) {
|
|
2547
|
+
import_core11.logger.warn({
|
|
2548
|
+
src: "plugin:farcaster",
|
|
2549
|
+
err: err instanceof Error ? err.message : String(err)
|
|
2550
|
+
}, "Failed to register Farcaster provider with ConnectorAccountManager");
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2401
2553
|
};
|
|
2402
|
-
var typescript_default = farcasterPlugin;
|
|
2403
2554
|
|
|
2404
|
-
//# debugId=
|
|
2555
|
+
//# debugId=24E35985E5D4101664756E2164756E21
|