@elizaos/agent 0.1.7-alpha.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.
Files changed (3) hide show
  1. package/package.json +62 -0
  2. package/src/index.ts +731 -0
  3. package/tsconfig.json +20 -0
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@elizaos/agent",
3
+ "version": "0.1.7-alpha.1",
4
+ "main": "src/index.ts",
5
+ "type": "module",
6
+ "scripts": {
7
+ "start": "node --loader ts-node/esm src/index.ts",
8
+ "dev": "node --loader ts-node/esm src/index.ts",
9
+ "check-types": "tsc --noEmit"
10
+ },
11
+ "nodemonConfig": {
12
+ "watch": [
13
+ "src",
14
+ "../core/dist"
15
+ ],
16
+ "ext": "ts,json",
17
+ "exec": "node --enable-source-maps --loader ts-node/esm src/index.ts"
18
+ },
19
+ "dependencies": {
20
+ "@elizaos/adapter-postgres": "workspace:*",
21
+ "@elizaos/adapter-redis": "workspace:*",
22
+ "@elizaos/adapter-sqlite": "workspace:*",
23
+ "@elizaos/client-auto": "workspace:*",
24
+ "@elizaos/client-direct": "workspace:*",
25
+ "@elizaos/client-discord": "workspace:*",
26
+ "@elizaos/client-farcaster": "workspace:*",
27
+ "@elizaos/client-lens": "workspace:*",
28
+ "@elizaos/client-telegram": "workspace:*",
29
+ "@elizaos/client-twitter": "workspace:*",
30
+ "@elizaos/client-slack": "workspace:*",
31
+ "@elizaos/core": "workspace:*",
32
+ "@elizaos/plugin-0g": "workspace:*",
33
+ "@elizaos/plugin-aptos": "workspace:*",
34
+ "@elizaos/plugin-bootstrap": "workspace:*",
35
+ "@elizaos/plugin-intiface": "workspace:*",
36
+ "@elizaos/plugin-coinbase": "workspace:*",
37
+ "@elizaos/plugin-conflux": "workspace:*",
38
+ "@elizaos/plugin-evm": "workspace:*",
39
+ "@elizaos/plugin-flow": "workspace:*",
40
+ "@elizaos/plugin-story": "workspace:*",
41
+ "@elizaos/plugin-goat": "workspace:*",
42
+ "@elizaos/plugin-icp": "workspace:*",
43
+ "@elizaos/plugin-image-generation": "workspace:*",
44
+ "@elizaos/plugin-nft-generation": "workspace:*",
45
+ "@elizaos/plugin-node": "workspace:*",
46
+ "@elizaos/plugin-solana": "workspace:*",
47
+ "@elizaos/plugin-starknet": "workspace:*",
48
+ "@elizaos/plugin-ton": "workspace:*",
49
+ "@elizaos/plugin-sui": "workspace:*",
50
+ "@elizaos/plugin-tee": "workspace:*",
51
+ "@elizaos/plugin-multiversx": "workspace:*",
52
+ "@elizaos/plugin-near": "workspace:*",
53
+ "@elizaos/plugin-zksync-era": "workspace:*",
54
+ "readline": "1.3.0",
55
+ "ws": "8.18.0",
56
+ "yargs": "17.7.2"
57
+ },
58
+ "devDependencies": {
59
+ "ts-node": "10.9.2",
60
+ "tsup": "8.3.5"
61
+ }
62
+ }
package/src/index.ts ADDED
@@ -0,0 +1,731 @@
1
+ import { PostgresDatabaseAdapter } from "@elizaos/adapter-postgres";
2
+ import { SqliteDatabaseAdapter } from "@elizaos/adapter-sqlite";
3
+ import { AutoClientInterface } from "@elizaos/client-auto";
4
+ import { DiscordClientInterface } from "@elizaos/client-discord";
5
+ import { FarcasterAgentClient } from "@elizaos/client-farcaster";
6
+ import { LensAgentClient } from "@elizaos/client-lens";
7
+ import { SlackClientInterface } from "@elizaos/client-slack";
8
+ import { TelegramClientInterface } from "@elizaos/client-telegram";
9
+ import { TwitterClientInterface } from "@elizaos/client-twitter";
10
+ import {
11
+ AgentRuntime,
12
+ CacheManager,
13
+ Character,
14
+ Clients,
15
+ DbCacheAdapter,
16
+ defaultCharacter,
17
+ elizaLogger,
18
+ FsCacheAdapter,
19
+ IAgentRuntime,
20
+ ICacheManager,
21
+ IDatabaseAdapter,
22
+ IDatabaseCacheAdapter,
23
+ ModelProviderName,
24
+ settings,
25
+ stringToUuid,
26
+ validateCharacterConfig,
27
+ CacheStore,
28
+ } from "@elizaos/core";
29
+ import { RedisClient } from "@elizaos/adapter-redis";
30
+ import { zgPlugin } from "@elizaos/plugin-0g";
31
+ import { bootstrapPlugin } from "@elizaos/plugin-bootstrap";
32
+ import createGoatPlugin from "@elizaos/plugin-goat";
33
+ // import { intifacePlugin } from "@elizaos/plugin-intiface";
34
+ import { DirectClient } from "@elizaos/client-direct";
35
+ import { aptosPlugin } from "@elizaos/plugin-aptos";
36
+ import {
37
+ advancedTradePlugin,
38
+ coinbaseCommercePlugin,
39
+ coinbaseMassPaymentsPlugin,
40
+ tokenContractPlugin,
41
+ tradePlugin,
42
+ webhookPlugin,
43
+ } from "@elizaos/plugin-coinbase";
44
+ import { confluxPlugin } from "@elizaos/plugin-conflux";
45
+ import { evmPlugin } from "@elizaos/plugin-evm";
46
+ import { storyPlugin } from "@elizaos/plugin-story";
47
+ import { flowPlugin } from "@elizaos/plugin-flow";
48
+ import { imageGenerationPlugin } from "@elizaos/plugin-image-generation";
49
+ import { multiversxPlugin } from "@elizaos/plugin-multiversx";
50
+ import { nearPlugin } from "@elizaos/plugin-near";
51
+ import { nftGenerationPlugin } from "@elizaos/plugin-nft-generation";
52
+ import { createNodePlugin } from "@elizaos/plugin-node";
53
+ import { solanaPlugin } from "@elizaos/plugin-solana";
54
+ import { suiPlugin } from "@elizaos/plugin-sui";
55
+ import { TEEMode, teePlugin } from "@elizaos/plugin-tee";
56
+ import { tonPlugin } from "@elizaos/plugin-ton";
57
+ import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era";
58
+ import Database from "better-sqlite3";
59
+ import fs from "fs";
60
+ import path from "path";
61
+ import { fileURLToPath } from "url";
62
+ import yargs from "yargs";
63
+
64
+ const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
65
+ const __dirname = path.dirname(__filename); // get the name of the directory
66
+
67
+ export const wait = (minTime: number = 1000, maxTime: number = 3000) => {
68
+ const waitTime =
69
+ Math.floor(Math.random() * (maxTime - minTime + 1)) + minTime;
70
+ return new Promise((resolve) => setTimeout(resolve, waitTime));
71
+ };
72
+
73
+ const logFetch = async (url: string, options: any) => {
74
+ elizaLogger.debug(`Fetching ${url}`);
75
+ // Disabled to avoid disclosure of sensitive information such as API keys
76
+ // elizaLogger.debug(JSON.stringify(options, null, 2));
77
+ return fetch(url, options);
78
+ };
79
+
80
+ export function parseArguments(): {
81
+ character?: string;
82
+ characters?: string;
83
+ } {
84
+ try {
85
+ return yargs(process.argv.slice(3))
86
+ .option("character", {
87
+ type: "string",
88
+ description: "Path to the character JSON file",
89
+ })
90
+ .option("characters", {
91
+ type: "string",
92
+ description:
93
+ "Comma separated list of paths to character JSON files",
94
+ })
95
+ .parseSync();
96
+ } catch (error) {
97
+ elizaLogger.error("Error parsing arguments:", error);
98
+ return {};
99
+ }
100
+ }
101
+
102
+ function tryLoadFile(filePath: string): string | null {
103
+ try {
104
+ return fs.readFileSync(filePath, "utf8");
105
+ } catch (e) {
106
+ return null;
107
+ }
108
+ }
109
+
110
+ function isAllStrings(arr: unknown[]): boolean {
111
+ return Array.isArray(arr) && arr.every((item) => typeof item === "string");
112
+ }
113
+
114
+ export async function loadCharacters(
115
+ charactersArg: string
116
+ ): Promise<Character[]> {
117
+ let characterPaths = charactersArg
118
+ ?.split(",")
119
+ .map((filePath) => filePath.trim());
120
+ const loadedCharacters = [];
121
+
122
+ if (characterPaths?.length > 0) {
123
+ for (const characterPath of characterPaths) {
124
+ let content = null;
125
+ let resolvedPath = "";
126
+
127
+ // Try different path resolutions in order
128
+ const pathsToTry = [
129
+ characterPath, // exact path as specified
130
+ path.resolve(process.cwd(), characterPath), // relative to cwd
131
+ path.resolve(process.cwd(), "agent", characterPath), // Add this
132
+ path.resolve(__dirname, characterPath), // relative to current script
133
+ path.resolve(
134
+ __dirname,
135
+ "characters",
136
+ path.basename(characterPath)
137
+ ), // relative to agent/characters
138
+ path.resolve(
139
+ __dirname,
140
+ "../characters",
141
+ path.basename(characterPath)
142
+ ), // relative to characters dir from agent
143
+ path.resolve(
144
+ __dirname,
145
+ "../../characters",
146
+ path.basename(characterPath)
147
+ ), // relative to project root characters dir
148
+ ];
149
+
150
+ elizaLogger.info(
151
+ "Trying paths:",
152
+ pathsToTry.map((p) => ({
153
+ path: p,
154
+ exists: fs.existsSync(p),
155
+ }))
156
+ );
157
+
158
+ for (const tryPath of pathsToTry) {
159
+ content = tryLoadFile(tryPath);
160
+ if (content !== null) {
161
+ resolvedPath = tryPath;
162
+ break;
163
+ }
164
+ }
165
+
166
+ if (content === null) {
167
+ elizaLogger.error(
168
+ `Error loading character from ${characterPath}: File not found in any of the expected locations`
169
+ );
170
+ elizaLogger.error("Tried the following paths:");
171
+ pathsToTry.forEach((p) => elizaLogger.error(` - ${p}`));
172
+ process.exit(1);
173
+ }
174
+
175
+ try {
176
+ const character = JSON.parse(content);
177
+ validateCharacterConfig(character);
178
+
179
+ // Handle plugins
180
+ if (isAllStrings(character.plugins)) {
181
+ elizaLogger.info("Plugins are: ", character.plugins);
182
+ const importedPlugins = await Promise.all(
183
+ character.plugins.map(async (plugin) => {
184
+ const importedPlugin = await import(plugin);
185
+ return importedPlugin.default;
186
+ })
187
+ );
188
+ character.plugins = importedPlugins;
189
+ }
190
+
191
+ loadedCharacters.push(character);
192
+ elizaLogger.info(
193
+ `Successfully loaded character from: ${resolvedPath}`
194
+ );
195
+ } catch (e) {
196
+ elizaLogger.error(
197
+ `Error parsing character from ${resolvedPath}: ${e}`
198
+ );
199
+ process.exit(1);
200
+ }
201
+ }
202
+ }
203
+
204
+ if (loadedCharacters.length === 0) {
205
+ elizaLogger.info("No characters found, using default character");
206
+ loadedCharacters.push(defaultCharacter);
207
+ }
208
+
209
+ return loadedCharacters;
210
+ }
211
+
212
+ export function getTokenForProvider(
213
+ provider: ModelProviderName,
214
+ character: Character
215
+ ): string {
216
+ switch (provider) {
217
+ // no key needed for llama_local or gaianet
218
+ case ModelProviderName.LLAMALOCAL:
219
+ return "";
220
+ case ModelProviderName.OLLAMA:
221
+ return "";
222
+ case ModelProviderName.GAIANET:
223
+ return "";
224
+ case ModelProviderName.OPENAI:
225
+ return (
226
+ character.settings?.secrets?.OPENAI_API_KEY ||
227
+ settings.OPENAI_API_KEY
228
+ );
229
+ case ModelProviderName.ETERNALAI:
230
+ return (
231
+ character.settings?.secrets?.ETERNALAI_API_KEY ||
232
+ settings.ETERNALAI_API_KEY
233
+ );
234
+ case ModelProviderName.LLAMACLOUD:
235
+ case ModelProviderName.TOGETHER:
236
+ return (
237
+ character.settings?.secrets?.LLAMACLOUD_API_KEY ||
238
+ settings.LLAMACLOUD_API_KEY ||
239
+ character.settings?.secrets?.TOGETHER_API_KEY ||
240
+ settings.TOGETHER_API_KEY ||
241
+ character.settings?.secrets?.XAI_API_KEY ||
242
+ settings.XAI_API_KEY ||
243
+ character.settings?.secrets?.OPENAI_API_KEY ||
244
+ settings.OPENAI_API_KEY
245
+ );
246
+ case ModelProviderName.CLAUDE_VERTEX:
247
+ case ModelProviderName.ANTHROPIC:
248
+ return (
249
+ character.settings?.secrets?.ANTHROPIC_API_KEY ||
250
+ character.settings?.secrets?.CLAUDE_API_KEY ||
251
+ settings.ANTHROPIC_API_KEY ||
252
+ settings.CLAUDE_API_KEY
253
+ );
254
+ case ModelProviderName.REDPILL:
255
+ return (
256
+ character.settings?.secrets?.REDPILL_API_KEY ||
257
+ settings.REDPILL_API_KEY
258
+ );
259
+ case ModelProviderName.OPENROUTER:
260
+ return (
261
+ character.settings?.secrets?.OPENROUTER ||
262
+ settings.OPENROUTER_API_KEY
263
+ );
264
+ case ModelProviderName.GROK:
265
+ return (
266
+ character.settings?.secrets?.GROK_API_KEY ||
267
+ settings.GROK_API_KEY
268
+ );
269
+ case ModelProviderName.HEURIST:
270
+ return (
271
+ character.settings?.secrets?.HEURIST_API_KEY ||
272
+ settings.HEURIST_API_KEY
273
+ );
274
+ case ModelProviderName.GROQ:
275
+ return (
276
+ character.settings?.secrets?.GROQ_API_KEY ||
277
+ settings.GROQ_API_KEY
278
+ );
279
+ case ModelProviderName.GALADRIEL:
280
+ return (
281
+ character.settings?.secrets?.GALADRIEL_API_KEY ||
282
+ settings.GALADRIEL_API_KEY
283
+ );
284
+ case ModelProviderName.FAL:
285
+ return (
286
+ character.settings?.secrets?.FAL_API_KEY || settings.FAL_API_KEY
287
+ );
288
+ case ModelProviderName.ALI_BAILIAN:
289
+ return (
290
+ character.settings?.secrets?.ALI_BAILIAN_API_KEY ||
291
+ settings.ALI_BAILIAN_API_KEY
292
+ );
293
+ case ModelProviderName.VOLENGINE:
294
+ return (
295
+ character.settings?.secrets?.VOLENGINE_API_KEY ||
296
+ settings.VOLENGINE_API_KEY
297
+ );
298
+ case ModelProviderName.NANOGPT:
299
+ return (
300
+ character.settings?.secrets?.NANOGPT_API_KEY ||
301
+ settings.NANOGPT_API_KEY
302
+ );
303
+ case ModelProviderName.HYPERBOLIC:
304
+ return (
305
+ character.settings?.secrets?.HYPERBOLIC_API_KEY ||
306
+ settings.HYPERBOLIC_API_KEY
307
+ );
308
+ case ModelProviderName.VENICE:
309
+ return (
310
+ character.settings?.secrets?.VENICE_API_KEY ||
311
+ settings.VENICE_API_KEY
312
+ );
313
+ case ModelProviderName.AKASH_CHAT_API:
314
+ return (
315
+ character.settings?.secrets?.AKASH_CHAT_API_KEY ||
316
+ settings.AKASH_CHAT_API_KEY
317
+ );
318
+ case ModelProviderName.GOOGLE:
319
+ return (
320
+ character.settings?.secrets?.GOOGLE_GENERATIVE_AI_API_KEY ||
321
+ settings.GOOGLE_GENERATIVE_AI_API_KEY
322
+ );
323
+ default:
324
+ const errorMessage = `Failed to get token - unsupported model provider: ${provider}`;
325
+ elizaLogger.error(errorMessage);
326
+ throw new Error(errorMessage);
327
+ }
328
+ }
329
+
330
+ function initializeDatabase(dataDir: string) {
331
+ if (process.env.POSTGRES_URL) {
332
+ elizaLogger.info("Initializing PostgreSQL connection...");
333
+ const db = new PostgresDatabaseAdapter({
334
+ connectionString: process.env.POSTGRES_URL,
335
+ parseInputs: true,
336
+ });
337
+
338
+ // Test the connection
339
+ db.init()
340
+ .then(() => {
341
+ elizaLogger.success(
342
+ "Successfully connected to PostgreSQL database"
343
+ );
344
+ })
345
+ .catch((error) => {
346
+ elizaLogger.error("Failed to connect to PostgreSQL:", error);
347
+ });
348
+
349
+ return db;
350
+ } else {
351
+ const filePath =
352
+ process.env.SQLITE_FILE ?? path.resolve(dataDir, "db.sqlite");
353
+ // ":memory:";
354
+ const db = new SqliteDatabaseAdapter(new Database(filePath));
355
+ return db;
356
+ }
357
+ }
358
+
359
+ // also adds plugins from character file into the runtime
360
+ export async function initializeClients(
361
+ character: Character,
362
+ runtime: IAgentRuntime
363
+ ) {
364
+ // each client can only register once
365
+ // and if we want two we can explicitly support it
366
+ const clients: Record<string, any> = {};
367
+ const clientTypes: string[] =
368
+ character.clients?.map((str) => str.toLowerCase()) || [];
369
+ elizaLogger.log("initializeClients", clientTypes, "for", character.name);
370
+
371
+ if (clientTypes.includes(Clients.DIRECT)) {
372
+ const autoClient = await AutoClientInterface.start(runtime);
373
+ if (autoClient) clients.auto = autoClient;
374
+ }
375
+
376
+ if (clientTypes.includes(Clients.DISCORD)) {
377
+ const discordClient = await DiscordClientInterface.start(runtime);
378
+ if (discordClient) clients.discord = discordClient;
379
+ }
380
+
381
+ if (clientTypes.includes(Clients.TELEGRAM)) {
382
+ const telegramClient = await TelegramClientInterface.start(runtime);
383
+ if (telegramClient) clients.telegram = telegramClient;
384
+ }
385
+
386
+ if (clientTypes.includes(Clients.TWITTER)) {
387
+ const twitterClient = await TwitterClientInterface.start(runtime);
388
+
389
+ if (twitterClient) {
390
+ clients.twitter = twitterClient;
391
+ (twitterClient as any).enableSearch = !isFalsish(
392
+ getSecret(character, "TWITTER_SEARCH_ENABLE")
393
+ );
394
+ }
395
+ }
396
+
397
+ if (clientTypes.includes(Clients.FARCASTER)) {
398
+ // why is this one different :(
399
+ const farcasterClient = new FarcasterAgentClient(runtime);
400
+ if (farcasterClient) {
401
+ farcasterClient.start();
402
+ clients.farcaster = farcasterClient;
403
+ }
404
+ }
405
+ if (clientTypes.includes("lens")) {
406
+ const lensClient = new LensAgentClient(runtime);
407
+ lensClient.start();
408
+ clients.lens = lensClient;
409
+ }
410
+
411
+ elizaLogger.log("client keys", Object.keys(clients));
412
+
413
+ // TODO: Add Slack client to the list
414
+ // Initialize clients as an object
415
+
416
+ if (clientTypes.includes("slack")) {
417
+ const slackClient = await SlackClientInterface.start(runtime);
418
+ if (slackClient) clients.slack = slackClient; // Use object property instead of push
419
+ }
420
+
421
+ if (character.plugins?.length > 0) {
422
+ for (const plugin of character.plugins) {
423
+ if (plugin.clients) {
424
+ for (const client of plugin.clients) {
425
+ const startedClient = await client.start(runtime);
426
+ clients[client.name] = startedClient; // Assuming client has a name property
427
+ }
428
+ }
429
+ }
430
+ }
431
+
432
+ return clients;
433
+ }
434
+
435
+ function isFalsish(input: any): boolean {
436
+ // If the input is exactly NaN, return true
437
+ if (Number.isNaN(input)) {
438
+ return true;
439
+ }
440
+
441
+ // Convert input to a string if it's not null or undefined
442
+ const value = input == null ? "" : String(input);
443
+
444
+ // List of common falsish string representations
445
+ const falsishValues = [
446
+ "false",
447
+ "0",
448
+ "no",
449
+ "n",
450
+ "off",
451
+ "null",
452
+ "undefined",
453
+ "",
454
+ ];
455
+
456
+ // Check if the value (trimmed and lowercased) is in the falsish list
457
+ return falsishValues.includes(value.trim().toLowerCase());
458
+ }
459
+
460
+ function getSecret(character: Character, secret: string) {
461
+ return character.settings?.secrets?.[secret] || process.env[secret];
462
+ }
463
+
464
+ let nodePlugin: any | undefined;
465
+
466
+ export async function createAgent(
467
+ character: Character,
468
+ db: IDatabaseAdapter,
469
+ cache: ICacheManager,
470
+ token: string
471
+ ): Promise<AgentRuntime> {
472
+ elizaLogger.success(
473
+ elizaLogger.successesTitle,
474
+ "Creating runtime for character",
475
+ character.name
476
+ );
477
+
478
+ nodePlugin ??= createNodePlugin();
479
+
480
+ const teeMode = getSecret(character, "TEE_MODE") || "OFF";
481
+ const walletSecretSalt = getSecret(character, "WALLET_SECRET_SALT");
482
+
483
+ // Validate TEE configuration
484
+ if (teeMode !== TEEMode.OFF && !walletSecretSalt) {
485
+ elizaLogger.error(
486
+ "WALLET_SECRET_SALT required when TEE_MODE is enabled"
487
+ );
488
+ throw new Error("Invalid TEE configuration");
489
+ }
490
+
491
+ let goatPlugin: any | undefined;
492
+ if (getSecret(character, "ALCHEMY_API_KEY")) {
493
+ goatPlugin = await createGoatPlugin((secret) =>
494
+ getSecret(character, secret)
495
+ );
496
+ }
497
+
498
+ return new AgentRuntime({
499
+ databaseAdapter: db,
500
+ token,
501
+ modelProvider: character.modelProvider,
502
+ evaluators: [],
503
+ character,
504
+ // character.plugins are handled when clients are added
505
+ plugins: [
506
+ bootstrapPlugin,
507
+ getSecret(character, "CONFLUX_CORE_PRIVATE_KEY")
508
+ ? confluxPlugin
509
+ : null,
510
+ nodePlugin,
511
+ getSecret(character, "SOLANA_PUBLIC_KEY") ||
512
+ (getSecret(character, "WALLET_PUBLIC_KEY") &&
513
+ !getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
514
+ ? solanaPlugin
515
+ : null,
516
+ (getSecret(character, "NEAR_ADDRESS") ||
517
+ getSecret(character, "NEAR_WALLET_PUBLIC_KEY")) &&
518
+ getSecret(character, "NEAR_WALLET_SECRET_KEY")
519
+ ? nearPlugin
520
+ : null,
521
+ getSecret(character, "EVM_PUBLIC_KEY") ||
522
+ (getSecret(character, "WALLET_PUBLIC_KEY") &&
523
+ getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
524
+ ? evmPlugin
525
+ : null,
526
+ (getSecret(character, "SOLANA_PUBLIC_KEY") ||
527
+ (getSecret(character, "WALLET_PUBLIC_KEY") &&
528
+ !getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith(
529
+ "0x"
530
+ ))) &&
531
+ getSecret(character, "SOLANA_ADMIN_PUBLIC_KEY") &&
532
+ getSecret(character, "SOLANA_PRIVATE_KEY") &&
533
+ getSecret(character, "SOLANA_ADMIN_PRIVATE_KEY")
534
+ ? nftGenerationPlugin
535
+ : null,
536
+ getSecret(character, "ZEROG_PRIVATE_KEY") ? zgPlugin : null,
537
+ getSecret(character, "COINBASE_COMMERCE_KEY")
538
+ ? coinbaseCommercePlugin
539
+ : null,
540
+ getSecret(character, "FAL_API_KEY") ||
541
+ getSecret(character, "OPENAI_API_KEY") ||
542
+ getSecret(character, "VENICE_API_KEY") ||
543
+ getSecret(character, "HEURIST_API_KEY")
544
+ ? imageGenerationPlugin
545
+ : null,
546
+ ...(getSecret(character, "COINBASE_API_KEY") &&
547
+ getSecret(character, "COINBASE_PRIVATE_KEY")
548
+ ? [
549
+ coinbaseMassPaymentsPlugin,
550
+ tradePlugin,
551
+ tokenContractPlugin,
552
+ advancedTradePlugin,
553
+ ]
554
+ : []),
555
+ ...(teeMode !== TEEMode.OFF && walletSecretSalt
556
+ ? [teePlugin, solanaPlugin]
557
+ : []),
558
+ getSecret(character, "COINBASE_API_KEY") &&
559
+ getSecret(character, "COINBASE_PRIVATE_KEY") &&
560
+ getSecret(character, "COINBASE_NOTIFICATION_URI")
561
+ ? webhookPlugin
562
+ : null,
563
+ getSecret(character, "ALCHEMY_API_KEY") ? goatPlugin : null,
564
+ getSecret(character, "FLOW_ADDRESS") &&
565
+ getSecret(character, "FLOW_PRIVATE_KEY")
566
+ ? flowPlugin
567
+ : null,
568
+ getSecret(character, "APTOS_PRIVATE_KEY") ? aptosPlugin : null,
569
+ getSecret(character, "MVX_PRIVATE_KEY") ? multiversxPlugin : null,
570
+ getSecret(character, "ZKSYNC_PRIVATE_KEY") ? zksyncEraPlugin : null,
571
+ getSecret(character, "TON_PRIVATE_KEY") ? tonPlugin : null,
572
+ getSecret(character, "SUI_PRIVATE_KEY") ? suiPlugin : null,
573
+ getSecret(character, "STORY_PRIVATE_KEY") ? storyPlugin : null,
574
+ ].filter(Boolean),
575
+ providers: [],
576
+ actions: [],
577
+ services: [],
578
+ managers: [],
579
+ cacheManager: cache,
580
+ fetch: logFetch,
581
+ });
582
+ }
583
+
584
+ function initializeFsCache(baseDir: string, character: Character) {
585
+ const cacheDir = path.resolve(baseDir, character.id, "cache");
586
+
587
+ const cache = new CacheManager(new FsCacheAdapter(cacheDir));
588
+ return cache;
589
+ }
590
+
591
+ function initializeDbCache(character: Character, db: IDatabaseCacheAdapter) {
592
+ const cache = new CacheManager(new DbCacheAdapter(db, character.id));
593
+ return cache;
594
+ }
595
+
596
+ function initializeCache(
597
+ cacheStore: string,
598
+ character: Character,
599
+ baseDir?: string,
600
+ db?: IDatabaseCacheAdapter
601
+ ) {
602
+ switch (cacheStore) {
603
+ case CacheStore.REDIS:
604
+ if (process.env.REDIS_URL) {
605
+ elizaLogger.info("Connecting to Redis...");
606
+ const redisClient = new RedisClient(process.env.REDIS_URL);
607
+ return new CacheManager(
608
+ new DbCacheAdapter(redisClient, character.id) // Using DbCacheAdapter since RedisClient also implements IDatabaseCacheAdapter
609
+ );
610
+ } else {
611
+ throw new Error("REDIS_URL environment variable is not set.");
612
+ }
613
+
614
+ case CacheStore.DATABASE:
615
+ if (db) {
616
+ elizaLogger.info("Using Database Cache...");
617
+ return initializeDbCache(character, db);
618
+ } else {
619
+ throw new Error(
620
+ "Database adapter is not provided for CacheStore.Database."
621
+ );
622
+ }
623
+
624
+ case CacheStore.FILESYSTEM:
625
+ elizaLogger.info("Using File System Cache...");
626
+ return initializeFsCache(baseDir, character);
627
+
628
+ default:
629
+ throw new Error(
630
+ `Invalid cache store: ${cacheStore} or required configuration missing.`
631
+ );
632
+ }
633
+ }
634
+
635
+ async function startAgent(
636
+ character: Character,
637
+ directClient: DirectClient
638
+ ): Promise<AgentRuntime> {
639
+ let db: IDatabaseAdapter & IDatabaseCacheAdapter;
640
+ try {
641
+ character.id ??= stringToUuid(character.name);
642
+ character.username ??= character.name;
643
+
644
+ const token = getTokenForProvider(character.modelProvider, character);
645
+ const dataDir = path.join(__dirname, "../data");
646
+
647
+ if (!fs.existsSync(dataDir)) {
648
+ fs.mkdirSync(dataDir, { recursive: true });
649
+ }
650
+
651
+ db = initializeDatabase(dataDir) as IDatabaseAdapter &
652
+ IDatabaseCacheAdapter;
653
+
654
+ await db.init();
655
+
656
+ const cache = initializeCache(
657
+ process.env.CACHE_STORE ?? CacheStore.DATABASE,
658
+ character,
659
+ "",
660
+ db
661
+ ); // "" should be replaced with dir for file system caching. THOUGHTS: might probably make this into an env
662
+ const runtime: AgentRuntime = await createAgent(
663
+ character,
664
+ db,
665
+ cache,
666
+ token
667
+ );
668
+
669
+ // start services/plugins/process knowledge
670
+ await runtime.initialize();
671
+
672
+ // start assigned clients
673
+ runtime.clients = await initializeClients(character, runtime);
674
+
675
+ // add to container
676
+ directClient.registerAgent(runtime);
677
+
678
+ // report to console
679
+ elizaLogger.debug(`Started ${character.name} as ${runtime.agentId}`);
680
+
681
+ return runtime;
682
+ } catch (error) {
683
+ elizaLogger.error(
684
+ `Error starting agent for character ${character.name}:`,
685
+ error
686
+ );
687
+ elizaLogger.error(error);
688
+ if (db) {
689
+ await db.close();
690
+ }
691
+ throw error;
692
+ }
693
+ }
694
+
695
+ const startAgents = async () => {
696
+ const directClient = new DirectClient();
697
+ const serverPort = parseInt(settings.SERVER_PORT || "3000");
698
+ const args = parseArguments();
699
+
700
+ let charactersArg = args.characters || args.character;
701
+
702
+ let characters = [defaultCharacter];
703
+
704
+ if (charactersArg) {
705
+ characters = await loadCharacters(charactersArg);
706
+ }
707
+
708
+ try {
709
+ for (const character of characters) {
710
+ await startAgent(character, directClient);
711
+ }
712
+ } catch (error) {
713
+ elizaLogger.error("Error starting agents:", error);
714
+ }
715
+
716
+ // upload some agent functionality into directClient
717
+ directClient.startAgent = async (character: Character) => {
718
+ // wrap it so we don't have to inject directClient later
719
+ return startAgent(character, directClient);
720
+ };
721
+ directClient.start(serverPort);
722
+
723
+ elizaLogger.log(
724
+ "Run `pnpm start:client` to start the client and visit the outputted URL (http://localhost:5173) to chat with your agents"
725
+ );
726
+ };
727
+
728
+ startAgents().catch((error) => {
729
+ elizaLogger.error("Unhandled error in startAgents:", error);
730
+ process.exit(1); // Exit the process after logging
731
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "../packages/core/tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": ".",
6
+ "module": "ESNext",
7
+ "moduleResolution": "Bundler",
8
+ "types": [
9
+ "node"
10
+ ]
11
+ },
12
+ "ts-node": {
13
+ "experimentalSpecifierResolution": "node",
14
+ "transpileOnly": true,
15
+ "esm": true,
16
+ },
17
+ "include": [
18
+ "src"
19
+ ]
20
+ }