From 75ab47829d1b0813dbdce12c1be577b7ef87571b Mon Sep 17 00:00:00 2001 From: Shaw Date: Tue, 11 Feb 2025 00:05:20 -0500 Subject: [PATCH 1/3] updates to local ai provider and tee --- bun.lockb | Bin 1375096 -> 1374072 bytes packages/agent/src/defaultCharacter.ts | 1 + packages/core/src/knowledge.ts | 2 + packages/core/src/memory.ts | 2 + .../plugin-bootstrap/src/providers/facts.ts | 2 + packages/plugin-drizzle/src/index.ts | 84 +-- packages/plugin-drizzle/src/migrations.ts | 6 +- packages/plugin-local-ai/echogarden.d.ts | 20 + packages/plugin-local-ai/environment.ts | 36 ++ packages/plugin-local-ai/package.json | 54 +- packages/plugin-local-ai/src/index.ts | 542 ++++++++++++++---- packages/plugin-node/package.json | 1 - .../src/providers/deriveKeyProvider.ts | 42 +- .../providers/remoteAttestationProvider.ts | 16 +- .../src/providers/walletProvider.ts | 32 +- 15 files changed, 632 insertions(+), 208 deletions(-) create mode 100644 packages/plugin-local-ai/echogarden.d.ts create mode 100644 packages/plugin-local-ai/environment.ts diff --git a/bun.lockb b/bun.lockb index 9eda4ab2b6584bc2f5db2a93eb0f41c19e06729c..b9729a4a9db5250eee2ce480e8e68a7a649751fc 100755 GIT binary patch delta 63929 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 delta 65221 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ೲmL?} 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 AqyPW_ diff --git a/packages/agent/src/defaultCharacter.ts b/packages/agent/src/defaultCharacter.ts index f74679419a2..5d7aa5c1446 100644 --- a/packages/agent/src/defaultCharacter.ts +++ b/packages/agent/src/defaultCharacter.ts @@ -6,6 +6,7 @@ export const defaultCharacter: Character = { plugins: [ "@elizaos/plugin-anthropic", "@elizaos/plugin-openai", + "@elizaos/plugin-local-ai", "@elizaos/plugin-discord", "@elizaos/plugin-node", "@elizaos/plugin-telegram", diff --git a/packages/core/src/knowledge.ts b/packages/core/src/knowledge.ts index 9a149d7455d..8b157646fa8 100644 --- a/packages/core/src/knowledge.ts +++ b/packages/core/src/knowledge.ts @@ -8,6 +8,7 @@ async function get( runtime: AgentRuntime, message: Memory ): Promise { + console.log("get", message); // Add validation for message if (!message?.content?.text) { logger.warn("Invalid message for knowledge query:", { @@ -69,6 +70,7 @@ async function set( chunkSize = 512, bleed = 20 ) { + console.log("set", item); const embedding = await runtime.useModel(ModelClass.TEXT_EMBEDDING, null); await runtime.documentsManager.createMemory({ id: item.id, diff --git a/packages/core/src/memory.ts b/packages/core/src/memory.ts index 08ea3e13e6e..bda44b96f7b 100644 --- a/packages/core/src/memory.ts +++ b/packages/core/src/memory.ts @@ -50,6 +50,7 @@ export class MemoryManager implements IMemoryManager { * @throws Error if the memory content is empty */ async addEmbeddingToMemory(memory: Memory): Promise { + console.log("addEmbeddingToMemory", memory); // Return early if embedding already exists if (memory.embedding) { return memory; @@ -173,6 +174,7 @@ export class MemoryManager implements IMemoryManager { */ async createMemory(memory: Memory, unique = false): Promise { // TODO: check memory.agentId == this.runtime.agentId + console.log("createMemory", memory); const existingMessage = await this.runtime.databaseAdapter.getMemoryById(memory.id); diff --git a/packages/plugin-bootstrap/src/providers/facts.ts b/packages/plugin-bootstrap/src/providers/facts.ts index 4e6aa0f3831..c1b3816e99b 100644 --- a/packages/plugin-bootstrap/src/providers/facts.ts +++ b/packages/plugin-bootstrap/src/providers/facts.ts @@ -10,6 +10,8 @@ import { formatFacts } from "../evaluators/fact.ts"; const factsProvider: Provider = { get: async (runtime: IAgentRuntime, message: Memory, state?: State) => { const recentMessagesData = state?.recentMessagesData?.slice(-10); + console.log("get", message); + const recentMessages = formatMessages({ messages: recentMessagesData, diff --git a/packages/plugin-drizzle/src/index.ts b/packages/plugin-drizzle/src/index.ts index ef0134a9e4d..31b400a4e58 100644 --- a/packages/plugin-drizzle/src/index.ts +++ b/packages/plugin-drizzle/src/index.ts @@ -4,7 +4,7 @@ import { DatabaseAdapter, type GoalStatus, type Participant, - elizaLogger, + logger, type Goal, type IDatabaseCacheAdapter, type Memory, @@ -67,7 +67,7 @@ export class DrizzleDatabaseAdapter this.pool = new pg.Pool(poolConfig); this.pool.on("error", (err) => { - elizaLogger.error("Unexpected pool error", err); + logger.error("Unexpected pool error", err); this.handlePoolError(err); }); @@ -92,7 +92,7 @@ export class DrizzleDatabaseAdapter } private async handlePoolError(error: Error) { - elizaLogger.error("Pool error occurred, attempting to reconnect", { + logger.error("Pool error occurred, attempting to reconnect", { error: error.message, }); @@ -107,9 +107,9 @@ export class DrizzleDatabaseAdapter }); await this.testConnection(); - elizaLogger.success("Pool reconnection successful"); + logger.success("Pool reconnection successful"); } catch (reconnectError) { - elizaLogger.error("Failed to reconnect pool", { + logger.error("Failed to reconnect pool", { error: reconnectError instanceof Error ? reconnectError.message @@ -146,7 +146,7 @@ export class DrizzleDatabaseAdapter const jitter = Math.random() * this.jitterMax; const delay = backoffDelay + jitter; - elizaLogger.warn( + logger.warn( `Database operation failed (attempt ${attempt}/${this.maxRetries}):`, { error: @@ -159,7 +159,7 @@ export class DrizzleDatabaseAdapter await new Promise((resolve) => setTimeout(resolve, delay)); } else { - elizaLogger.error("Max retry attempts reached:", { + logger.error("Max retry attempts reached:", { error: error instanceof Error ? error.message @@ -179,9 +179,9 @@ export class DrizzleDatabaseAdapter async cleanup(): Promise { try { await this.pool.end(); - elizaLogger.info("Database pool closed"); + logger.info("Database pool closed"); } catch (error) { - elizaLogger.error("Error closing database pool:", error); + logger.error("Error closing database pool:", error); } } @@ -221,13 +221,13 @@ export class DrizzleDatabaseAdapter const hasVector = vectorExt?.rows.length > 0; if (!hasVector) { - elizaLogger.warn("Vector extension not found"); + logger.warn("Vector extension not found"); return false; } return true; } catch (error) { - elizaLogger.error("Error validating vector setup:", error); + logger.error("Error validating vector setup:", error); return false; } } @@ -248,7 +248,7 @@ export class DrizzleDatabaseAdapter await runMigrations(this.pool); } } catch (error) { - elizaLogger.error("Failed to initialize database:", error); + logger.error("Failed to initialize database:", error); throw error; } } @@ -259,7 +259,7 @@ export class DrizzleDatabaseAdapter await (this.db as any).client.close(); } } catch (error) { - elizaLogger.error("Failed to close database connection:", { + logger.error("Failed to close database connection:", { error: error instanceof Error ? error.message : String(error), }); throw error; @@ -269,13 +269,13 @@ export class DrizzleDatabaseAdapter async testConnection(): Promise { try { const result = await this.db.execute(sql`SELECT NOW()`); - elizaLogger.success( + logger.success( "Database connection test successful:", result.rows[0] ); return true; } catch (error) { - elizaLogger.error("Database connection test failed:", error); + logger.error("Database connection test failed:", error); throw new Error( `Failed to connect to database: ${(error as Error).message}` ); @@ -319,13 +319,13 @@ export class DrizzleDatabaseAdapter details: sql`${account.details}::jsonb` || {}, }); - elizaLogger.debug("Account created successfully:", { + logger.debug("Account created successfully:", { accountId, }); return true; } catch (error) { - elizaLogger.error("Error creating account:", { + logger.error("Error creating account:", { error: error instanceof Error ? error.message : String(error), accountId: account.id, @@ -547,7 +547,7 @@ export class DrizzleDatabaseAdapter })) .filter((row) => Array.isArray(row.embedding)); } catch (error) { - elizaLogger.error("Error in getCachedEmbeddings:", { + logger.error("Error in getCachedEmbeddings:", { error: error instanceof Error ? error.message : String(error), tableName: opts.query_table_name, @@ -580,7 +580,7 @@ export class DrizzleDatabaseAdapter type: params.type, }); } catch (error) { - elizaLogger.error("Failed to create log entry:", { + logger.error("Failed to create log entry:", { error: error instanceof Error ? error.message : String(error), type: params.type, @@ -614,7 +614,7 @@ export class DrizzleDatabaseAdapter .where(eq(participantTable.roomId, params.roomId)) .orderBy(accountTable.name); - elizaLogger.debug("Retrieved actor details:", { + logger.debug("Retrieved actor details:", { roomId: params.roomId, actorCount: result.length, }); @@ -637,7 +637,7 @@ export class DrizzleDatabaseAdapter }, }; } catch (error) { - elizaLogger.warn("Failed to parse actor details:", { + logger.warn("Failed to parse actor details:", { actorId: row.id, error: error instanceof Error @@ -658,7 +658,7 @@ export class DrizzleDatabaseAdapter } }); } catch (error) { - elizaLogger.error("Failed to fetch actor details:", { + logger.error("Failed to fetch actor details:", { roomId: params.roomId, error: error instanceof Error ? error.message : String(error), @@ -778,7 +778,7 @@ export class DrizzleDatabaseAdapter async createMemory(memory: Memory, tableName: string): Promise { return this.withDatabase(async () => { - elizaLogger.debug("DrizzleAdapter createMemory:", { + logger.debug("DrizzleAdapter createMemory:", { memoryId: memory.id, embeddingLength: memory.embedding?.length, contentLength: memory.content?.text?.length, @@ -786,7 +786,7 @@ export class DrizzleDatabaseAdapter let isUnique = true; if (memory.embedding) { - elizaLogger.info("Searching for similar memories:"); + logger.info("Searching for similar memories:"); const similarMemories = await this.searchMemoriesByEmbedding( memory.embedding, { @@ -844,7 +844,7 @@ export class DrizzleDatabaseAdapter ) ); - elizaLogger.debug("All memories removed successfully:", { + logger.debug("All memories removed successfully:", { roomId, tableName, }); @@ -931,7 +931,7 @@ export class DrizzleDatabaseAdapter }) .where(eq(goalTable.id, goal.id as string)); } catch (error) { - elizaLogger.error("Failed to update goal:", { + logger.error("Failed to update goal:", { error: error instanceof Error ? error.message : String(error), goalId: goal.id, @@ -953,7 +953,7 @@ export class DrizzleDatabaseAdapter objectives: sql`${goal.objectives}::jsonb`, }); } catch (error) { - elizaLogger.error("Failed to update goal:", { + logger.error("Failed to update goal:", { goalId: goal.id, error: error instanceof Error ? error.message : String(error), status: goal.status, @@ -969,12 +969,12 @@ export class DrizzleDatabaseAdapter try { await this.db.delete(goalTable).where(eq(goalTable.id, goalId)); - elizaLogger.debug("Goal removal attempt:", { + logger.debug("Goal removal attempt:", { goalId, removed: true, }); } catch (error) { - elizaLogger.error("Failed to remove goal:", { + logger.error("Failed to remove goal:", { error: error instanceof Error ? error.message : String(error), goalId, @@ -1055,7 +1055,7 @@ export class DrizzleDatabaseAdapter }); return true; } catch (error) { - elizaLogger.error("Failed to add participant:", { + logger.error("Failed to add participant:", { error: error instanceof Error ? error.message : String(error), userId, @@ -1081,7 +1081,7 @@ export class DrizzleDatabaseAdapter return result.length > 0; } catch (error) { - elizaLogger.error("Failed to remove participant:", { + logger.error("Failed to remove participant:", { error: error instanceof Error ? error.message : String(error), userId, @@ -1183,7 +1183,7 @@ export class DrizzleDatabaseAdapter userId: params.userA, }); - elizaLogger.debug("Relationship created successfully:", { + logger.debug("Relationship created successfully:", { relationshipId, userA: params.userA, userB: params.userB, @@ -1194,7 +1194,7 @@ export class DrizzleDatabaseAdapter // Check for unique constraint violation or other specific errors if ((error as { code?: string }).code === "23505") { // Unique violation - elizaLogger.warn("Relationship already exists:", { + logger.warn("Relationship already exists:", { userA: params.userA, userB: params.userB, error: @@ -1203,7 +1203,7 @@ export class DrizzleDatabaseAdapter : String(error), }); } else { - elizaLogger.error("Failed to create relationship:", { + logger.error("Failed to create relationship:", { userA: params.userA, userB: params.userB, error: @@ -1248,13 +1248,13 @@ export class DrizzleDatabaseAdapter return result[0] as unknown as Relationship; } - elizaLogger.debug("No relationship found between users:", { + logger.debug("No relationship found between users:", { userA: params.userA, userB: params.userB, }); return null; } catch (error) { - elizaLogger.error("Error fetching relationship:", { + logger.error("Error fetching relationship:", { userA: params.userA, userB: params.userB, error: @@ -1282,14 +1282,14 @@ export class DrizzleDatabaseAdapter ) .orderBy(desc(relationshipTable.createdAt)); - elizaLogger.debug("Retrieved relationships:", { + logger.debug("Retrieved relationships:", { userId: params.userId, count: result.length, }); return result as unknown as Relationship[]; } catch (error) { - elizaLogger.error("Failed to fetch relationships:", { + logger.error("Failed to fetch relationships:", { userId: params.userId, error: error instanceof Error ? error.message : String(error), @@ -1345,7 +1345,7 @@ export class DrizzleDatabaseAdapter .delete(knowledgeTable) .where(eq(knowledgeTable.id, id)); } catch (error) { - elizaLogger.error("Failed to remove knowledge:", { + logger.error("Failed to remove knowledge:", { error: error instanceof Error ? error.message : String(error), id, @@ -1392,7 +1392,7 @@ export class DrizzleDatabaseAdapter return result[0]?.value || undefined; } catch (error) { - elizaLogger.error("Error fetching cache", { + logger.error("Error fetching cache", { error: error instanceof Error ? error.message : String(error), key: params.key, @@ -1425,7 +1425,7 @@ export class DrizzleDatabaseAdapter }); return true; } catch (error) { - elizaLogger.error("Error setting cache", { + logger.error("Error setting cache", { error: error instanceof Error ? error.message : String(error), key: params.key, @@ -1452,7 +1452,7 @@ export class DrizzleDatabaseAdapter ); return true; } catch (error) { - elizaLogger.error("Error deleting cache", { + logger.error("Error deleting cache", { error: error instanceof Error ? error.message : String(error), key: params.key, diff --git a/packages/plugin-drizzle/src/migrations.ts b/packages/plugin-drizzle/src/migrations.ts index 6478227582c..4e31834a063 100644 --- a/packages/plugin-drizzle/src/migrations.ts +++ b/packages/plugin-drizzle/src/migrations.ts @@ -3,7 +3,7 @@ import { fileURLToPath } from 'url'; import path from "path"; import { drizzle } from "drizzle-orm/node-postgres"; import { Pool } from "pg"; -import { elizaLogger } from "@elizaos/core"; +import { logger } from "@elizaos/core"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -14,9 +14,9 @@ export async function runMigrations(pgPool: Pool): Promise { await migrate(db, { migrationsFolder: path.resolve(__dirname, "../drizzle/migrations"), }); - elizaLogger.info("Migrations completed successfully!"); + logger.info("Migrations completed successfully!"); } catch (error) { - elizaLogger.error("Failed to run database migrations:", error); + logger.error("Failed to run database migrations:", error); throw error; } } diff --git a/packages/plugin-local-ai/echogarden.d.ts b/packages/plugin-local-ai/echogarden.d.ts new file mode 100644 index 00000000000..b678af4e0a3 --- /dev/null +++ b/packages/plugin-local-ai/echogarden.d.ts @@ -0,0 +1,20 @@ +declare module "echogarden" { + interface SynthesizeOptions { + engine: string; + voice: string; + } + + interface RawAudio { + audioChannels: { buffer: ArrayBuffer }[]; + sampleRate: number; + } + + interface SynthesizeResult { + audio: Buffer | RawAudio; + } + + export function synthesize( + text: string, + options: SynthesizeOptions + ): Promise; +} diff --git a/packages/plugin-local-ai/environment.ts b/packages/plugin-local-ai/environment.ts new file mode 100644 index 00000000000..c218e7e1ae6 --- /dev/null +++ b/packages/plugin-local-ai/environment.ts @@ -0,0 +1,36 @@ +import type { IAgentRuntime } from "@elizaos/core"; +import { z } from "zod"; + +export const nodeEnvSchema = z.object({ + VITS_VOICE: z.string().optional(), + VITS_MODEL: z.string().optional(), +}); + +export type NodeConfig = z.infer; + +export async function validateNodeConfig( + runtime: IAgentRuntime +): Promise { + try { + const voiceSettings = runtime.character.settings?.voice; + + // Only include what's absolutely required + const config = { + // VITS settings + VITS_VOICE: voiceSettings?.model || process.env.VITS_VOICE, + VITS_MODEL: process.env.VITS_MODEL, + }; + + return nodeEnvSchema.parse(config); + } catch (error) { + if (error instanceof z.ZodError) { + const errorMessages = error.errors + .map((err) => `${err.path.join(".")}: ${err.message}`) + .join("\n"); + throw new Error( + `Node configuration validation failed:\n${errorMessages}` + ); + } + throw error; + } +} diff --git a/packages/plugin-local-ai/package.json b/packages/plugin-local-ai/package.json index c436e8a70d0..3f1e0c6f3b1 100644 --- a/packages/plugin-local-ai/package.json +++ b/packages/plugin-local-ai/package.json @@ -23,7 +23,59 @@ "@huggingface/transformers": "^3.3.3", "fastembed": "^1.14.1", "tsup": "8.3.5", - "zod": "3.21.4" + "zod": "3.21.4", + "@aws-sdk/client-s3": "^3.705.0", + "@aws-sdk/s3-request-presigner": "^3.705.0", + "@cliqz/adblocker-playwright": "1.34.0", + "@echogarden/espeak-ng-emscripten": "0.3.3", + "@echogarden/kissfft-wasm": "0.2.0", + "@echogarden/speex-resampler-wasm": "0.2.1", + "@opendocsg/pdf2md": "0.1.32", + "@types/uuid": "10.0.0", + "alawmulaw": "6.0.0", + "bignumber.js": "9.1.2", + "capsolver-npm": "2.0.2", + "cldr-segmentation": "2.2.1", + "command-exists": "1.2.9", + "csv-writer": "1.6.0", + "echogarden": "2.0.7", + "espeak-ng": "1.0.2", + "ffmpeg-static": "5.2.0", + "fluent-ffmpeg": "2.1.3", + "formdata-node": "6.0.3", + "fs-extra": "11.2.0", + "gaxios": "6.7.1", + "glob": "11.0.0", + "graceful-fs": "4.2.11", + "html-escaper": "3.0.3", + "html-to-text": "9.0.5", + "import-meta-resolve": "4.1.0", + "jieba-wasm": "2.2.0", + "json5": "2.2.3", + "kuromoji": "0.1.2", + "libsodium-wrappers": "0.7.15", + "multer": "1.4.5-lts.1", + "node-cache": "5.1.2", + "node-llama-cpp": "3.1.1", + "nodejs-whisper": "0.1.18", + "onnxruntime-node": "1.20.1", + "pdfjs-dist": "4.7.76", + "playwright": "1.48.2", + "pm2": "5.4.3", + "puppeteer-extra": "3.3.6", + "puppeteer-extra-plugin-capsolver": "2.0.1", + "sharp": "0.33.5", + "srt": "0.0.3", + "systeminformation": "5.23.8", + "tar": "7.4.3", + "tinyld": "1.3.4", + "uuid": "11.0.3", + "wav": "1.0.2", + "wav-encoder": "1.3.0", + "wavefile": "11.0.0", + "yargs": "17.7.2", + "youtube-dl-exec": "3.0.15", + "cookie": "0.7.0" }, "scripts": { "build": "tsup --format esm --dts", diff --git a/packages/plugin-local-ai/src/index.ts b/packages/plugin-local-ai/src/index.ts index 01938022d35..79b4676584a 100644 --- a/packages/plugin-local-ai/src/index.ts +++ b/packages/plugin-local-ai/src/index.ts @@ -1,110 +1,448 @@ -import { ModelClass, Plugin, logger } from "@elizaos/core"; -import { AutoTokenizer } from "@huggingface/transformers"; +import { type IAgentRuntime, logger, ModelClass, type Plugin } from "@elizaos/core"; +import { + AutoProcessor, + AutoTokenizer, + env, + Florence2ForConditionalGeneration, + type Florence2Processor, + type PreTrainedModel, + type PreTrainedTokenizer, + RawImage, + type Tensor, +} from "@huggingface/transformers"; +import { exec } from "child_process"; +import * as Echogarden from "echogarden"; import { EmbeddingModel, FlagEmbedding } from "fastembed"; -import path from "node:path"; +import fs from "fs"; +import { + getLlama, + type Llama, + LlamaChatSession, + type LlamaChatSessionRepeatPenalty, + type LlamaContext, + type LlamaContextSequence, + type LlamaModel +} from "node-llama-cpp"; +import { nodewhisper } from "nodejs-whisper"; +import os from "os"; +import path from "path"; +import { PassThrough, Readable } from "stream"; import { fileURLToPath } from "url"; +import { promisify } from "util"; import { z } from "zod"; -// Configuration schema for the local AI plugin +const execAsync = promisify(exec); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Configuration schema const configSchema = z.object({ LLAMALOCAL_PATH: z.string().optional(), OLLAMA_MODEL: z.string().optional(), TOKENIZER_MODEL: z.string().optional().default("gpt-4o"), CACHE_DIR: z.string().optional().default("./cache"), + VITS_VOICE: z.string().optional(), + VITS_MODEL: z.string().optional(), }); +// Utility functions +function getWavHeader( + audioLength: number, + sampleRate: number, + channelCount = 1, + bitsPerSample = 16 +): Buffer { + const wavHeader = Buffer.alloc(44); + wavHeader.write("RIFF", 0); + wavHeader.writeUInt32LE(36 + audioLength, 4); + wavHeader.write("WAVE", 8); + wavHeader.write("fmt ", 12); + wavHeader.writeUInt32LE(16, 16); + wavHeader.writeUInt16LE(1, 20); + wavHeader.writeUInt16LE(channelCount, 22); + wavHeader.writeUInt32LE(sampleRate, 24); + wavHeader.writeUInt32LE((sampleRate * bitsPerSample * channelCount) / 8, 28); + wavHeader.writeUInt16LE((bitsPerSample * channelCount) / 8, 32); + wavHeader.writeUInt16LE(bitsPerSample, 34); + wavHeader.write("data", 36); + wavHeader.writeUInt32LE(audioLength, 40); + return wavHeader; +} + +function prependWavHeader( + readable: Readable, + audioLength: number, + sampleRate: number, + channelCount = 1, + bitsPerSample = 16 +): Readable { + const wavHeader = getWavHeader(audioLength, sampleRate, channelCount, bitsPerSample); + let pushedHeader = false; + const passThrough = new PassThrough(); + readable.on("data", (data) => { + if (!pushedHeader) { + passThrough.push(wavHeader); + pushedHeader = true; + } + passThrough.push(data); + }); + readable.on("end", () => { + passThrough.end(); + }); + return passThrough; +} + +// Words to punish in LLM responses +const wordsToPunish = [ + " please", " feel", " free", "!", "–", "—", "?", ".", ",", "; ", + " cosmos", " tapestry", " tapestries", " glitch", " matrix", " cyberspace", + " troll", " questions", " topics", " discuss", " basically", " simulation", + " simulate", " universe", " like", " debug", " debugging", " wild", + " existential", " juicy", " circuits", " help", " ask", " happy", " just", + " cosmic", " cool", " joke", " punchline", " fancy", " glad", " assist", + " algorithm", " Indeed", " Furthermore", " However", " Notably", " Therefore" +]; + class LocalAIManager { + private static instance: LocalAIManager | null = null; + private llama: Llama | undefined; + private model: LlamaModel | undefined; + private modelPath: string; + private grammar: any; + private ctx: LlamaContext | undefined; + private sequence: LlamaContextSequence | undefined; tokenizer: any; - embeddingModel: FlagEmbedding | null; + private embeddingModel: FlagEmbedding | null = null; + private embeddingInitPromise: Promise | null = null; + private embeddingInitLock = false; + private florenceModel: PreTrainedModel | null = null; + private florenceProcessor: Florence2Processor | null = null; + private florenceTokenizer: PreTrainedTokenizer | null = null; + private isCudaAvailable = false; + private CONTENT_CACHE_DIR: string; + private TARGET_SAMPLE_RATE = 16000; + + constructor() { + const modelName = "model.gguf"; + this.modelPath = path.join(process.env.LLAMALOCAL_PATH?.trim() ?? "./", modelName); + this.CONTENT_CACHE_DIR = path.join(__dirname, "../../content_cache"); + this.ensureCacheDirectoryExists(); + this.detectCuda(); + } + + private ensureCacheDirectoryExists() { + if (!fs.existsSync(this.CONTENT_CACHE_DIR)) { + fs.mkdirSync(this.CONTENT_CACHE_DIR, { recursive: true }); + } + } + + private async detectCuda() { + const platform = os.platform(); + if (platform === "linux") { + try { + fs.accessSync("/usr/local/cuda/bin/nvcc", fs.constants.X_OK); + this.isCudaAvailable = true; + logger.log("CUDA detected. Acceleration available."); + } catch { + logger.log("CUDA not detected. Using CPU only."); + } + } else if (platform === "win32") { + const cudaPath = process.env.CUDA_PATH || "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v11.0"; + if (fs.existsSync(path.join(cudaPath, "bin", "nvcc.exe"))) { + this.isCudaAvailable = true; + logger.log("CUDA detected. Acceleration available."); + } + } + } + + async initialize() { + await this.initializeLlama(); + await this.initializeFlorence(); + await this.initializeTokenizer(); + await this.initializeEmbedding(); + } + + private async initializeLlama() { + try { + if (!fs.existsSync(this.modelPath)) { + logger.info("Downloading LLaMA model..."); + // Add model download logic here + } + + this.llama = await getLlama({ + gpu: this.isCudaAvailable ? "cuda" : undefined, + }); + + this.model = await this.llama.loadModel({ + modelPath: this.modelPath, + }); + + this.ctx = await this.model.createContext({ contextSize: 8192 }); + this.sequence = this.ctx.getSequence(); + + logger.success("LLaMA initialization complete"); + } catch (error) { + logger.error("LLaMA initialization failed:", error); + throw error; + } + } + + private async initializeFlorence() { + try { + env.allowLocalModels = false; + env.allowRemoteModels = true; + env.backends.onnx.logLevel = "fatal"; + const modelId = "onnx-community/Florence-2-base-ft"; + + logger.info("Downloading Florence model..."); + this.florenceModel = await Florence2ForConditionalGeneration.from_pretrained(modelId, { + device: "gpu", + progress_callback: (progress) => { + if (progress.status === "download") { + const percent = (((progress as any).loaded / (progress as any).total) * 100).toFixed(1); + const dots = ".".repeat(Math.floor(Number(percent) / 5)); + logger.info(`Downloading Florence model: [${dots.padEnd(20, " ")}] ${percent}%`); + } + }, + }); + + logger.info("Downloading processor..."); + this.florenceProcessor = await AutoProcessor.from_pretrained(modelId, { + device: "gpu", + progress_callback: (progress) => { + if (progress.status === "download") { + const percent = ((progress.loaded / progress.total) * 100).toFixed(1); + const dots = ".".repeat(Math.floor(Number(percent) / 5)); + logger.info(`Downloading Florence model: [${dots.padEnd(20, " ")}] ${percent}%`); + } + }, + }) as Florence2Processor; + + logger.info("Downloading tokenizer..."); + this.florenceTokenizer = await AutoTokenizer.from_pretrained(modelId); + + logger.success("Florence initialization complete"); + } catch (error) { + logger.error("Florence initialization failed:", error); + throw error; + } + } + + private async initializeEmbedding(): Promise { + // If already initialized, return immediately + if (this.embeddingModel) { + return; + } + + // If initialization is in progress, wait for it + if (this.embeddingInitPromise) { + return this.embeddingInitPromise; + } + + // Use a lock to prevent multiple simultaneous initializations + if (this.embeddingInitLock) { + // Wait for current initialization to complete + while (this.embeddingInitLock) { + await new Promise((resolve) => setTimeout(resolve, 100)); + } + return; + } - async initializeEmbeddingModel() { - if (this.embeddingModel) return; + this.embeddingInitLock = true; + + try { + this.embeddingInitPromise = this.initializeEmbeddingModel(); + await this.embeddingInitPromise; + } finally { + this.embeddingInitLock = false; + this.embeddingInitPromise = null; + } + } + private async initializeEmbeddingModel(): Promise { try { - // Get cache directory - const __filename = fileURLToPath(import.meta.url); - const __dirname = path.dirname(__filename); - const cacheDir = path.resolve( - __dirname, - process.env.CACHE_DIR || "./cache" - ); - - // Ensure cache directory exists - const fs = await import("fs"); + const cacheDir = path.resolve(__dirname, process.env.CACHE_DIR || "./cache"); if (!fs.existsSync(cacheDir)) { fs.mkdirSync(cacheDir, { recursive: true }); } logger.debug("Initializing BGE embedding model..."); - this.embeddingModel = await FlagEmbedding.init({ cacheDir: cacheDir, model: EmbeddingModel.BGESmallENV15, maxLength: 512, }); + + logger.success("Embedding model initialization complete"); + } catch (error) { + logger.error("Embedding initialization failed:", error); + throw error; + } + } - logger.debug("BGE model initialized successfully"); + // LLaMA text generation + async generateText(context: string, temperature: number = 0.7, stopSequences: string[] = []): Promise { + if (!this.sequence) { + throw new Error("LLaMA model not initialized"); + } + + const session = new LlamaChatSession({ contextSequence: this.sequence }); + const wordsToPunishTokens = wordsToPunish.flatMap((word) => this.model!.tokenize(word)); + + const repeatPenalty: LlamaChatSessionRepeatPenalty = { + punishTokensFilter: () => wordsToPunishTokens, + penalty: 1.2, + frequencyPenalty: 0.7, + presencePenalty: 0.7, + }; + + const response = await session.prompt(context, { + temperature: temperature, + repeatPenalty: repeatPenalty, + }); + + await this.sequence.clearHistory(); + return response || ""; + } + + private async initializeTokenizer() { + try { + const tokenizerModel = process.env.TOKENIZER_MODEL || "gpt-4o"; + this.tokenizer = await AutoTokenizer.from_pretrained(tokenizerModel); + logger.success(`Tokenizer initialized with model: ${tokenizerModel}`); } catch (error) { - logger.error("Failed to initialize BGE model:", error); + logger.error("Tokenizer initialization failed:", error); throw error; } } + // Embedding generation async generateEmbedding(text: string): Promise { if (!this.embeddingModel) { - await this.initializeEmbeddingModel(); + await this.initializeEmbedding(); + throw new Error("Embedding model not initialized"); } + const embedding = await this.embeddingModel.queryEmbed(text); + return Array.from(embedding); + } + + // Image description + async describeImage(imageData: Buffer, mimeType: string): Promise<{ title: string; description: string }> { + if (!this.florenceModel || !this.florenceProcessor || !this.florenceTokenizer) { + throw new Error("Florence model not initialized"); + } + + const blob = new Blob([imageData], { type: mimeType }); + const image = await RawImage.fromBlob(blob); + const visionInputs = await this.florenceProcessor(image); + const prompts = this.florenceProcessor.construct_prompts(""); + const textInputs = this.florenceTokenizer(prompts); + + const generatedIds = await this.florenceModel.generate({ + ...textInputs, + ...visionInputs, + max_new_tokens: 256, + }) as Tensor; + + const generatedText = this.florenceTokenizer.batch_decode(generatedIds, { + skip_special_tokens: false, + })[0]; + + const result = this.florenceProcessor.post_process_generation( + generatedText, + "", + image.size + ); + + const detailedCaption = result[""] as string; + return { title: detailedCaption, description: detailedCaption }; + } + + // Audio transcription + async transcribeAudio(audioBuffer: ArrayBuffer): Promise { try { - const embedding = await this.embeddingModel!.queryEmbed(text); - return this.processEmbedding(embedding); + if (audioBuffer.byteLength < 0.2 * 16000) { + return null; + } + + const arrayBuffer = new Uint8Array(audioBuffer).buffer; + const tempWavFile = path.join(this.CONTENT_CACHE_DIR, `temp_${Date.now()}.wav`); + fs.writeFileSync(tempWavFile, Buffer.from(arrayBuffer)); + + const output = await nodewhisper(tempWavFile, { + modelName: "base.en", + autoDownloadModelName: "base.en", + verbose: false, + withCuda: this.isCudaAvailable, + whisperOptions: { + outputInText: true, + translateToEnglish: false, + }, + }); + + fs.unlinkSync(tempWavFile); + + if (!output || output.length < 5) { + return null; + } + + return output.split("\n") + .map(line => line.trim().startsWith("[") ? line.substring(line.indexOf("]") + 1) : line) + .join("\n"); } catch (error) { - logger.error("Embedding generation failed:", error); - throw error; + logger.error("Transcription error:", error); + return null; } } - processEmbedding(embedding: number[]): number[] { - let finalEmbedding: number[]; + // Text to speech + async generateSpeech(runtime: IAgentRuntime, text: string): Promise { + try { + const voiceSettings = runtime.character.settings?.voice; + + const vitsVoice = voiceSettings?.model || process.env.VITS_VOICE || "en_US-hfc_female-medium"; + const { audio } = await Echogarden.synthesize(text, { + engine: "vits", + voice: vitsVoice, + }); - if ( - ArrayBuffer.isView(embedding) && - embedding.constructor === Float32Array - ) { - finalEmbedding = Array.from(embedding); - } else if ( - Array.isArray(embedding) && - ArrayBuffer.isView(embedding[0]) && - embedding[0].constructor === Float32Array - ) { - finalEmbedding = Array.from(embedding[0]); - } else if (Array.isArray(embedding)) { - finalEmbedding = embedding; - } else { - throw new Error(`Unexpected embedding format: ${typeof embedding}`); + return this.processVitsAudio(audio); + } catch (error) { + logger.error("Speech generation error:", error); + throw error; } + } - finalEmbedding = finalEmbedding.map((n) => Number(n)); - - if (!Array.isArray(finalEmbedding) || finalEmbedding[0] === undefined) { - throw new Error( - "Invalid embedding format: must be an array starting with a number" - ); + private async processVitsAudio(audio: any): Promise { + if (audio instanceof Buffer) { + return Readable.from(audio); } - if (finalEmbedding.length !== 384) { - logger.warn(`Unexpected embedding dimension: ${finalEmbedding.length}`); + if ("audioChannels" in audio && "sampleRate" in audio) { + const floatBuffer = Buffer.from(audio.audioChannels[0].buffer); + const floatArray = new Float32Array(floatBuffer.buffer); + const pcmBuffer = new Int16Array(floatArray.length); + + for (let i = 0; i < floatArray.length; i++) { + pcmBuffer[i] = Math.round(floatArray[i] * 32767); + } + + const wavHeaderBuffer = getWavHeader(pcmBuffer.length * 2, audio.sampleRate, 1, 16); + const wavBuffer = Buffer.concat([wavHeaderBuffer, Buffer.from(pcmBuffer.buffer)]); + return Readable.from(wavBuffer); } - return finalEmbedding; + throw new Error("Unsupported audio format"); } } +// Create manager instance const localAIManager = new LocalAIManager(); export const localAIPlugin: Plugin = { name: "local-ai", - description: "Local AI plugin using LLaMA, AutoTokenizer and FastEmbed", + description: "Local AI plugin using LLaMA, Florence, and other local models", async init(config: Record) { try { @@ -113,21 +451,11 @@ export const localAIPlugin: Plugin = { if (value) process.env[key] = value; }); - // Initialize tokenizer - const tokenizerModel = validatedConfig.TOKENIZER_MODEL; - localAIManager.tokenizer = await AutoTokenizer.from_pretrained( - tokenizerModel - ); - logger.info(`Initialized AutoTokenizer with model: ${tokenizerModel}`); - - // Pre-initialize embedding model - await localAIManager.initializeEmbeddingModel(); + await localAIManager.initialize(); } catch (error) { if (error instanceof z.ZodError) { throw new Error( - `Invalid plugin configuration: ${error.errors - .map((e) => e.message) - .join(", ")}` + `Invalid plugin configuration: ${error.errors.map((e) => e.message).join(", ")}` ); } throw error; @@ -135,49 +463,25 @@ export const localAIPlugin: Plugin = { }, models: { - // Text generation for small tasks - [ModelClass.TEXT_SMALL]: async ( - runtime, - { - context, - stopSequences = [], - }) => { + [ModelClass.TEXT_SMALL]: async (runtime, { context, stopSequences = [], temperature = 0.7 }) => { try { - const modelPath = process.env.LLAMALOCAL_PATH || "./model.gguf"; - - // TODO: implement hermes here - - - + return await localAIManager.generateText(context, temperature, stopSequences); } catch (error) { logger.error("Error in TEXT_SMALL handler:", error); throw error; } }, - // Text generation for larger tasks - [ModelClass.TEXT_LARGE]: async ( - runtime, - { - context, - stopSequences = [], - }) => { + [ModelClass.TEXT_LARGE]: async (runtime, { context, stopSequences = [], temperature = 0.7 }) => { try { - const modelPath = process.env.LLAMALOCAL_PATH || "./model.gguf"; - - // TODO: implement hermes - + return await localAIManager.generateText(context, temperature, stopSequences); } catch (error) { logger.error("Error in TEXT_LARGE handler:", error); throw error; } }, - // Text embedding using FastEmbed - [ModelClass.TEXT_EMBEDDING]: async ( - runtime, - { text } - ) => { + [ModelClass.TEXT_EMBEDDING]: async (runtime, text) => { try { return await localAIManager.generateEmbedding(text); } catch (error) { @@ -186,15 +490,8 @@ export const localAIPlugin: Plugin = { } }, - // Text tokenization using AutoTokenizer - [ModelClass.TEXT_TOKENIZER_ENCODE]: async ( - runtime, - { text } - ) => { + [ModelClass.TEXT_TOKENIZER_ENCODE]: async (runtime, { text }) => { try { - if (!localAIManager.tokenizer) { - throw new Error("Tokenizer not initialized"); - } return await localAIManager.tokenizer.encode(text); } catch (error) { logger.error("Error in TEXT_TOKENIZER_ENCODE handler:", error); @@ -202,15 +499,8 @@ export const localAIPlugin: Plugin = { } }, - // Text detokenization using AutoTokenizer - [ModelClass.TEXT_TOKENIZER_DECODE]: async ( - runtime, - { tokens } - ) => { + [ModelClass.TEXT_TOKENIZER_DECODE]: async (runtime, { tokens }) => { try { - if (!localAIManager.tokenizer) { - throw new Error("Tokenizer not initialized"); - } return await localAIManager.tokenizer.decode(tokens); } catch (error) { logger.error("Error in TEXT_TOKENIZER_DECODE handler:", error); @@ -218,19 +508,39 @@ export const localAIPlugin: Plugin = { } }, - // Image description using local Florence model [ModelClass.IMAGE_DESCRIPTION]: async (runtime, imageUrl) => { try { - - // TODO: Add florence - - + const response = await fetch(imageUrl); + if (!response.ok) { + throw new Error(`Failed to fetch image: ${response.statusText}`); + } + const imageBuffer = Buffer.from(await response.arrayBuffer()); + const mimeType = response.headers.get("content-type") || "image/jpeg"; + return await localAIManager.describeImage(imageBuffer, mimeType); } catch (error) { logger.error("Error in IMAGE_DESCRIPTION handler:", error); throw error; } }, - }, + + [ModelClass.TRANSCRIPTION]: async (runtime, audioBuffer) => { + try { + return await localAIManager.transcribeAudio(audioBuffer); + } catch (error) { + logger.error("Error in TRANSCRIPTION handler:", error); + throw error; + } + }, + + [ModelClass.TEXT_TO_SPEECH]: async (runtime, text) => { + try { + return await localAIManager.generateSpeech(runtime, text); + } catch (error) { + logger.error("Error in SPEECH_GENERATION handler:", error); + throw error; + } + } + } }; -export default localAIPlugin; +export default localAIPlugin; \ No newline at end of file diff --git a/packages/plugin-node/package.json b/packages/plugin-node/package.json index eb1137912bb..aaac96d5a18 100644 --- a/packages/plugin-node/package.json +++ b/packages/plugin-node/package.json @@ -30,7 +30,6 @@ "@echogarden/kissfft-wasm": "0.2.0", "@echogarden/speex-resampler-wasm": "0.2.1", "@elizaos/core": "workspace:*", - "@huggingface/transformers": "3.0.2", "@opendocsg/pdf2md": "0.1.32", "@types/uuid": "10.0.0", "alawmulaw": "6.0.0", diff --git a/packages/plugin-tee/src/providers/deriveKeyProvider.ts b/packages/plugin-tee/src/providers/deriveKeyProvider.ts index 2e41b14010a..f3e5681ad7a 100644 --- a/packages/plugin-tee/src/providers/deriveKeyProvider.ts +++ b/packages/plugin-tee/src/providers/deriveKeyProvider.ts @@ -3,7 +3,7 @@ import { type Memory, type Provider, type State, - elizaLogger, + logger, } from "@elizaos/core"; import { Keypair } from "@solana/web3.js"; import crypto from "crypto"; @@ -24,19 +24,19 @@ class DeriveKeyProvider { switch (teeMode) { case TEEMode.LOCAL: endpoint = "http://localhost:8090"; - elizaLogger.log( + logger.log( "TEE: Connecting to local simulator at localhost:8090" ); break; case TEEMode.DOCKER: endpoint = "http://host.docker.internal:8090"; - elizaLogger.log( + logger.log( "TEE: Connecting to simulator via Docker at host.docker.internal:8090" ); break; case TEEMode.PRODUCTION: endpoint = undefined; - elizaLogger.log( + logger.log( "TEE: Running in production mode without simulator" ); break; @@ -61,11 +61,11 @@ class DeriveKeyProvider { subject, }; const reportdata = JSON.stringify(deriveKeyData); - elizaLogger.log( + logger.log( "Generating Remote Attestation Quote for Derive Key..." ); const quote = await this.raProvider.generateAttestation(reportdata); - elizaLogger.log("Remote Attestation Quote generated successfully!"); + logger.log("Remote Attestation Quote generated successfully!"); return quote; } @@ -81,18 +81,18 @@ class DeriveKeyProvider { ): Promise { try { if (!path || !subject) { - elizaLogger.error( + logger.error( "Path and Subject are required for key derivation" ); } - elizaLogger.log("Deriving Raw Key in TEE..."); + logger.log("Deriving Raw Key in TEE..."); const derivedKey = await this.client.deriveKey(path, subject); - elizaLogger.log("Raw Key Derived Successfully!"); + logger.log("Raw Key Derived Successfully!"); return derivedKey; } catch (error) { - elizaLogger.error("Error deriving raw key:", error); + logger.error("Error deriving raw key:", error); throw error; } } @@ -111,12 +111,12 @@ class DeriveKeyProvider { ): Promise<{ keypair: Keypair; attestation: RemoteAttestationQuote }> { try { if (!path || !subject) { - elizaLogger.error( + logger.error( "Path and Subject are required for key derivation" ); } - elizaLogger.log("Deriving Key in TEE..."); + logger.log("Deriving Key in TEE..."); const derivedKey = await this.client.deriveKey(path, subject); const uint8ArrayDerivedKey = derivedKey.asUint8Array(); @@ -131,11 +131,11 @@ class DeriveKeyProvider { agentId, keypair.publicKey.toBase58() ); - elizaLogger.log("Key Derived Successfully!"); + logger.log("Key Derived Successfully!"); return { keypair, attestation }; } catch (error) { - elizaLogger.error("Error deriving key:", error); + logger.error("Error deriving key:", error); throw error; } } @@ -157,12 +157,12 @@ class DeriveKeyProvider { }> { try { if (!path || !subject) { - elizaLogger.error( + logger.error( "Path and Subject are required for key derivation" ); } - elizaLogger.log("Deriving ECDSA Key in TEE..."); + logger.log("Deriving ECDSA Key in TEE..."); const deriveKeyResponse: DeriveKeyResponse = await this.client.deriveKey(path, subject); const hex = keccak256(deriveKeyResponse.asUint8Array()); @@ -173,11 +173,11 @@ class DeriveKeyProvider { agentId, keypair.address ); - elizaLogger.log("ECDSA Key Derived Successfully!"); + logger.log("ECDSA Key Derived Successfully!"); return { keypair, attestation }; } catch (error) { - elizaLogger.error("Error deriving ecdsa key:", error); + logger.error("Error deriving ecdsa key:", error); throw error; } } @@ -191,7 +191,7 @@ const deriveKeyProvider: Provider = { try { // Validate wallet configuration if (!runtime.getSetting("WALLET_SECRET_SALT")) { - elizaLogger.error( + logger.error( "Wallet secret salt is not configured in settings" ); return ""; @@ -215,11 +215,11 @@ const deriveKeyProvider: Provider = { evm: evmKeypair.keypair.address, }); } catch (error) { - elizaLogger.error("Error creating PublicKey:", error); + logger.error("Error creating PublicKey:", error); return ""; } } catch (error) { - elizaLogger.error("Error in derive key provider:", error.message); + logger.error("Error in derive key provider:", error.message); return `Failed to fetch derive key information: ${error instanceof Error ? error.message : "Unknown error"}`; } }, diff --git a/packages/plugin-tee/src/providers/remoteAttestationProvider.ts b/packages/plugin-tee/src/providers/remoteAttestationProvider.ts index 59bfd2bb2b9..0fc676df97a 100644 --- a/packages/plugin-tee/src/providers/remoteAttestationProvider.ts +++ b/packages/plugin-tee/src/providers/remoteAttestationProvider.ts @@ -3,7 +3,7 @@ import { type Memory, type Provider, type State, - elizaLogger, + logger, } from "@elizaos/core"; import { type TdxQuoteResponse, TappdClient, type TdxQuoteHashAlgorithms } from "@phala/dstack-sdk"; import { type RemoteAttestationQuote, TEEMode, type RemoteAttestationMessage } from "@elizaos/core"; @@ -18,19 +18,19 @@ class RemoteAttestationProvider { switch (teeMode) { case TEEMode.LOCAL: endpoint = "http://localhost:8090"; - elizaLogger.log( + logger.log( "TEE: Connecting to local simulator at localhost:8090" ); break; case TEEMode.DOCKER: endpoint = "http://host.docker.internal:8090"; - elizaLogger.log( + logger.log( "TEE: Connecting to simulator via Docker at host.docker.internal:8090" ); break; case TEEMode.PRODUCTION: endpoint = undefined; - elizaLogger.log( + logger.log( "TEE: Running in production mode without simulator" ); break; @@ -48,18 +48,18 @@ class RemoteAttestationProvider { hashAlgorithm?: TdxQuoteHashAlgorithms ): Promise { try { - elizaLogger.log("Generating attestation for: ", reportData); + logger.log("Generating attestation for: ", reportData); const tdxQuote: TdxQuoteResponse = await this.client.tdxQuote(reportData, hashAlgorithm); const rtmrs = tdxQuote.replayRtmrs(); - elizaLogger.log( + logger.log( `rtmr0: ${rtmrs[0]}\nrtmr1: ${rtmrs[1]}\nrtmr2: ${rtmrs[2]}\nrtmr3: ${rtmrs[3]}f` ); const quote: RemoteAttestationQuote = { quote: tdxQuote.quote, timestamp: Date.now(), }; - elizaLogger.log("Remote attestation quote: ", quote); + logger.log("Remote attestation quote: ", quote); return quote; } catch (error) { console.error("Error generating remote attestation:", error); @@ -89,7 +89,7 @@ const remoteAttestationProvider: Provider = { content: message.content.text, } }; - elizaLogger.log("Generating attestation for: ", JSON.stringify(attestationMessage)); + logger.log("Generating attestation for: ", JSON.stringify(attestationMessage)); const attestation = await provider.generateAttestation(JSON.stringify(attestationMessage)); return `Your Agent's remote attestation is: ${JSON.stringify(attestation)}`; } catch (error) { diff --git a/packages/plugin-tee/src/providers/walletProvider.ts b/packages/plugin-tee/src/providers/walletProvider.ts index 6f0f6dddb02..b81c2ce13a9 100644 --- a/packages/plugin-tee/src/providers/walletProvider.ts +++ b/packages/plugin-tee/src/providers/walletProvider.ts @@ -4,7 +4,7 @@ import { type Memory, type Provider, type State, - elizaLogger, + logger, } from "@elizaos/core"; import { Connection, type Keypair, type PublicKey } from "@solana/web3.js"; import BigNumber from "bignumber.js"; @@ -97,7 +97,7 @@ export class WalletProvider { const data = await response.json(); return data; } catch (error) { - elizaLogger.error(`Attempt ${i + 1} failed:`, error); + logger.error(`Attempt ${i + 1} failed:`, error); lastError = error; if (i < PROVIDER_CONFIG.MAX_RETRIES - 1) { const delay = PROVIDER_CONFIG.RETRY_DELAY * Math.pow(2, i); @@ -107,7 +107,7 @@ export class WalletProvider { } } - elizaLogger.error( + logger.error( "All attempts failed. Throwing the last error:", lastError ); @@ -120,10 +120,10 @@ export class WalletProvider { const cachedValue = this.cache.get(cacheKey); if (cachedValue) { - elizaLogger.log("Cache hit for fetchPortfolioValue"); + logger.log("Cache hit for fetchPortfolioValue"); return cachedValue; } - elizaLogger.log("Cache miss for fetchPortfolioValue"); + logger.log("Cache miss for fetchPortfolioValue"); const walletData = await this.fetchWithRetry( runtime, @@ -131,7 +131,7 @@ export class WalletProvider { ); if (!walletData?.success || !walletData?.data) { - elizaLogger.error("No portfolio data available", walletData); + logger.error("No portfolio data available", walletData); throw new Error("No portfolio data available"); } @@ -164,7 +164,7 @@ export class WalletProvider { this.cache.set(cacheKey, portfolio); return portfolio; } catch (error) { - elizaLogger.error("Error fetching portfolio:", error); + logger.error("Error fetching portfolio:", error); throw error; } } @@ -175,10 +175,10 @@ export class WalletProvider { const cachedValue = this.cache.get(cacheKey); if (cachedValue) { - elizaLogger.log("Cache hit for fetchPrices"); + logger.log("Cache hit for fetchPrices"); return cachedValue; } - elizaLogger.log("Cache miss for fetchPrices"); + logger.log("Cache miss for fetchPrices"); const { SOL, BTC, ETH } = PROVIDER_CONFIG.TOKEN_ADDRESSES; const tokens = [SOL, BTC, ETH]; @@ -209,7 +209,7 @@ export class WalletProvider { : "ethereum" ].usd = price; } else { - elizaLogger.warn( + logger.warn( `No price data available for token: ${token}` ); } @@ -218,7 +218,7 @@ export class WalletProvider { this.cache.set(cacheKey, prices); return prices; } catch (error) { - elizaLogger.error("Error fetching prices:", error); + logger.error("Error fetching prices:", error); throw error; } } @@ -269,7 +269,7 @@ export class WalletProvider { return this.formatPortfolio(runtime, portfolio, prices); } catch (error) { - elizaLogger.error("Error generating portfolio report:", error); + logger.error("Error generating portfolio report:", error); return "Unable to fetch wallet information. Please try again later."; } } @@ -287,7 +287,7 @@ const walletProvider: Provider = { try { // Validate wallet configuration if (!runtime.getSetting("WALLET_SECRET_SALT")) { - elizaLogger.error( + logger.error( "Wallet secret salt is not configured in settings" ); return ""; @@ -304,9 +304,9 @@ const walletProvider: Provider = { agentId ); publicKey = derivedKeyPair.keypair.publicKey; - elizaLogger.log("Wallet Public Key: ", publicKey.toBase58()); + logger.log("Wallet Public Key: ", publicKey.toBase58()); } catch (error) { - elizaLogger.error("Error creating PublicKey:", error); + logger.error("Error creating PublicKey:", error); return ""; } @@ -316,7 +316,7 @@ const walletProvider: Provider = { const porfolio = await provider.getFormattedPortfolio(runtime); return porfolio; } catch (error) { - elizaLogger.error("Error in wallet provider:", error.message); + logger.error("Error in wallet provider:", error.message); return `Failed to fetch wallet information: ${error instanceof Error ? error.message : "Unknown error"}`; } }, From 048fafa2c36366decca2cb7268c964f74f08da42 Mon Sep 17 00:00:00 2001 From: Shaw Date: Tue, 11 Feb 2025 00:22:48 -0500 Subject: [PATCH 2/3] clean up and lint --- biome.json | 10 +- package.json | 2 +- packages/agent/src/api.ts | 6 +- packages/agent/src/defaultCharacter.ts | 2 +- packages/agent/src/plugins.test.ts | 2 +- packages/agent/src/reply.ts | 14 +- packages/cli/src/commands/agent.ts | 8 +- packages/core/__tests__/knowledge.test.ts | 2 +- packages/core/__tests__/mockCharacter.ts | 2 +- packages/core/__tests__/runtime.test.ts | 12 +- packages/core/src/runtime.ts | 4 +- .../core/src/test_resources/createRuntime.ts | 2 +- packages/plugin-anthropic/biome.json | 40 ++++ packages/plugin-anthropic/src/index.ts | 2 +- packages/plugin-bootstrap/biome.json | 40 ++++ .../plugin-bootstrap/src/providers/facts.ts | 2 +- packages/plugin-discord/biome.json | 40 ++++ packages/plugin-discord/src/actions/reply.ts | 12 +- packages/plugin-discord/src/index.ts | 4 +- packages/plugin-discord/src/messages.ts | 2 +- packages/plugin-discord/src/types.ts | 8 +- packages/plugin-drizzle/biome.json | 40 ++++ packages/plugin-drizzle/src/index.ts | 6 +- packages/plugin-drizzle/src/migrations.ts | 2 +- packages/plugin-local-ai/biome.json | 40 ++++ packages/plugin-local-ai/src/index.ts | 2 +- packages/plugin-node/biome.json | 40 ++++ packages/plugin-openai/biome.json | 40 ++++ packages/plugin-openai/src/index.ts | 8 +- packages/plugin-sqlite/biome.json | 40 ++++ packages/plugin-tee/biome.json | 7 +- .../src/__tests__/deriveKey.test.ts | 22 +-- .../src/__tests__/remoteAttestation.test.ts | 16 +- .../__tests__/remoteAttestationAction.test.ts | 32 ++-- .../plugin-tee/src/__tests__/timeout.test.ts | 54 +++--- packages/plugin-tee/src/adapters/sqliteDAO.ts | 88 ++++++--- packages/plugin-tee/src/index.ts | 41 ++--- .../src/providers/deriveKeyProvider.ts | 132 +++++-------- .../providers/remoteAttestationProvider.ts | 66 +++---- .../src/providers/sgxAttestationProvider.ts | 40 ++-- .../src/providers/walletProvider.ts | 173 +++++++----------- .../plugin-tee/src/services/teeLogManager.ts | 42 +++-- .../plugin-tee/src/services/teeLogService.ts | 59 ++++-- packages/plugin-tee/src/types.ts | 8 +- packages/plugin-telegram/biome.json | 40 ++++ packages/plugin-telegram/src/actions/reply.ts | 12 +- packages/plugin-twitter/biome.json | 40 ++++ packages/plugin-twitter/src/base.ts | 2 +- packages/plugin-twitter/src/client/api.ts | 6 +- .../plugin-twitter/src/client/auth-user.ts | 6 +- packages/plugin-twitter/src/client/auth.ts | 6 +- packages/plugin-twitter/src/client/command.ts | 2 +- packages/plugin-twitter/src/client/grok.ts | 2 +- .../plugin-twitter/src/client/messages.ts | 2 +- .../src/client/platform/index.ts | 2 +- .../src/client/platform/node/index.ts | 2 +- .../plugin-twitter/src/client/profile.test.ts | 2 +- packages/plugin-twitter/src/client/profile.ts | 6 +- .../src/client/relationships.ts | 8 +- .../plugin-twitter/src/client/requests.ts | 2 +- packages/plugin-twitter/src/client/scraper.ts | 36 ++-- .../plugin-twitter/src/client/search.test.ts | 2 +- packages/plugin-twitter/src/client/search.ts | 20 +- packages/plugin-twitter/src/client/spaces.ts | 4 +- .../src/client/spaces/core/ChatClient.ts | 2 +- .../src/client/spaces/core/JanusAudio.ts | 2 +- .../src/client/spaces/core/JanusClient.ts | 6 +- .../src/client/spaces/core/Space.ts | 2 +- .../client/spaces/core/SpaceParticipant.ts | 2 +- .../client/spaces/plugins/HlsRecordPlugin.ts | 6 +- .../spaces/plugins/IdleMonitorPlugin.ts | 8 +- .../spaces/plugins/MonitorAudioPlugin.ts | 4 +- .../spaces/plugins/RecordToDiskPlugin.ts | 8 +- .../src/client/spaces/plugins/SttTtsPlugin.ts | 22 +-- .../plugin-twitter/src/client/spaces/test.ts | 2 +- .../plugin-twitter/src/client/spaces/types.ts | 4 +- .../plugin-twitter/src/client/spaces/utils.ts | 6 +- .../src/client/timeline-async.ts | 4 +- .../src/client/timeline-following.ts | 4 +- .../src/client/timeline-home.ts | 4 +- .../src/client/timeline-list.ts | 6 +- .../src/client/timeline-relationship.ts | 6 +- .../src/client/timeline-search.ts | 10 +- .../src/client/timeline-tweet-util.ts | 8 +- .../plugin-twitter/src/client/timeline-v1.ts | 6 +- .../plugin-twitter/src/client/timeline-v2.ts | 10 +- packages/plugin-twitter/src/client/trends.ts | 4 +- .../plugin-twitter/src/client/tweets.test.ts | 4 +- packages/plugin-twitter/src/client/tweets.ts | 16 +- .../plugin-twitter/src/client/type-util.ts | 4 +- packages/plugin-twitter/src/post.ts | 2 +- packages/plugin-twitter/src/spaces.ts | 2 +- packages/plugin-twitter/src/utils.ts | 2 +- turbo.json | 3 + 94 files changed, 979 insertions(+), 606 deletions(-) create mode 100644 packages/plugin-anthropic/biome.json create mode 100644 packages/plugin-bootstrap/biome.json create mode 100644 packages/plugin-discord/biome.json create mode 100644 packages/plugin-drizzle/biome.json create mode 100644 packages/plugin-local-ai/biome.json create mode 100644 packages/plugin-node/biome.json create mode 100644 packages/plugin-openai/biome.json create mode 100644 packages/plugin-sqlite/biome.json create mode 100644 packages/plugin-telegram/biome.json create mode 100644 packages/plugin-twitter/biome.json diff --git a/biome.json b/biome.json index 7772669d21d..e578c13ad0d 100644 --- a/biome.json +++ b/biome.json @@ -13,12 +13,10 @@ "noPrototypeBuiltins": "warn", "noDuplicateObjectKeys": "warn", "noGlobalIsNan": "warn", - "noDuplicateFontNames": "warn", "noSelfCompare": "warn", "noDoubleEquals": "warn", "noImplicitAnyLet": "warn", "noAssignInExpressions": "warn", - "noExportsInTest": "warn", "noConstEnum": "warn", "noEmptyInterface": "warn" }, @@ -33,11 +31,8 @@ "style": { "useConst": "warn", "useTemplate": "warn", - "useImportType": "warn", - "useNodejsImportProtocol": "warn", "noUselessElse": "warn", "useSelfClosingElements": "warn", - "useNumberNamespace": "warn", "noUnusedTemplateLiteral": "warn", "noInferrableTypes": "warn", "noNonNullAssertion": "warn", @@ -45,12 +40,10 @@ "useDefaultParameterLast": "warn", "useExponentiationOperator": "warn", "noVar": "warn", - "useSingleVarDeclarator": "warn", - "useExportType": "warn" + "useSingleVarDeclarator": "warn" }, "a11y": { "useAltText": "warn", - "useFocusableInteractive": "warn", "useMediaCaption": "warn", "noSvgWithoutTitle": "warn", "useKeyWithClickEvents": "warn" @@ -65,7 +58,6 @@ "noStaticOnlyClass": "warn", "noThisInStatic": "warn", "noUselessConstructor": "warn", - "noUselessTernary": "warn", "noUselessSwitchCase": "warn", "noUselessCatch": "warn" }, diff --git a/package.json b/package.json index 1a2998276ab..adb30f0eae7 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "scripts": { "format": "biome format --write .", "cli": "bun --filter=@elizaos/cli cli", - "lint": "biome lint . --write", + "lint": "turbo run lint:fix --filter=./packages/*", "check": "biome check --apply .", "preinstall": "npx only-allow bun", "build": "turbo run build --filter=./packages/core && turbo run build --filter=./packages/*", diff --git a/packages/agent/src/api.ts b/packages/agent/src/api.ts index 84872a1e627..bfee1123210 100644 --- a/packages/agent/src/api.ts +++ b/packages/agent/src/api.ts @@ -1,11 +1,11 @@ import { type Character, getEnvVariable, - IAgentRuntime, - ITeeLogService, + type IAgentRuntime, + type ITeeLogService, logger, ServiceType, - TeeLogQuery, + type TeeLogQuery, type UUID, validateCharacterConfig, validateUuid diff --git a/packages/agent/src/defaultCharacter.ts b/packages/agent/src/defaultCharacter.ts index 5d7aa5c1446..2f3a570de06 100644 --- a/packages/agent/src/defaultCharacter.ts +++ b/packages/agent/src/defaultCharacter.ts @@ -1,4 +1,4 @@ -import { type Character } from "@elizaos/core"; +import type { Character } from "@elizaos/core"; export const defaultCharacter: Character = { name: "Eliza", diff --git a/packages/agent/src/plugins.test.ts b/packages/agent/src/plugins.test.ts index 460d1bbb086..16618050955 100644 --- a/packages/agent/src/plugins.test.ts +++ b/packages/agent/src/plugins.test.ts @@ -8,7 +8,7 @@ import { DbCacheAdapter, logger, stringToUuid, - TestSuite, + type TestSuite, type IAgentRuntime } from "@elizaos/core"; import { afterAll, beforeAll, describe, it } from 'vitest'; diff --git a/packages/agent/src/reply.ts b/packages/agent/src/reply.ts index 61d91e65af1..d59be455e80 100644 --- a/packages/agent/src/reply.ts +++ b/packages/agent/src/reply.ts @@ -1,10 +1,10 @@ -import { - type Action, - type ActionExample, - type HandlerCallback, - type IAgentRuntime, - type Memory, - type State, +import type { + Action, + ActionExample, + HandlerCallback, + IAgentRuntime, + Memory, + State, } from "@elizaos/core"; const replyAction = { diff --git a/packages/cli/src/commands/agent.ts b/packages/cli/src/commands/agent.ts index 1ebcb5a5808..91a00467ae8 100644 --- a/packages/cli/src/commands/agent.ts +++ b/packages/cli/src/commands/agent.ts @@ -62,7 +62,7 @@ agent try { // If input is a number, get agent ID from index if (!isNaN(Number(agentIdOrIndex))) { - agentIdOrIndex = await getAgentIdFromIndex(parseInt(agentIdOrIndex)) + agentIdOrIndex = await getAgentIdFromIndex(Number.parseInt(agentIdOrIndex)) } const response = await fetch(`${AGENT_RUNTIME_URL}/agents/${agentIdOrIndex}`) @@ -122,7 +122,7 @@ agent .action(async (agentIdOrIndex) => { try { if (!isNaN(Number(agentIdOrIndex))) { - agentIdOrIndex = await getAgentIdFromIndex(parseInt(agentIdOrIndex)) + agentIdOrIndex = await getAgentIdFromIndex(Number.parseInt(agentIdOrIndex)) } const response = await fetch(`${AGENT_RUNTIME_URL}/agents/${agentIdOrIndex}/stop`, { @@ -146,7 +146,7 @@ agent .action(async (agentIdOrIndex) => { try { if (!isNaN(Number(agentIdOrIndex))) { - agentIdOrIndex = await getAgentIdFromIndex(parseInt(agentIdOrIndex)) + agentIdOrIndex = await getAgentIdFromIndex(Number.parseInt(agentIdOrIndex)) } const response = await fetch(`${AGENT_RUNTIME_URL}/agents/${agentIdOrIndex}`, { @@ -171,7 +171,7 @@ agent .action(async (agentIdOrIndex, configPath) => { try { if (!isNaN(Number(agentIdOrIndex))) { - agentIdOrIndex = await getAgentIdFromIndex(parseInt(agentIdOrIndex)) + agentIdOrIndex = await getAgentIdFromIndex(Number.parseInt(agentIdOrIndex)) } const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) diff --git a/packages/core/__tests__/knowledge.test.ts b/packages/core/__tests__/knowledge.test.ts index 299394924f7..a6a2919ba62 100644 --- a/packages/core/__tests__/knowledge.test.ts +++ b/packages/core/__tests__/knowledge.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import knowledge from "../src/knowledge"; import type { AgentRuntime } from "../src/runtime"; -import { type Memory } from "../src/types"; +import type { Memory } from "../src/types"; vi.mock("../generation", () => ({ splitChunks: vi.fn().mockImplementation(async (text) => [text]), diff --git a/packages/core/__tests__/mockCharacter.ts b/packages/core/__tests__/mockCharacter.ts index 52aac1434b8..bf7043c30c2 100644 --- a/packages/core/__tests__/mockCharacter.ts +++ b/packages/core/__tests__/mockCharacter.ts @@ -1,4 +1,4 @@ -import { type Character } from "@elizaos/core"; +import type { Character } from "@elizaos/core"; export const mockCharacter: Character = { name: "Eliza", diff --git a/packages/core/__tests__/runtime.test.ts b/packages/core/__tests__/runtime.test.ts index dd4eb2a7f32..b418c531d9c 100644 --- a/packages/core/__tests__/runtime.test.ts +++ b/packages/core/__tests__/runtime.test.ts @@ -1,11 +1,11 @@ import { beforeEach, describe, expect, it, test, vi } from "vitest"; import { AgentRuntime } from "../src/runtime"; -import { - type Action, - type IDatabaseAdapter, - type IMemoryManager, - type Memory, - type UUID +import type { + Action, + IDatabaseAdapter, + IMemoryManager, + Memory, + UUID } from "../src/types"; // Mock dependencies with minimal implementations diff --git a/packages/core/src/runtime.ts b/packages/core/src/runtime.ts index 5ebe34f90cd..4bcd1e6d798 100644 --- a/packages/core/src/runtime.ts +++ b/packages/core/src/runtime.ts @@ -44,8 +44,8 @@ import { type Provider, type State, type UUID, - ServiceType, - Service + type ServiceType, + type Service } from "./types.ts"; import { stringToUuid } from "./uuid.ts"; diff --git a/packages/core/src/test_resources/createRuntime.ts b/packages/core/src/test_resources/createRuntime.ts index 62b183ba06d..21beebe4d21 100644 --- a/packages/core/src/test_resources/createRuntime.ts +++ b/packages/core/src/test_resources/createRuntime.ts @@ -1,7 +1,7 @@ import { SqliteDatabaseAdapter, loadVecExtensions } from "@elizaos-plugins/sqlite"; import type { DatabaseAdapter } from "../database.ts"; import { AgentRuntime } from "../runtime.ts"; -import { type Action, type Evaluator, type Provider } from "../types.ts"; +import type { Action, Evaluator, Provider } from "../types.ts"; import { zeroUuid } from "./constants.ts"; diff --git a/packages/plugin-anthropic/biome.json b/packages/plugin-anthropic/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-anthropic/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-anthropic/src/index.ts b/packages/plugin-anthropic/src/index.ts index 777b68675b4..9c3418566c1 100644 --- a/packages/plugin-anthropic/src/index.ts +++ b/packages/plugin-anthropic/src/index.ts @@ -1,7 +1,7 @@ import { anthropic } from "@ai-sdk/anthropic"; import type { Plugin } from "@elizaos/core"; import { - GenerateTextParams, + type GenerateTextParams, ModelClass } from "@elizaos/core"; import { generateText } from "ai"; diff --git a/packages/plugin-bootstrap/biome.json b/packages/plugin-bootstrap/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-bootstrap/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-bootstrap/src/providers/facts.ts b/packages/plugin-bootstrap/src/providers/facts.ts index c1b3816e99b..6bd2eac5c2c 100644 --- a/packages/plugin-bootstrap/src/providers/facts.ts +++ b/packages/plugin-bootstrap/src/providers/facts.ts @@ -1,7 +1,7 @@ import type { Memory, Provider, State } from "@elizaos/core"; import { formatMessages, - IAgentRuntime, + type IAgentRuntime, MemoryManager, ModelClass } from "@elizaos/core"; diff --git a/packages/plugin-discord/biome.json b/packages/plugin-discord/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-discord/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-discord/src/actions/reply.ts b/packages/plugin-discord/src/actions/reply.ts index bb887919a06..98aff89e52c 100644 --- a/packages/plugin-discord/src/actions/reply.ts +++ b/packages/plugin-discord/src/actions/reply.ts @@ -1,9 +1,9 @@ -import { - type Action, - type HandlerCallback, - type IAgentRuntime, - type Memory, - type State +import type { + Action, + HandlerCallback, + IAgentRuntime, + Memory, + State } from "@elizaos/core"; const replyAction = { diff --git a/packages/plugin-discord/src/index.ts b/packages/plugin-discord/src/index.ts index 533ed7fe405..d9ffd9e08dd 100644 --- a/packages/plugin-discord/src/index.ts +++ b/packages/plugin-discord/src/index.ts @@ -1,7 +1,7 @@ import { logger, stringToUuid, - TestSuite, + type TestSuite, type Character, type Client as ElizaClient, type IAgentRuntime, @@ -28,7 +28,7 @@ import { MessageManager } from "./messages.ts"; import channelStateProvider from "./providers/channelState.ts"; import voiceStateProvider from "./providers/voiceState.ts"; import reply from "./actions/reply.ts"; -import { IDiscordClient } from "./types.ts"; +import type { IDiscordClient } from "./types.ts"; import { VoiceManager } from "./voice.ts"; export class DiscordClient extends EventEmitter implements IDiscordClient { diff --git a/packages/plugin-discord/src/messages.ts b/packages/plugin-discord/src/messages.ts index f3742f1fe7b..84e5205c7bf 100644 --- a/packages/plugin-discord/src/messages.ts +++ b/packages/plugin-discord/src/messages.ts @@ -9,7 +9,7 @@ import { ChannelType, type Client, type Message as DiscordMessage, - TextChannel, + type TextChannel, } from "discord.js"; import { AttachmentManager } from "./attachments.ts"; import { diff --git a/packages/plugin-discord/src/types.ts b/packages/plugin-discord/src/types.ts index bffdc28c665..ec45a8243d0 100644 --- a/packages/plugin-discord/src/types.ts +++ b/packages/plugin-discord/src/types.ts @@ -1,8 +1,8 @@ -import { - type Character, - type IAgentRuntime +import type { + Character, + IAgentRuntime } from "@elizaos/core"; -import { +import type { Client } from "discord.js"; diff --git a/packages/plugin-drizzle/biome.json b/packages/plugin-drizzle/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-drizzle/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-drizzle/src/index.ts b/packages/plugin-drizzle/src/index.ts index 31b400a4e58..a8091b581f5 100644 --- a/packages/plugin-drizzle/src/index.ts +++ b/packages/plugin-drizzle/src/index.ts @@ -33,10 +33,10 @@ import { knowledgeTable, cacheTable, } from "./schema"; -import { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres"; +import { drizzle, type NodePgDatabase } from "drizzle-orm/node-postgres"; import { v4 } from "uuid"; import { runMigrations } from "./migrations"; -import pg, { ConnectionConfig, PoolConfig } from "pg"; +import pg, { type ConnectionConfig, type PoolConfig } from "pg"; type Pool = pg.Pool; export class DrizzleDatabaseAdapter @@ -138,7 +138,7 @@ export class DrizzleDatabaseAdapter if (attempt < this.maxRetries) { // Calculate delay with exponential backoff const backoffDelay = Math.min( - this.baseDelay * Math.pow(2, attempt - 1), + this.baseDelay * (2 ** (attempt - 1)), this.maxDelay ); diff --git a/packages/plugin-drizzle/src/migrations.ts b/packages/plugin-drizzle/src/migrations.ts index 4e31834a063..148393396bb 100644 --- a/packages/plugin-drizzle/src/migrations.ts +++ b/packages/plugin-drizzle/src/migrations.ts @@ -2,7 +2,7 @@ import { migrate } from "drizzle-orm/node-postgres/migrator"; import { fileURLToPath } from 'url'; import path from "path"; import { drizzle } from "drizzle-orm/node-postgres"; -import { Pool } from "pg"; +import type { Pool } from "pg"; import { logger } from "@elizaos/core"; const __filename = fileURLToPath(import.meta.url); diff --git a/packages/plugin-local-ai/biome.json b/packages/plugin-local-ai/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-local-ai/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-local-ai/src/index.ts b/packages/plugin-local-ai/src/index.ts index 79b4676584a..ae292618386 100644 --- a/packages/plugin-local-ai/src/index.ts +++ b/packages/plugin-local-ai/src/index.ts @@ -281,7 +281,7 @@ class LocalAIManager { } // LLaMA text generation - async generateText(context: string, temperature: number = 0.7, stopSequences: string[] = []): Promise { + async generateText(context: string, temperature = 0.7, stopSequences: string[] = []): Promise { if (!this.sequence) { throw new Error("LLaMA model not initialized"); } diff --git a/packages/plugin-node/biome.json b/packages/plugin-node/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-node/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-openai/biome.json b/packages/plugin-openai/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-openai/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-openai/src/index.ts b/packages/plugin-openai/src/index.ts index c0a4df07d5c..1f8a2d826dc 100644 --- a/packages/plugin-openai/src/index.ts +++ b/packages/plugin-openai/src/index.ts @@ -1,10 +1,10 @@ import { createOpenAI } from "@ai-sdk/openai"; import type { IAgentRuntime, Plugin } from "@elizaos/core"; import { - DetokenizeTextParams, - GenerateTextParams, + type DetokenizeTextParams, + type GenerateTextParams, ModelClass, - TokenizeTextParams, + type TokenizeTextParams, } from "@elizaos/core"; import { generateText as aiGenerateText } from "ai"; import { encodingForModel, type TiktokenModel } from "js-tiktoken"; @@ -246,7 +246,7 @@ export const openaiPlugin: Plugin = { { role: "user", content: [{ - type: "image" as "image", + type: "image" as const, image: imageUrl }] } diff --git a/packages/plugin-sqlite/biome.json b/packages/plugin-sqlite/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-sqlite/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-tee/biome.json b/packages/plugin-tee/biome.json index 818716a6219..7a635ccfaec 100644 --- a/packages/plugin-tee/biome.json +++ b/packages/plugin-tee/biome.json @@ -1,5 +1,6 @@ { "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], "organizeImports": { "enabled": false }, @@ -14,8 +15,7 @@ "noExplicitAny": "error" }, "style": { - "useConst": "error", - "useImportType": "off" + "useConst": "error" } } }, @@ -27,8 +27,7 @@ }, "javascript": { "formatter": { - "quoteStyle": "single", - "trailingCommas": "es5" + "quoteStyle": "single" } }, "files": { diff --git a/packages/plugin-tee/src/__tests__/deriveKey.test.ts b/packages/plugin-tee/src/__tests__/deriveKey.test.ts index deabae7bcaf..318989e1b30 100644 --- a/packages/plugin-tee/src/__tests__/deriveKey.test.ts +++ b/packages/plugin-tee/src/__tests__/deriveKey.test.ts @@ -7,30 +7,30 @@ import { TEEMode } from '../src/types/tee'; vi.mock('@phala/dstack-sdk', () => ({ TappdClient: vi.fn().mockImplementation(() => ({ deriveKey: vi.fn().mockResolvedValue({ - asUint8Array: () => new Uint8Array([1, 2, 3, 4, 5]) + asUint8Array: () => new Uint8Array([1, 2, 3, 4, 5]), }), tdxQuote: vi.fn().mockResolvedValue({ quote: 'mock-quote-data', - replayRtmrs: () => ['rtmr0', 'rtmr1', 'rtmr2', 'rtmr3'] + replayRtmrs: () => ['rtmr0', 'rtmr1', 'rtmr2', 'rtmr3'], }), - rawDeriveKey: vi.fn() - })) + rawDeriveKey: vi.fn(), + })), })); vi.mock('@solana/web3.js', () => ({ Keypair: { fromSeed: vi.fn().mockReturnValue({ publicKey: { - toBase58: () => 'mock-solana-public-key' - } - }) - } + toBase58: () => 'mock-solana-public-key', + }, + }), + }, })); vi.mock('viem/accounts', () => ({ privateKeyToAccount: vi.fn().mockReturnValue({ - address: 'mock-evm-address' - }) + address: 'mock-evm-address', + }), })); describe('DeriveKeyProvider', () => { @@ -108,4 +108,4 @@ describe('DeriveKeyProvider', () => { expect(result.keypair.publicKey.toBase58()).toEqual('mock-solana-public-key'); }); }); -}); \ No newline at end of file +}); diff --git a/packages/plugin-tee/src/__tests__/remoteAttestation.test.ts b/packages/plugin-tee/src/__tests__/remoteAttestation.test.ts index 4890d6683e5..85d078d5ac3 100644 --- a/packages/plugin-tee/src/__tests__/remoteAttestation.test.ts +++ b/packages/plugin-tee/src/__tests__/remoteAttestation.test.ts @@ -8,10 +8,10 @@ vi.mock('@phala/dstack-sdk', () => ({ TappdClient: vi.fn().mockImplementation(() => ({ tdxQuote: vi.fn().mockResolvedValue({ quote: 'mock-quote-data', - replayRtmrs: () => ['rtmr0', 'rtmr1', 'rtmr2', 'rtmr3'] + replayRtmrs: () => ['rtmr0', 'rtmr1', 'rtmr2', 'rtmr3'], }), - deriveKey: vi.fn() - })) + deriveKey: vi.fn(), + })), })); describe('RemoteAttestationProvider', () => { @@ -53,7 +53,7 @@ describe('RemoteAttestationProvider', () => { expect(quote).toEqual({ quote: 'mock-quote-data', - timestamp: expect.any(Number) + timestamp: expect.any(Number), }); }); @@ -62,11 +62,13 @@ describe('RemoteAttestationProvider', () => { const mockTdxQuote = vi.fn().mockRejectedValue(mockError); vi.mocked(TappdClient).mockImplementationOnce(() => ({ tdxQuote: mockTdxQuote, - deriveKey: vi.fn() + deriveKey: vi.fn(), })); const provider = new RemoteAttestationProvider(TEEMode.LOCAL); - await expect(provider.generateAttestation('test-data')).rejects.toThrow('Failed to generate TDX Quote'); + await expect(provider.generateAttestation('test-data')).rejects.toThrow( + 'Failed to generate TDX Quote', + ); }); it('should pass hash algorithm to tdxQuote when provided', async () => { @@ -78,4 +80,4 @@ describe('RemoteAttestationProvider', () => { expect(client.tdxQuote).toHaveBeenCalledWith(reportData, hashAlgorithm); }); }); -}); \ No newline at end of file +}); diff --git a/packages/plugin-tee/src/__tests__/remoteAttestationAction.test.ts b/packages/plugin-tee/src/__tests__/remoteAttestationAction.test.ts index 897de2c7d86..5ae17f61b9c 100644 --- a/packages/plugin-tee/src/__tests__/remoteAttestationAction.test.ts +++ b/packages/plugin-tee/src/__tests__/remoteAttestationAction.test.ts @@ -6,8 +6,8 @@ import { RemoteAttestationProvider } from '../src/providers/remoteAttestationPro vi.mock('../src/providers/remoteAttestationProvider'); vi.mock('undici', () => ({ fetch: vi.fn().mockResolvedValue({ - json: () => Promise.resolve({ checksum: 'mock-checksum' }) - }) + json: () => Promise.resolve({ checksum: 'mock-checksum' }), + }), })); describe('remoteAttestationAction', () => { @@ -16,8 +16,12 @@ describe('remoteAttestationAction', () => { getSetting: vi.fn().mockReturnValue('LOCAL'), getState: vi.fn(), setState: vi.fn(), - message:{ userId: 'user', roomId: 'room', content: { text: 'If you are running in a TEE, generate a remote attestation' } }, - setConversation: vi.fn() + message: { + userId: 'user', + roomId: 'room', + content: { text: 'If you are running in a TEE, generate a remote attestation' }, + }, + setConversation: vi.fn(), }; const mockCallback = vi.fn(); @@ -27,8 +31,8 @@ describe('remoteAttestationAction', () => { vi.mocked(RemoteAttestationProvider).mockImplementation(() => ({ generateAttestation: vi.fn().mockResolvedValue({ quote: '0x1234', - timestamp: Date.now() - }) + timestamp: Date.now(), + }), })); }); @@ -46,13 +50,13 @@ describe('remoteAttestationAction', () => { mockRuntime.message, {}, {}, - mockCallback + mockCallback, ); expect(result).toBe(true); expect(mockCallback).toHaveBeenCalledWith({ text: expect.stringContaining('https://proof.t16z.com/reports/mock-checksum'), - action: 'NONE' + action: 'NONE', }); }); @@ -62,8 +66,8 @@ describe('remoteAttestationAction', () => { generateAttestation: vi.fn().mockRejectedValueOnce(mockError), client: { tdxQuote: vi.fn(), - deriveKey: vi.fn() - } + deriveKey: vi.fn(), + }, })); const result = await remoteAttestationAction.handler( @@ -71,7 +75,7 @@ describe('remoteAttestationAction', () => { {}, {}, {}, - mockCallback + mockCallback, ); expect(result).toBe(false); @@ -92,7 +96,9 @@ describe('remoteAttestationAction', () => { const [userMessage, agentMessage] = remoteAttestationAction.examples[0]; expect(userMessage.user).toBe('{{user1}}'); - expect(userMessage.content.text).toBe('If you are running in a TEE, generate a remote attestation'); + expect(userMessage.content.text).toBe( + 'If you are running in a TEE, generate a remote attestation', + ); expect(userMessage.content.action).toBe('REMOTE_ATTESTATION'); expect(agentMessage.user).toBe('{{user2}}'); @@ -100,4 +106,4 @@ describe('remoteAttestationAction', () => { expect(agentMessage.content.action).toBeUndefined(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/plugin-tee/src/__tests__/timeout.test.ts b/packages/plugin-tee/src/__tests__/timeout.test.ts index f16a54d8a49..3c4906c079b 100644 --- a/packages/plugin-tee/src/__tests__/timeout.test.ts +++ b/packages/plugin-tee/src/__tests__/timeout.test.ts @@ -8,8 +8,8 @@ import { TappdClient } from '@phala/dstack-sdk'; vi.mock('@phala/dstack-sdk', () => ({ TappdClient: vi.fn().mockImplementation(() => ({ tdxQuote: vi.fn(), - deriveKey: vi.fn() - })) + deriveKey: vi.fn(), + })), })); describe('TEE Provider Timeout Tests', () => { @@ -19,18 +19,17 @@ describe('TEE Provider Timeout Tests', () => { describe('RemoteAttestationProvider', () => { it('should handle API timeout during attestation generation', async () => { - const mockTdxQuote = vi.fn() - .mockRejectedValueOnce(new Error('Request timed out')); + const mockTdxQuote = vi.fn().mockRejectedValueOnce(new Error('Request timed out')); vi.mocked(TappdClient).mockImplementation(() => ({ tdxQuote: mockTdxQuote, - deriveKey: vi.fn() + deriveKey: vi.fn(), })); const provider = new RemoteAttestationProvider(TEEMode.LOCAL); - await expect(() => provider.generateAttestation('test-data')) - .rejects - .toThrow('Failed to generate TDX Quote: Request timed out'); + await expect(() => provider.generateAttestation('test-data')).rejects.toThrow( + 'Failed to generate TDX Quote: Request timed out', + ); // Verify the call was made once expect(mockTdxQuote).toHaveBeenCalledTimes(1); @@ -38,18 +37,17 @@ describe('TEE Provider Timeout Tests', () => { }); it('should handle network errors during attestation generation', async () => { - const mockTdxQuote = vi.fn() - .mockRejectedValueOnce(new Error('Network error')); + const mockTdxQuote = vi.fn().mockRejectedValueOnce(new Error('Network error')); vi.mocked(TappdClient).mockImplementation(() => ({ tdxQuote: mockTdxQuote, - deriveKey: vi.fn() + deriveKey: vi.fn(), })); const provider = new RemoteAttestationProvider(TEEMode.LOCAL); - await expect(() => provider.generateAttestation('test-data')) - .rejects - .toThrow('Failed to generate TDX Quote: Network error'); + await expect(() => provider.generateAttestation('test-data')).rejects.toThrow( + 'Failed to generate TDX Quote: Network error', + ); expect(mockTdxQuote).toHaveBeenCalledTimes(1); }); @@ -57,14 +55,14 @@ describe('TEE Provider Timeout Tests', () => { it('should handle successful attestation generation', async () => { const mockQuote = { quote: 'test-quote', - replayRtmrs: () => ['rtmr0', 'rtmr1', 'rtmr2', 'rtmr3'] + replayRtmrs: () => ['rtmr0', 'rtmr1', 'rtmr2', 'rtmr3'], }; const mockTdxQuote = vi.fn().mockResolvedValueOnce(mockQuote); vi.mocked(TappdClient).mockImplementation(() => ({ tdxQuote: mockTdxQuote, - deriveKey: vi.fn() + deriveKey: vi.fn(), })); const provider = new RemoteAttestationProvider(TEEMode.LOCAL); @@ -73,43 +71,41 @@ describe('TEE Provider Timeout Tests', () => { expect(mockTdxQuote).toHaveBeenCalledTimes(1); expect(result).toEqual({ quote: 'test-quote', - timestamp: expect.any(Number) + timestamp: expect.any(Number), }); }); }); describe('DeriveKeyProvider', () => { it('should handle API timeout during key derivation', async () => { - const mockDeriveKey = vi.fn() - .mockRejectedValueOnce(new Error('Request timed out')); + const mockDeriveKey = vi.fn().mockRejectedValueOnce(new Error('Request timed out')); vi.mocked(TappdClient).mockImplementation(() => ({ tdxQuote: vi.fn(), - deriveKey: mockDeriveKey + deriveKey: mockDeriveKey, })); const provider = new DeriveKeyProvider(TEEMode.LOCAL); - await expect(() => provider.rawDeriveKey('test-path', 'test-subject')) - .rejects - .toThrow('Request timed out'); + await expect(() => provider.rawDeriveKey('test-path', 'test-subject')).rejects.toThrow( + 'Request timed out', + ); expect(mockDeriveKey).toHaveBeenCalledTimes(1); expect(mockDeriveKey).toHaveBeenCalledWith('test-path', 'test-subject'); }); it('should handle API timeout during Ed25519 key derivation', async () => { - const mockDeriveKey = vi.fn() - .mockRejectedValueOnce(new Error('Request timed out')); + const mockDeriveKey = vi.fn().mockRejectedValueOnce(new Error('Request timed out')); vi.mocked(TappdClient).mockImplementation(() => ({ tdxQuote: vi.fn(), - deriveKey: mockDeriveKey + deriveKey: mockDeriveKey, })); const provider = new DeriveKeyProvider(TEEMode.LOCAL); - await expect(() => provider.deriveEd25519Keypair('test-path', 'test-subject')) - .rejects - .toThrow('Request timed out'); + await expect(() => + provider.deriveEd25519Keypair('test-path', 'test-subject'), + ).rejects.toThrow('Request timed out'); expect(mockDeriveKey).toHaveBeenCalledTimes(1); }); diff --git a/packages/plugin-tee/src/adapters/sqliteDAO.ts b/packages/plugin-tee/src/adapters/sqliteDAO.ts index 9d985c23c8b..fa9d597b597 100644 --- a/packages/plugin-tee/src/adapters/sqliteDAO.ts +++ b/packages/plugin-tee/src/adapters/sqliteDAO.ts @@ -1,6 +1,12 @@ -import type { Database } from "better-sqlite3"; -import { TeeLogDAO, type TeeAgent, type TeeLog, type TeeLogQuery, type TeePageQuery } from "../types.ts"; -import { sqliteTables } from "./sqliteTables.ts"; +import type { Database } from 'better-sqlite3'; +import { + TeeLogDAO, + type TeeAgent, + type TeeLog, + type TeeLogQuery, + type TeePageQuery, +} from '../types.ts'; +import { sqliteTables } from './sqliteTables.ts'; export class SqliteTeeLogDAO extends TeeLogDAO { constructor(db: Database) { @@ -14,18 +20,31 @@ export class SqliteTeeLogDAO extends TeeLogDAO { async addLog(log: TeeLog): Promise { const stmt = this.db.prepare( - "INSERT INTO tee_logs (id, agentId, roomId, userId, type, content, timestamp, signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" + 'INSERT INTO tee_logs (id, agentId, roomId, userId, type, content, timestamp, signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', ); try { - stmt.run(log.id, log.agentId, log.roomId, log.userId, log.type, log.content, log.timestamp, log.signature); + stmt.run( + log.id, + log.agentId, + log.roomId, + log.userId, + log.type, + log.content, + log.timestamp, + log.signature, + ); return true; } catch (error) { - console.error("Error adding log to database", error); + console.error('Error adding log to database', error); return false; } } - async getPagedLogs(query: TeeLogQuery, page: number, pageSize: number): Promise> { + async getPagedLogs( + query: TeeLogQuery, + page: number, + pageSize: number, + ): Promise> { if (page < 1) { page = 1; } @@ -35,46 +54,46 @@ export class SqliteTeeLogDAO extends TeeLogDAO { const whereConditions = []; const params = []; - if (query.agentId && query.agentId !== "") { - whereConditions.push("agentId = ?"); + if (query.agentId && query.agentId !== '') { + whereConditions.push('agentId = ?'); params.push(query.agentId); } - if (query.roomId && query.roomId !== "") { - whereConditions.push("roomId = ?"); + if (query.roomId && query.roomId !== '') { + whereConditions.push('roomId = ?'); params.push(query.roomId); } - if (query.userId && query.userId !== "") { - whereConditions.push("userId = ?"); + if (query.userId && query.userId !== '') { + whereConditions.push('userId = ?'); params.push(query.userId); } - if (query.type && query.type !== "") { - whereConditions.push("type = ?"); + if (query.type && query.type !== '') { + whereConditions.push('type = ?'); params.push(query.type); } - if (query.containsContent && query.containsContent !== "") { - whereConditions.push("content LIKE ?"); + if (query.containsContent && query.containsContent !== '') { + whereConditions.push('content LIKE ?'); params.push(`%${query.containsContent}%`); } if (query.startTimestamp) { - whereConditions.push("timestamp >= ?"); + whereConditions.push('timestamp >= ?'); params.push(query.startTimestamp); } if (query.endTimestamp) { - whereConditions.push("timestamp <= ?"); + whereConditions.push('timestamp <= ?'); params.push(query.endTimestamp); } const whereClause = - whereConditions.length > 0 ? `WHERE ${whereConditions.join(" AND ")}` : ""; + whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; try { const total_stmt = this.db.prepare( - `SELECT COUNT(*) as total FROM tee_logs ${whereClause}` + `SELECT COUNT(*) as total FROM tee_logs ${whereClause}`, ); const total = total_stmt.get(params).total; const logs_stmt = this.db.prepare( - `SELECT * FROM tee_logs ${whereClause} ORDER BY timestamp ASC LIMIT ? OFFSET ?` + `SELECT * FROM tee_logs ${whereClause} ORDER BY timestamp ASC LIMIT ? OFFSET ?`, ); const logs = logs_stmt.all(...params, limit, offset); @@ -85,40 +104,49 @@ export class SqliteTeeLogDAO extends TeeLogDAO { data: logs, }; } catch (error) { - console.error("Error getting paged logs from database", error); + console.error('Error getting paged logs from database', error); throw error; } } async addAgent(agent: TeeAgent): Promise { const stmt = this.db.prepare( - "INSERT INTO tee_agents (id, agentId, agentName, createdAt, publicKey, attestation) VALUES (?, ?, ?, ?, ?, ?)" + 'INSERT INTO tee_agents (id, agentId, agentName, createdAt, publicKey, attestation) VALUES (?, ?, ?, ?, ?, ?)', ); try { - stmt.run(agent.id, agent.agentId, agent.agentName, agent.createdAt, agent.publicKey, agent.attestation); + stmt.run( + agent.id, + agent.agentId, + agent.agentName, + agent.createdAt, + agent.publicKey, + agent.attestation, + ); return true; } catch (error) { - console.error("Error adding agent to database", error); + console.error('Error adding agent to database', error); return false; } } async getAgent(agentId: string): Promise { - const stmt = this.db.prepare("SELECT * FROM tee_agents WHERE agentId = ? ORDER BY createdAt DESC LIMIT 1"); + const stmt = this.db.prepare( + 'SELECT * FROM tee_agents WHERE agentId = ? ORDER BY createdAt DESC LIMIT 1', + ); try { return stmt.get(agentId); } catch (error) { - console.error("Error getting agent from database", error); + console.error('Error getting agent from database', error); throw error; } } async getAllAgents(): Promise { - const stmt = this.db.prepare("SELECT * FROM tee_agents"); + const stmt = this.db.prepare('SELECT * FROM tee_agents'); try { return stmt.all(); } catch (error) { - console.error("Error getting all agents from database", error); + console.error('Error getting all agents from database', error); throw error; } } diff --git a/packages/plugin-tee/src/index.ts b/packages/plugin-tee/src/index.ts index 7d2ba0a4af8..494b64138dc 100644 --- a/packages/plugin-tee/src/index.ts +++ b/packages/plugin-tee/src/index.ts @@ -1,34 +1,19 @@ -import type { Plugin } from "@elizaos/core"; -import { - deriveKeyProvider, -} from "./providers/deriveKeyProvider"; -import { - remoteAttestationProvider -} from "./providers/remoteAttestationProvider"; +import type { Plugin } from '@elizaos/core'; +import { deriveKeyProvider } from './providers/deriveKeyProvider'; +import { remoteAttestationProvider } from './providers/remoteAttestationProvider'; -export { - DeriveKeyProvider -} from "./providers/deriveKeyProvider"; +export { DeriveKeyProvider } from './providers/deriveKeyProvider'; -import { sgxAttestationProvider } from "./providers/sgxAttestationProvider"; -import { TeeLogService } from "./services/teeLogService"; +import { sgxAttestationProvider } from './providers/sgxAttestationProvider'; +import { TeeLogService } from './services/teeLogService'; export { TeeLogService }; export const teePlugin: Plugin = { - name: "tee", - description: - "TEE plugin with actions to generate remote attestations and derive keys", - actions: [ - ], - evaluators: [ - ], - providers: [ - remoteAttestationProvider, - deriveKeyProvider, - sgxAttestationProvider, - ], - services: [ - new TeeLogService() - ], -}; \ No newline at end of file + name: 'tee', + description: 'TEE plugin with actions to generate remote attestations and derive keys', + actions: [], + evaluators: [], + providers: [remoteAttestationProvider, deriveKeyProvider, sgxAttestationProvider], + services: [new TeeLogService()], +}; diff --git a/packages/plugin-tee/src/providers/deriveKeyProvider.ts b/packages/plugin-tee/src/providers/deriveKeyProvider.ts index f3e5681ad7a..70caee42fd3 100644 --- a/packages/plugin-tee/src/providers/deriveKeyProvider.ts +++ b/packages/plugin-tee/src/providers/deriveKeyProvider.ts @@ -1,17 +1,11 @@ -import { - type IAgentRuntime, - type Memory, - type Provider, - type State, - logger, -} from "@elizaos/core"; -import { Keypair } from "@solana/web3.js"; -import crypto from "crypto"; -import { type DeriveKeyResponse, TappdClient } from "@phala/dstack-sdk"; -import { privateKeyToAccount } from "viem/accounts"; -import { type PrivateKeyAccount, keccak256 } from "viem"; -import { RemoteAttestationProvider } from "./remoteAttestationProvider"; -import { TEEMode, type RemoteAttestationQuote, type DeriveKeyAttestationData } from "@elizaos/core"; +import { type IAgentRuntime, type Memory, type Provider, type State, logger } from '@elizaos/core'; +import { Keypair } from '@solana/web3.js'; +import crypto from 'crypto'; +import { type DeriveKeyResponse, TappdClient } from '@phala/dstack-sdk'; +import { privateKeyToAccount } from 'viem/accounts'; +import { type PrivateKeyAccount, keccak256 } from 'viem'; +import { RemoteAttestationProvider } from './remoteAttestationProvider'; +import { TEEMode, type RemoteAttestationQuote, type DeriveKeyAttestationData } from '@elizaos/core'; class DeriveKeyProvider { private client: TappdClient; @@ -23,26 +17,20 @@ class DeriveKeyProvider { // Both LOCAL and DOCKER modes use the simulator, just with different endpoints switch (teeMode) { case TEEMode.LOCAL: - endpoint = "http://localhost:8090"; - logger.log( - "TEE: Connecting to local simulator at localhost:8090" - ); + endpoint = 'http://localhost:8090'; + logger.log('TEE: Connecting to local simulator at localhost:8090'); break; case TEEMode.DOCKER: - endpoint = "http://host.docker.internal:8090"; - logger.log( - "TEE: Connecting to simulator via Docker at host.docker.internal:8090" - ); + endpoint = 'http://host.docker.internal:8090'; + logger.log('TEE: Connecting to simulator via Docker at host.docker.internal:8090'); break; case TEEMode.PRODUCTION: endpoint = undefined; - logger.log( - "TEE: Running in production mode without simulator" - ); + logger.log('TEE: Running in production mode without simulator'); break; default: throw new Error( - `Invalid TEE_MODE: ${teeMode}. Must be one of: LOCAL, DOCKER, PRODUCTION` + `Invalid TEE_MODE: ${teeMode}. Must be one of: LOCAL, DOCKER, PRODUCTION`, ); } @@ -53,7 +41,7 @@ class DeriveKeyProvider { private async generateDeriveKeyAttestation( agentId: string, publicKey: string, - subject?: string + subject?: string, ): Promise { const deriveKeyData: DeriveKeyAttestationData = { agentId, @@ -61,11 +49,9 @@ class DeriveKeyProvider { subject, }; const reportdata = JSON.stringify(deriveKeyData); - logger.log( - "Generating Remote Attestation Quote for Derive Key..." - ); + logger.log('Generating Remote Attestation Quote for Derive Key...'); const quote = await this.raProvider.generateAttestation(reportdata); - logger.log("Remote Attestation Quote generated successfully!"); + logger.log('Remote Attestation Quote generated successfully!'); return quote; } @@ -75,24 +61,19 @@ class DeriveKeyProvider { * @param subject - The subject to derive the key from. This is used for the certificate chain. * @returns The derived key. */ - async rawDeriveKey( - path: string, - subject: string - ): Promise { + async rawDeriveKey(path: string, subject: string): Promise { try { if (!path || !subject) { - logger.error( - "Path and Subject are required for key derivation" - ); + logger.error('Path and Subject are required for key derivation'); } - logger.log("Deriving Raw Key in TEE..."); + logger.log('Deriving Raw Key in TEE...'); const derivedKey = await this.client.deriveKey(path, subject); - logger.log("Raw Key Derived Successfully!"); + logger.log('Raw Key Derived Successfully!'); return derivedKey; } catch (error) { - logger.error("Error deriving raw key:", error); + logger.error('Error deriving raw key:', error); throw error; } } @@ -107,20 +88,18 @@ class DeriveKeyProvider { async deriveEd25519Keypair( path: string, subject: string, - agentId: string + agentId: string, ): Promise<{ keypair: Keypair; attestation: RemoteAttestationQuote }> { try { if (!path || !subject) { - logger.error( - "Path and Subject are required for key derivation" - ); + logger.error('Path and Subject are required for key derivation'); } - logger.log("Deriving Key in TEE..."); + logger.log('Deriving Key in TEE...'); const derivedKey = await this.client.deriveKey(path, subject); const uint8ArrayDerivedKey = derivedKey.asUint8Array(); - const hash = crypto.createHash("sha256"); + const hash = crypto.createHash('sha256'); hash.update(uint8ArrayDerivedKey); const seed = hash.digest(); const seedArray = new Uint8Array(seed); @@ -129,13 +108,13 @@ class DeriveKeyProvider { // Generate an attestation for the derived key data for public to verify const attestation = await this.generateDeriveKeyAttestation( agentId, - keypair.publicKey.toBase58() + keypair.publicKey.toBase58(), ); - logger.log("Key Derived Successfully!"); + logger.log('Key Derived Successfully!'); return { keypair, attestation }; } catch (error) { - logger.error("Error deriving key:", error); + logger.error('Error deriving key:', error); throw error; } } @@ -150,34 +129,28 @@ class DeriveKeyProvider { async deriveEcdsaKeypair( path: string, subject: string, - agentId: string + agentId: string, ): Promise<{ keypair: PrivateKeyAccount; attestation: RemoteAttestationQuote; }> { try { if (!path || !subject) { - logger.error( - "Path and Subject are required for key derivation" - ); + logger.error('Path and Subject are required for key derivation'); } - logger.log("Deriving ECDSA Key in TEE..."); - const deriveKeyResponse: DeriveKeyResponse = - await this.client.deriveKey(path, subject); + logger.log('Deriving ECDSA Key in TEE...'); + const deriveKeyResponse: DeriveKeyResponse = await this.client.deriveKey(path, subject); const hex = keccak256(deriveKeyResponse.asUint8Array()); const keypair: PrivateKeyAccount = privateKeyToAccount(hex); // Generate an attestation for the derived key data for public to verify - const attestation = await this.generateDeriveKeyAttestation( - agentId, - keypair.address - ); - logger.log("ECDSA Key Derived Successfully!"); + const attestation = await this.generateDeriveKeyAttestation(agentId, keypair.address); + logger.log('ECDSA Key Derived Successfully!'); return { keypair, attestation }; } catch (error) { - logger.error("Error deriving ecdsa key:", error); + logger.error('Error deriving ecdsa key:', error); throw error; } } @@ -185,42 +158,37 @@ class DeriveKeyProvider { const deriveKeyProvider: Provider = { get: async (runtime: IAgentRuntime, _message?: Memory, _state?: State) => { - const teeMode = runtime.getSetting("TEE_MODE"); + const teeMode = runtime.getSetting('TEE_MODE'); const provider = new DeriveKeyProvider(teeMode); const agentId = runtime.agentId; try { // Validate wallet configuration - if (!runtime.getSetting("WALLET_SECRET_SALT")) { - logger.error( - "Wallet secret salt is not configured in settings" - ); - return ""; + if (!runtime.getSetting('WALLET_SECRET_SALT')) { + logger.error('Wallet secret salt is not configured in settings'); + return ''; } try { - const secretSalt = - runtime.getSetting("WALLET_SECRET_SALT") || "secret_salt"; + const secretSalt = runtime.getSetting('WALLET_SECRET_SALT') || 'secret_salt'; const solanaKeypair = await provider.deriveEd25519Keypair( secretSalt, - "solana", - agentId - ); - const evmKeypair = await provider.deriveEcdsaKeypair( - secretSalt, - "evm", - agentId + 'solana', + agentId, ); + const evmKeypair = await provider.deriveEcdsaKeypair(secretSalt, 'evm', agentId); return JSON.stringify({ solana: solanaKeypair.keypair.publicKey, evm: evmKeypair.keypair.address, }); } catch (error) { - logger.error("Error creating PublicKey:", error); - return ""; + logger.error('Error creating PublicKey:', error); + return ''; } } catch (error) { - logger.error("Error in derive key provider:", error.message); - return `Failed to fetch derive key information: ${error instanceof Error ? error.message : "Unknown error"}`; + logger.error('Error in derive key provider:', error.message); + return `Failed to fetch derive key information: ${ + error instanceof Error ? error.message : 'Unknown error' + }`; } }, }; diff --git a/packages/plugin-tee/src/providers/remoteAttestationProvider.ts b/packages/plugin-tee/src/providers/remoteAttestationProvider.ts index 0fc676df97a..eb8b33c5a64 100644 --- a/packages/plugin-tee/src/providers/remoteAttestationProvider.ts +++ b/packages/plugin-tee/src/providers/remoteAttestationProvider.ts @@ -1,12 +1,6 @@ -import { - type IAgentRuntime, - type Memory, - type Provider, - type State, - logger, -} from "@elizaos/core"; -import { type TdxQuoteResponse, TappdClient, type TdxQuoteHashAlgorithms } from "@phala/dstack-sdk"; -import { type RemoteAttestationQuote, TEEMode, type RemoteAttestationMessage } from "@elizaos/core"; +import { type IAgentRuntime, type Memory, type Provider, type State, logger } from '@elizaos/core'; +import { type TdxQuoteResponse, TappdClient, type TdxQuoteHashAlgorithms } from '@phala/dstack-sdk'; +import { type RemoteAttestationQuote, TEEMode, type RemoteAttestationMessage } from '@elizaos/core'; class RemoteAttestationProvider { private client: TappdClient; @@ -17,26 +11,20 @@ class RemoteAttestationProvider { // Both LOCAL and DOCKER modes use the simulator, just with different endpoints switch (teeMode) { case TEEMode.LOCAL: - endpoint = "http://localhost:8090"; - logger.log( - "TEE: Connecting to local simulator at localhost:8090" - ); + endpoint = 'http://localhost:8090'; + logger.log('TEE: Connecting to local simulator at localhost:8090'); break; case TEEMode.DOCKER: - endpoint = "http://host.docker.internal:8090"; - logger.log( - "TEE: Connecting to simulator via Docker at host.docker.internal:8090" - ); + endpoint = 'http://host.docker.internal:8090'; + logger.log('TEE: Connecting to simulator via Docker at host.docker.internal:8090'); break; case TEEMode.PRODUCTION: endpoint = undefined; - logger.log( - "TEE: Running in production mode without simulator" - ); + logger.log('TEE: Running in production mode without simulator'); break; default: throw new Error( - `Invalid TEE_MODE: ${teeMode}. Must be one of: LOCAL, DOCKER, PRODUCTION` + `Invalid TEE_MODE: ${teeMode}. Must be one of: LOCAL, DOCKER, PRODUCTION`, ); } @@ -45,28 +33,30 @@ class RemoteAttestationProvider { async generateAttestation( reportData: string, - hashAlgorithm?: TdxQuoteHashAlgorithms + hashAlgorithm?: TdxQuoteHashAlgorithms, ): Promise { try { - logger.log("Generating attestation for: ", reportData); - const tdxQuote: TdxQuoteResponse = - await this.client.tdxQuote(reportData, hashAlgorithm); + logger.log('Generating attestation for: ', reportData); + const tdxQuote: TdxQuoteResponse = await this.client.tdxQuote( + reportData, + hashAlgorithm, + ); const rtmrs = tdxQuote.replayRtmrs(); logger.log( - `rtmr0: ${rtmrs[0]}\nrtmr1: ${rtmrs[1]}\nrtmr2: ${rtmrs[2]}\nrtmr3: ${rtmrs[3]}f` + `rtmr0: ${rtmrs[0]}\nrtmr1: ${rtmrs[1]}\nrtmr2: ${rtmrs[2]}\nrtmr3: ${rtmrs[3]}f`, ); const quote: RemoteAttestationQuote = { quote: tdxQuote.quote, timestamp: Date.now(), }; - logger.log("Remote attestation quote: ", quote); + logger.log('Remote attestation quote: ', quote); return quote; } catch (error) { - console.error("Error generating remote attestation:", error); + console.error('Error generating remote attestation:', error); throw new Error( `Failed to generate TDX Quote: ${ - error instanceof Error ? error.message : "Unknown error" - }` + error instanceof Error ? error.message : 'Unknown error' + }`, ); } } @@ -75,7 +65,7 @@ class RemoteAttestationProvider { // Keep the original provider for backwards compatibility const remoteAttestationProvider: Provider = { get: async (runtime: IAgentRuntime, message: Memory, _state?: State) => { - const teeMode = runtime.getSetting("TEE_MODE"); + const teeMode = runtime.getSetting('TEE_MODE'); const provider = new RemoteAttestationProvider(teeMode); const agentId = runtime.agentId; @@ -87,17 +77,19 @@ const remoteAttestationProvider: Provider = { userId: message.userId, roomId: message.roomId, content: message.content.text, - } + }, }; - logger.log("Generating attestation for: ", JSON.stringify(attestationMessage)); - const attestation = await provider.generateAttestation(JSON.stringify(attestationMessage)); + logger.log('Generating attestation for: ', JSON.stringify(attestationMessage)); + const attestation = await provider.generateAttestation( + JSON.stringify(attestationMessage), + ); return `Your Agent's remote attestation is: ${JSON.stringify(attestation)}`; } catch (error) { - console.error("Error in remote attestation provider:", error); + console.error('Error in remote attestation provider:', error); throw new Error( `Failed to generate TDX Quote: ${ - error instanceof Error ? error.message : "Unknown error" - }` + error instanceof Error ? error.message : 'Unknown error' + }`, ); } }, diff --git a/packages/plugin-tee/src/providers/sgxAttestationProvider.ts b/packages/plugin-tee/src/providers/sgxAttestationProvider.ts index a50462f7d8b..a4117ce7bf4 100644 --- a/packages/plugin-tee/src/providers/sgxAttestationProvider.ts +++ b/packages/plugin-tee/src/providers/sgxAttestationProvider.ts @@ -1,5 +1,5 @@ -import type { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; -import type { SgxAttestation } from "@elizaos/core"; +import type { IAgentRuntime, Memory, Provider, State } from '@elizaos/core'; +import type { SgxAttestation } from '@elizaos/core'; import { promises as fs } from 'node:fs'; // Fix: Use node: protocol import { createHash } from 'node:crypto'; // Fix: Use node: protocol @@ -14,17 +14,15 @@ class SgxAttestationProvider { private readonly SGX_QUOTE_MAX_SIZE: number = 8192 * 4; private readonly SGX_TARGET_INFO_SIZE: number = 512; - private readonly MY_TARGET_INFO_PATH: string = "/dev/attestation/my_target_info"; - private readonly TARGET_INFO_PATH: string = "/dev/attestation/target_info"; - private readonly USER_REPORT_DATA_PATH: string = "/dev/attestation/user_report_data"; - private readonly QUOTE_PATH: string = "/dev/attestation/quote"; + private readonly MY_TARGET_INFO_PATH: string = '/dev/attestation/my_target_info'; + private readonly TARGET_INFO_PATH: string = '/dev/attestation/target_info'; + private readonly USER_REPORT_DATA_PATH: string = '/dev/attestation/user_report_data'; + private readonly QUOTE_PATH: string = '/dev/attestation/quote'; // Remove unnecessary constructor // constructor() {} - async generateAttestation( - reportData: string - ): Promise { + async generateAttestation(reportData: string): Promise { // Hash the report data to generate the raw user report. // The resulting hash value is 32 bytes long. // Ensure that the length of the raw user report does not exceed 64 bytes. @@ -42,25 +40,23 @@ class SgxAttestationProvider { // console.log("SGX remote attestation: ", attestation); return attestation; } catch (error) { - console.error("Error generating SGX remote attestation:", error); + console.error('Error generating SGX remote attestation:', error); throw new Error( `Failed to generate SGX Quote: ${ - error instanceof Error ? error.message : "Unknown error" - }` + error instanceof Error ? error.message : 'Unknown error' + }`, ); } } - async generateQuoteByGramine( - rawUserReport: Buffer - ): Promise { + async generateQuoteByGramine(rawUserReport: Buffer): Promise { if (rawUserReport.length > 64) { - throw new Error("the length of rawUserReport exceeds 64 bytes"); + throw new Error('the length of rawUserReport exceeds 64 bytes'); } const myTargetInfo = await fs.readFile(this.MY_TARGET_INFO_PATH); if (myTargetInfo.length !== this.SGX_TARGET_INFO_SIZE) { - throw new Error("Invalid my_target_info length"); + throw new Error('Invalid my_target_info length'); } await fs.writeFile(this.TARGET_INFO_PATH, myTargetInfo); @@ -69,12 +65,12 @@ class SgxAttestationProvider { // Read quote const quoteData = await fs.readFile(this.QUOTE_PATH); if (quoteData.length > this.SGX_QUOTE_MAX_SIZE) { - throw new Error("Invalid quote length"); + throw new Error('Invalid quote length'); } const realLen = quoteData.lastIndexOf(0); if (realLen === -1) { - throw new Error("quote without EOF"); + throw new Error('quote without EOF'); } //return '0x' + quoteData.subarray(0, realLen + 1).toString('hex'); @@ -92,11 +88,11 @@ const sgxAttestationProvider: Provider = { const attestation = await provider.generateAttestation(agentId); return `Your Agent's remote attestation is: ${JSON.stringify(attestation)}`; } catch (error) { - console.error("Error in remote attestation provider:", error); + console.error('Error in remote attestation provider:', error); throw new Error( `Failed to generate SGX Quote: ${ - error instanceof Error ? error.message : "Unknown error" - }` + error instanceof Error ? error.message : 'Unknown error' + }`, ); } }, diff --git a/packages/plugin-tee/src/providers/walletProvider.ts b/packages/plugin-tee/src/providers/walletProvider.ts index b81c2ce13a9..24ac7943b34 100644 --- a/packages/plugin-tee/src/providers/walletProvider.ts +++ b/packages/plugin-tee/src/providers/walletProvider.ts @@ -1,26 +1,20 @@ /* This is an example of how WalletProvider can use DeriveKeyProvider to generate a Solana Keypair */ -import { - type IAgentRuntime, - type Memory, - type Provider, - type State, - logger, -} from "@elizaos/core"; -import { Connection, type Keypair, type PublicKey } from "@solana/web3.js"; -import BigNumber from "bignumber.js"; -import NodeCache from "node-cache"; -import { DeriveKeyProvider } from "./deriveKeyProvider"; -import type { RemoteAttestationQuote } from "@elizaos/core"; +import { type IAgentRuntime, type Memory, type Provider, type State, logger } from '@elizaos/core'; +import { Connection, type Keypair, type PublicKey } from '@solana/web3.js'; +import BigNumber from 'bignumber.js'; +import NodeCache from 'node-cache'; +import { DeriveKeyProvider } from './deriveKeyProvider'; +import type { RemoteAttestationQuote } from '@elizaos/core'; // Provider configuration const PROVIDER_CONFIG = { - BIRDEYE_API: "https://public-api.birdeye.so", + BIRDEYE_API: 'https://public-api.birdeye.so', MAX_RETRIES: 3, RETRY_DELAY: 2000, - DEFAULT_RPC: "https://api.mainnet-beta.solana.com", + DEFAULT_RPC: 'https://api.mainnet-beta.solana.com', TOKEN_ADDRESSES: { - SOL: "So11111111111111111111111111111111111111112", - BTC: "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh", - ETH: "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs", + SOL: 'So11111111111111111111111111111111111111112', + BTC: '3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh', + ETH: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs', }, }; @@ -62,27 +56,23 @@ export class WalletProvider { constructor( private connection: Connection, - private walletPublicKey: PublicKey + private walletPublicKey: PublicKey, ) { this.cache = new NodeCache({ stdTTL: 300 }); // Cache TTL set to 5 minutes } - private async fetchWithRetry( - runtime, - url: string, - options: RequestInit = {} - ): Promise { + private async fetchWithRetry(runtime, url: string, options: RequestInit = {}): Promise { let lastError: Error; for (let i = 0; i < PROVIDER_CONFIG.MAX_RETRIES; i++) { try { - const apiKey = runtime.getSetting("BIRDEYE_API_KEY"); + const apiKey = runtime.getSetting('BIRDEYE_API_KEY'); const response = await fetch(url, { ...options, headers: { - Accept: "application/json", - "x-chain": "solana", - "X-API-KEY": apiKey || "", + Accept: 'application/json', + 'x-chain': 'solana', + 'X-API-KEY': apiKey || '', ...options.headers, }, }); @@ -90,7 +80,7 @@ export class WalletProvider { if (!response.ok) { const errorText = await response.text(); throw new Error( - `HTTP error! status: ${response.status}, message: ${errorText}` + `HTTP error! status: ${response.status}, message: ${errorText}`, ); } @@ -100,17 +90,13 @@ export class WalletProvider { logger.error(`Attempt ${i + 1} failed:`, error); lastError = error; if (i < PROVIDER_CONFIG.MAX_RETRIES - 1) { - const delay = PROVIDER_CONFIG.RETRY_DELAY * Math.pow(2, i); + const delay = PROVIDER_CONFIG.RETRY_DELAY * 2 ** i; await new Promise((resolve) => setTimeout(resolve, delay)); - continue; } } } - logger.error( - "All attempts failed. Throwing the last error:", - lastError - ); + logger.error('All attempts failed. Throwing the last error:', lastError); throw lastError; } @@ -120,19 +106,21 @@ export class WalletProvider { const cachedValue = this.cache.get(cacheKey); if (cachedValue) { - logger.log("Cache hit for fetchPortfolioValue"); + logger.log('Cache hit for fetchPortfolioValue'); return cachedValue; } - logger.log("Cache miss for fetchPortfolioValue"); + logger.log('Cache miss for fetchPortfolioValue'); const walletData = await this.fetchWithRetry( runtime, - `${PROVIDER_CONFIG.BIRDEYE_API}/v1/wallet/token_list?wallet=${this.walletPublicKey.toBase58()}` + `${ + PROVIDER_CONFIG.BIRDEYE_API + }/v1/wallet/token_list?wallet=${this.walletPublicKey.toBase58()}`, ); if (!walletData?.success || !walletData?.data) { - logger.error("No portfolio data available", walletData); - throw new Error("No portfolio data available"); + logger.error('No portfolio data available', walletData); + throw new Error('No portfolio data available'); } const data = walletData.data; @@ -142,13 +130,11 @@ export class WalletProvider { const items = data.items.map((item: any) => ({ ...item, - valueSol: new BigNumber(item.valueUsd || 0) - .div(solPriceInUSD) - .toFixed(6), - name: item.name || "Unknown", - symbol: item.symbol || "Unknown", - priceUsd: item.priceUsd || "0", - valueUsd: item.valueUsd || "0", + valueSol: new BigNumber(item.valueUsd || 0).div(solPriceInUSD).toFixed(6), + name: item.name || 'Unknown', + symbol: item.symbol || 'Unknown', + priceUsd: item.priceUsd || '0', + valueUsd: item.valueUsd || '0', })); const totalSol = totalUsd.div(solPriceInUSD); @@ -156,36 +142,34 @@ export class WalletProvider { totalUsd: totalUsd.toString(), totalSol: totalSol.toFixed(6), items: items.sort((a, b) => - new BigNumber(b.valueUsd) - .minus(new BigNumber(a.valueUsd)) - .toNumber() + new BigNumber(b.valueUsd).minus(new BigNumber(a.valueUsd)).toNumber(), ), }; this.cache.set(cacheKey, portfolio); return portfolio; } catch (error) { - logger.error("Error fetching portfolio:", error); + logger.error('Error fetching portfolio:', error); throw error; } } async fetchPrices(runtime): Promise { try { - const cacheKey = "prices"; + const cacheKey = 'prices'; const cachedValue = this.cache.get(cacheKey); if (cachedValue) { - logger.log("Cache hit for fetchPrices"); + logger.log('Cache hit for fetchPrices'); return cachedValue; } - logger.log("Cache miss for fetchPrices"); + logger.log('Cache miss for fetchPrices'); const { SOL, BTC, ETH } = PROVIDER_CONFIG.TOKEN_ADDRESSES; const tokens = [SOL, BTC, ETH]; const prices: Prices = { - solana: { usd: "0" }, - bitcoin: { usd: "0" }, - ethereum: { usd: "0" }, + solana: { usd: '0' }, + bitcoin: { usd: '0' }, + ethereum: { usd: '0' }, }; for (const token of tokens) { @@ -194,40 +178,29 @@ export class WalletProvider { `${PROVIDER_CONFIG.BIRDEYE_API}/defi/price?address=${token}`, { headers: { - "x-chain": "solana", + 'x-chain': 'solana', }, - } + }, ); if (response?.data?.value) { const price = response.data.value.toString(); - prices[ - token === SOL - ? "solana" - : token === BTC - ? "bitcoin" - : "ethereum" - ].usd = price; + prices[token === SOL ? 'solana' : token === BTC ? 'bitcoin' : 'ethereum'].usd = + price; } else { - logger.warn( - `No price data available for token: ${token}` - ); + logger.warn(`No price data available for token: ${token}`); } } this.cache.set(cacheKey, prices); return prices; } catch (error) { - logger.error("Error fetching prices:", error); + logger.error('Error fetching prices:', error); throw error; } } - formatPortfolio( - runtime, - portfolio: WalletPortfolio, - prices: Prices - ): string { + formatPortfolio(runtime, portfolio: WalletPortfolio, prices: Prices): string { let output = `${runtime.character.description}\n`; output += `Wallet Address: ${this.walletPublicKey.toBase58()}\n\n`; @@ -235,24 +208,24 @@ export class WalletProvider { const totalSolFormatted = portfolio.totalSol; output += `Total Value: $${totalUsdFormatted} (${totalSolFormatted} SOL)\n\n`; - output += "Token Balances:\n"; + output += 'Token Balances:\n'; const nonZeroItems = portfolio.items.filter((item) => - new BigNumber(item.uiAmount).isGreaterThan(0) + new BigNumber(item.uiAmount).isGreaterThan(0), ); if (nonZeroItems.length === 0) { - output += "No tokens found with non-zero balance\n"; + output += 'No tokens found with non-zero balance\n'; } else { for (const item of nonZeroItems) { const valueUsd = new BigNumber(item.valueUsd).toFixed(2); - output += `${item.name} (${item.symbol}): ${new BigNumber( - item.uiAmount - ).toFixed(6)} ($${valueUsd} | ${item.valueSol} SOL)\n`; + output += `${item.name} (${item.symbol}): ${new BigNumber(item.uiAmount).toFixed( + 6, + )} ($${valueUsd} | ${item.valueSol} SOL)\n`; } } - output += "\nMarket Prices:\n"; + output += '\nMarket Prices:\n'; output += `SOL: $${new BigNumber(prices.solana.usd).toFixed(2)}\n`; output += `BTC: $${new BigNumber(prices.bitcoin.usd).toFixed(2)}\n`; output += `ETH: $${new BigNumber(prices.ethereum.usd).toFixed(2)}\n`; @@ -269,28 +242,22 @@ export class WalletProvider { return this.formatPortfolio(runtime, portfolio, prices); } catch (error) { - logger.error("Error generating portfolio report:", error); - return "Unable to fetch wallet information. Please try again later."; + logger.error('Error generating portfolio report:', error); + return 'Unable to fetch wallet information. Please try again later.'; } } } const walletProvider: Provider = { - get: async ( - runtime: IAgentRuntime, - _message: Memory, - _state?: State - ): Promise => { + get: async (runtime: IAgentRuntime, _message: Memory, _state?: State): Promise => { const agentId = runtime.agentId; - const teeMode = runtime.getSetting("TEE_MODE"); + const teeMode = runtime.getSetting('TEE_MODE'); const deriveKeyProvider = new DeriveKeyProvider(teeMode); try { // Validate wallet configuration - if (!runtime.getSetting("WALLET_SECRET_SALT")) { - logger.error( - "Wallet secret salt is not configured in settings" - ); - return ""; + if (!runtime.getSetting('WALLET_SECRET_SALT')) { + logger.error('Wallet secret salt is not configured in settings'); + return ''; } let publicKey: PublicKey; @@ -299,15 +266,15 @@ const walletProvider: Provider = { keypair: Keypair; attestation: RemoteAttestationQuote; } = await deriveKeyProvider.deriveEd25519Keypair( - runtime.getSetting("WALLET_SECRET_SALT"), - "solana", - agentId + runtime.getSetting('WALLET_SECRET_SALT'), + 'solana', + agentId, ); publicKey = derivedKeyPair.keypair.publicKey; - logger.log("Wallet Public Key: ", publicKey.toBase58()); + logger.log('Wallet Public Key: ', publicKey.toBase58()); } catch (error) { - logger.error("Error creating PublicKey:", error); - return ""; + logger.error('Error creating PublicKey:', error); + return ''; } const connection = new Connection(PROVIDER_CONFIG.DEFAULT_RPC); @@ -316,8 +283,10 @@ const walletProvider: Provider = { const porfolio = await provider.getFormattedPortfolio(runtime); return porfolio; } catch (error) { - logger.error("Error in wallet provider:", error.message); - return `Failed to fetch wallet information: ${error instanceof Error ? error.message : "Unknown error"}`; + logger.error('Error in wallet provider:', error.message); + return `Failed to fetch wallet information: ${ + error instanceof Error ? error.message : 'Unknown error' + }`; } }, }; diff --git a/packages/plugin-tee/src/services/teeLogManager.ts b/packages/plugin-tee/src/services/teeLogManager.ts index fcba701a810..02b1d284813 100644 --- a/packages/plugin-tee/src/services/teeLogManager.ts +++ b/packages/plugin-tee/src/services/teeLogManager.ts @@ -1,8 +1,16 @@ -import { SgxAttestationProvider } from "../providers/sgxAttestationProvider"; -import { RemoteAttestationProvider as TdxAttestationProvider } from "../providers/remoteAttestationProvider"; -import { TEEMode, TeeType, type TeeLogDAO, type TeeAgent, type TeeLog, type TeeLogQuery, type TeePageQuery } from "@elizaos/core"; -import elliptic from "elliptic"; -import { v4 } from "uuid"; +import { SgxAttestationProvider } from '../providers/sgxAttestationProvider'; +import { RemoteAttestationProvider as TdxAttestationProvider } from '../providers/remoteAttestationProvider'; +import { + type TEEMode, + TeeType, + type TeeLogDAO, + type TeeAgent, + type TeeLog, + type TeeLogQuery, + type TeePageQuery, +} from '@elizaos/core'; +import elliptic from 'elliptic'; +import { v4 } from 'uuid'; export class TeeLogManager { private teeLogDAO: TeeLogDAO; @@ -22,7 +30,7 @@ export class TeeLogManager { public async registerAgent(agentId: string, agentName: string): Promise { if (!agentId) { - throw new Error("Agent ID is required"); + throw new Error('Agent ID is required'); } const keyPair = this.generateKeyPair(); @@ -34,13 +42,13 @@ export class TeeLogManager { const new_agent = { id: v4(), agentId, - agentName: agentName || "", + agentName: agentName || '', createdAt: new Date().getTime(), publicKey, attestation, }; - console.log("registerAgent new_agent", new_agent); + console.log('registerAgent new_agent', new_agent); return this.teeLogDAO.addAgent(new_agent); } @@ -53,7 +61,13 @@ export class TeeLogManager { return this.teeLogDAO.getAgent(agentId); } - public async log(agentId: string, roomId: string, userId: string, type: string, content: string): Promise { + public async log( + agentId: string, + roomId: string, + userId: string, + type: string, + content: string, + ): Promise { const keyPair = this.keyPairs.get(agentId); if (!keyPair) { throw new Error(`Agent ${agentId} not found`); @@ -65,7 +79,7 @@ export class TeeLogManager { const messageToSign = `${agentId}|${roomId}|${userId}|${type}|${content}|${timestamp}`; // Sign the joined message - const signature = "0x" + keyPair.sign(messageToSign).toDER('hex'); + const signature = '0x' + keyPair.sign(messageToSign).toDER('hex'); return this.teeLogDAO.addLog({ id: v4(), @@ -79,7 +93,11 @@ export class TeeLogManager { }); } - public async getLogs(query: TeeLogQuery, page: number, pageSize: number): Promise> { + public async getLogs( + query: TeeLogQuery, + page: number, + pageSize: number, + ): Promise> { return this.teeLogDAO.getPagedLogs(query, page, pageSize); } @@ -99,7 +117,7 @@ export class TeeLogManager { const tdxAttestation = await tdxAttestationProvider.generateAttestation(userReport); return JSON.stringify(tdxAttestation); } else { - throw new Error("Invalid TEE type"); + throw new Error('Invalid TEE type'); } } } diff --git a/packages/plugin-tee/src/services/teeLogService.ts b/packages/plugin-tee/src/services/teeLogService.ts index 1178b4f92d4..e3503d44ec0 100644 --- a/packages/plugin-tee/src/services/teeLogService.ts +++ b/packages/plugin-tee/src/services/teeLogService.ts @@ -1,8 +1,20 @@ -import { type IAgentRuntime, Service, ServiceType, type ITeeLogService, TeeType, type TeeLogDAO, type TeeAgent, type TeeLog, type TeeLogQuery, type TeePageQuery, TEEMode } from "@elizaos/core"; -import { SqliteTeeLogDAO } from "../adapters/sqliteDAO"; -import { TeeLogManager } from "./teeLogManager"; -import Database from "better-sqlite3"; -import path from "path"; +import { + type IAgentRuntime, + Service, + ServiceType, + type ITeeLogService, + TeeType, + type TeeLogDAO, + type TeeAgent, + type TeeLog, + type TeeLogQuery, + type TeePageQuery, + TEEMode, +} from '@elizaos/core'; +import { SqliteTeeLogDAO } from '../adapters/sqliteDAO'; +import { TeeLogManager } from './teeLogManager'; +import Database from 'better-sqlite3'; +import path from 'path'; export class TeeLogService extends Service implements ITeeLogService { private dbPath: string; @@ -15,7 +27,6 @@ export class TeeLogService extends Service implements ITeeLogService { private teeLogDAO: TeeLogDAO; private teeLogManager: TeeLogManager; - getInstance(): ITeeLogService { return this; } @@ -29,21 +40,21 @@ export class TeeLogService extends Service implements ITeeLogService { return; } - const enableValues = ["true", "1", "yes", "enable", "enabled", "on"]; + const enableValues = ['true', '1', 'yes', 'enable', 'enabled', 'on']; - const enableTeeLog = runtime.getSetting("ENABLE_TEE_LOG"); + const enableTeeLog = runtime.getSetting('ENABLE_TEE_LOG'); if (enableTeeLog === null) { - throw new Error("ENABLE_TEE_LOG is not set."); + throw new Error('ENABLE_TEE_LOG is not set.'); } this.enableTeeLog = enableValues.includes(enableTeeLog.toLowerCase()); if (!this.enableTeeLog) { - console.log("TEE log is not enabled."); + console.log('TEE log is not enabled.'); return; } - const runInSgx = runtime.getSetting("SGX"); - const teeMode = runtime.getSetting("TEE_MODE"); - const walletSecretSalt = runtime.getSetting("WALLET_SECRET_SALT"); + const runInSgx = runtime.getSetting('SGX'); + const teeMode = runtime.getSetting('TEE_MODE'); + const walletSecretSalt = runtime.getSetting('WALLET_SECRET_SALT'); this.teeMode = teeMode ? TEEMode[teeMode as keyof typeof TEEMode] : TEEMode.OFF; @@ -51,17 +62,17 @@ export class TeeLogService extends Service implements ITeeLogService { const useTdxDstack = teeMode && teeMode !== TEEMode.OFF && walletSecretSalt; if (useSgxGramine && useTdxDstack) { - throw new Error("Cannot configure both SGX and TDX at the same time."); + throw new Error('Cannot configure both SGX and TDX at the same time.'); } else if (useSgxGramine) { this.teeType = TeeType.SGX_GRAMINE; } else if (useTdxDstack) { this.teeType = TeeType.TDX_DSTACK; } else { - throw new Error("Invalid TEE configuration."); + throw new Error('Invalid TEE configuration.'); } - const dbPathSetting = runtime.getSetting("TEE_LOG_DB_PATH"); - this.dbPath = dbPathSetting || path.resolve("data/tee_log.sqlite"); + const dbPathSetting = runtime.getSetting('TEE_LOG_DB_PATH'); + this.dbPath = dbPathSetting || path.resolve('data/tee_log.sqlite'); const db = new Database(this.dbPath); this.teeLogDAO = new SqliteTeeLogDAO(db); @@ -79,7 +90,13 @@ export class TeeLogService extends Service implements ITeeLogService { this.initialized = true; } - async log(agentId: string, roomId: string, userId: string, type: string, content: string): Promise { + async log( + agentId: string, + roomId: string, + userId: string, + type: string, + content: string, + ): Promise { if (!this.enableTeeLog) { return false; } @@ -103,7 +120,11 @@ export class TeeLogService extends Service implements ITeeLogService { return this.teeLogManager.getAgent(agentId); } - async getLogs(query: TeeLogQuery, page: number, pageSize: number): Promise> { + async getLogs( + query: TeeLogQuery, + page: number, + pageSize: number, + ): Promise> { if (!this.enableTeeLog) { return { data: [], diff --git a/packages/plugin-tee/src/types.ts b/packages/plugin-tee/src/types.ts index 3f4bee57aac..c676f0fae84 100644 --- a/packages/plugin-tee/src/types.ts +++ b/packages/plugin-tee/src/types.ts @@ -1,6 +1,6 @@ export enum TeeType { - SGX_GRAMINE = "sgx_gramine", - TDX_DSTACK = "tdx_dstack", + SGX_GRAMINE = 'sgx_gramine', + TDX_DSTACK = 'tdx_dstack', } // Represents a log entry in the TeeLog table, containing details about agent activities. @@ -55,7 +55,7 @@ export abstract class TeeLogDAO { abstract getPagedLogs( query: TeeLogQuery, page: number, - pageSize: number + pageSize: number, ): Promise>; abstract addAgent(agent: TeeAgent): Promise; @@ -63,4 +63,4 @@ export abstract class TeeLogDAO { abstract getAgent(agentId: string): Promise; abstract getAllAgents(): Promise; -} \ No newline at end of file +} diff --git a/packages/plugin-telegram/biome.json b/packages/plugin-telegram/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-telegram/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-telegram/src/actions/reply.ts b/packages/plugin-telegram/src/actions/reply.ts index 5cdc959ad4d..9dd788067d5 100644 --- a/packages/plugin-telegram/src/actions/reply.ts +++ b/packages/plugin-telegram/src/actions/reply.ts @@ -1,9 +1,9 @@ -import { - type Action, - type HandlerCallback, - type IAgentRuntime, - type Memory, - type State, +import type { + Action, + HandlerCallback, + IAgentRuntime, + Memory, + State, } from "@elizaos/core"; const replyAction = { diff --git a/packages/plugin-twitter/biome.json b/packages/plugin-twitter/biome.json new file mode 100644 index 00000000000..7a635ccfaec --- /dev/null +++ b/packages/plugin-twitter/biome.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-twitter/src/base.ts b/packages/plugin-twitter/src/base.ts index 849d395bf34..a55a5ca37ee 100644 --- a/packages/plugin-twitter/src/base.ts +++ b/packages/plugin-twitter/src/base.ts @@ -70,7 +70,7 @@ class RequestQueue { } private async exponentialBackoff(retryCount: number): Promise { - const delay = Math.pow(2, retryCount) * 1000; + const delay = (2 ** retryCount) * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); } diff --git a/packages/plugin-twitter/src/client/api.ts b/packages/plugin-twitter/src/client/api.ts index 08d9f4ced7f..5e40d19ae60 100644 --- a/packages/plugin-twitter/src/client/api.ts +++ b/packages/plugin-twitter/src/client/api.ts @@ -1,6 +1,6 @@ -import { TwitterAuth } from './auth'; +import type { TwitterAuth } from './auth'; import { ApiError } from './errors'; -import { Platform, PlatformExtensions } from './platform'; +import { Platform, type PlatformExtensions } from './platform'; import { updateCookieJar } from './requests'; import { Headers } from 'headers-polyfill'; @@ -89,7 +89,7 @@ export async function requestApi( const xRateLimitReset = res.headers.get('x-rate-limit-reset'); if (xRateLimitRemaining == '0' && xRateLimitReset) { const currentTime = new Date().valueOf() / 1000; - const timeDeltaMs = 1000 * (parseInt(xRateLimitReset) - currentTime); + const timeDeltaMs = 1000 * (Number.parseInt(xRateLimitReset) - currentTime); // I have seen this block for 800s (~13 *minutes*) await new Promise((resolve) => setTimeout(resolve, timeDeltaMs)); diff --git a/packages/plugin-twitter/src/client/auth-user.ts b/packages/plugin-twitter/src/client/auth-user.ts index 4898047e511..08b36f6e111 100644 --- a/packages/plugin-twitter/src/client/auth-user.ts +++ b/packages/plugin-twitter/src/client/auth-user.ts @@ -1,13 +1,13 @@ -import { TwitterAuthOptions, TwitterGuestAuth } from './auth'; +import { type TwitterAuthOptions, TwitterGuestAuth } from './auth'; import { requestApi } from './api'; import { CookieJar } from 'tough-cookie'; import { updateCookieJar } from './requests'; import { Headers } from 'headers-polyfill'; -import { TwitterApiErrorRaw } from './errors'; +import type { TwitterApiErrorRaw } from './errors'; import { Type, type Static } from '@sinclair/typebox'; import { Check } from '@sinclair/typebox/value'; import * as OTPAuth from 'otpauth'; -import { LegacyUserRaw, parseProfile, type Profile } from './profile'; +import { type LegacyUserRaw, parseProfile, type Profile } from './profile'; interface TwitterUserAuthFlowInitRequest { flow_name: string; diff --git a/packages/plugin-twitter/src/client/auth.ts b/packages/plugin-twitter/src/client/auth.ts index 96cf3801d2c..4508a9765e9 100644 --- a/packages/plugin-twitter/src/client/auth.ts +++ b/packages/plugin-twitter/src/client/auth.ts @@ -1,9 +1,9 @@ -import { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie'; +import { type Cookie, CookieJar, type MemoryCookieStore } from 'tough-cookie'; import { updateCookieJar } from './requests'; import { Headers } from 'headers-polyfill'; -import { FetchTransformOptions } from './api'; +import type { FetchTransformOptions } from './api'; import { TwitterApi } from 'twitter-api-v2'; -import { Profile } from './profile'; +import type { Profile } from './profile'; export interface TwitterAuthOptions { fetch: typeof fetch; diff --git a/packages/plugin-twitter/src/client/command.ts b/packages/plugin-twitter/src/client/command.ts index 76e21660522..7b4ab0f61f9 100644 --- a/packages/plugin-twitter/src/client/command.ts +++ b/packages/plugin-twitter/src/client/command.ts @@ -1,6 +1,6 @@ // Your existing imports import { Scraper } from './scraper'; -import { Photo, Tweet } from './tweets'; +import type { Photo, Tweet } from './tweets'; import fs from 'fs'; import path from 'path'; import readline from 'readline'; diff --git a/packages/plugin-twitter/src/client/grok.ts b/packages/plugin-twitter/src/client/grok.ts index b39b1c780db..31bd68d3252 100644 --- a/packages/plugin-twitter/src/client/grok.ts +++ b/packages/plugin-twitter/src/client/grok.ts @@ -1,5 +1,5 @@ import { requestApi } from './api'; -import { TwitterAuth } from './auth'; +import type { TwitterAuth } from './auth'; export interface GrokConversation { data: { diff --git a/packages/plugin-twitter/src/client/messages.ts b/packages/plugin-twitter/src/client/messages.ts index 6975320d8b7..a7028db4857 100644 --- a/packages/plugin-twitter/src/client/messages.ts +++ b/packages/plugin-twitter/src/client/messages.ts @@ -1,4 +1,4 @@ -import { TwitterAuth } from './auth'; +import type { TwitterAuth } from './auth'; import { updateCookieJar } from './requests'; export interface DirectMessage { diff --git a/packages/plugin-twitter/src/client/platform/index.ts b/packages/plugin-twitter/src/client/platform/index.ts index fd0abc2a3fb..24625a7e232 100644 --- a/packages/plugin-twitter/src/client/platform/index.ts +++ b/packages/plugin-twitter/src/client/platform/index.ts @@ -1,4 +1,4 @@ -import { PlatformExtensions, genericPlatform } from './platform-interface.js'; +import { type PlatformExtensions, genericPlatform } from './platform-interface.js'; export * from './platform-interface.js'; diff --git a/packages/plugin-twitter/src/client/platform/node/index.ts b/packages/plugin-twitter/src/client/platform/node/index.ts index b4a19ed92a4..50a08ee5107 100644 --- a/packages/plugin-twitter/src/client/platform/node/index.ts +++ b/packages/plugin-twitter/src/client/platform/node/index.ts @@ -1,4 +1,4 @@ -import { PlatformExtensions } from '../platform-interface'; +import type { PlatformExtensions } from '../platform-interface'; import { randomizeCiphers } from './randomize-ciphers'; class NodePlatform implements PlatformExtensions { diff --git a/packages/plugin-twitter/src/client/profile.test.ts b/packages/plugin-twitter/src/client/profile.test.ts index b7a876f13dd..257748f5b04 100644 --- a/packages/plugin-twitter/src/client/profile.test.ts +++ b/packages/plugin-twitter/src/client/profile.test.ts @@ -1,4 +1,4 @@ -import { Profile } from './profile'; +import type { Profile } from './profile'; import { getScraper } from './test-utils'; test('scraper can get screen name by user id', async () => { diff --git a/packages/plugin-twitter/src/client/profile.ts b/packages/plugin-twitter/src/client/profile.ts index 1c178439380..361013951bb 100644 --- a/packages/plugin-twitter/src/client/profile.ts +++ b/packages/plugin-twitter/src/client/profile.ts @@ -1,7 +1,7 @@ import stringify from 'json-stable-stringify'; -import { requestApi, RequestApiResult } from './api'; -import { TwitterAuth } from './auth'; -import { TwitterApiErrorRaw } from './errors'; +import { requestApi, type RequestApiResult } from './api'; +import type { TwitterAuth } from './auth'; +import type { TwitterApiErrorRaw } from './errors'; export interface LegacyUserRaw { created_at?: string; diff --git a/packages/plugin-twitter/src/client/relationships.ts b/packages/plugin-twitter/src/client/relationships.ts index f1b8b0d5fe1..969f3103c0b 100644 --- a/packages/plugin-twitter/src/client/relationships.ts +++ b/packages/plugin-twitter/src/client/relationships.ts @@ -1,11 +1,11 @@ import { addApiFeatures, requestApi, bearerToken } from './api'; import { Headers } from 'headers-polyfill'; -import { TwitterAuth } from './auth'; -import { Profile, getUserIdByScreenName } from './profile'; -import { QueryProfilesResponse } from './timeline-v1'; +import type { TwitterAuth } from './auth'; +import { type Profile, getUserIdByScreenName } from './profile'; +import type { QueryProfilesResponse } from './timeline-v1'; import { getUserTimeline } from './timeline-async'; import { - RelationshipTimeline, + type RelationshipTimeline, parseRelationshipTimeline, } from './timeline-relationship'; import stringify from 'json-stable-stringify'; diff --git a/packages/plugin-twitter/src/client/requests.ts b/packages/plugin-twitter/src/client/requests.ts index aba633ac629..54d05a22abb 100644 --- a/packages/plugin-twitter/src/client/requests.ts +++ b/packages/plugin-twitter/src/client/requests.ts @@ -1,4 +1,4 @@ -import { Cookie, CookieJar } from 'tough-cookie'; +import { Cookie, type CookieJar } from 'tough-cookie'; import setCookie from 'set-cookie-parser'; import type { Headers as HeadersPolyfill } from 'headers-polyfill'; diff --git a/packages/plugin-twitter/src/client/scraper.ts b/packages/plugin-twitter/src/client/scraper.ts index 9546f3f0902..5a23d1b31ce 100644 --- a/packages/plugin-twitter/src/client/scraper.ts +++ b/packages/plugin-twitter/src/client/scraper.ts @@ -1,17 +1,17 @@ -import { Cookie } from 'tough-cookie'; +import type { Cookie } from 'tough-cookie'; import { bearerToken, - FetchTransformOptions, + type FetchTransformOptions, requestApi, - RequestApiResult, + type RequestApiResult, } from './api'; -import { TwitterAuth, TwitterAuthOptions, TwitterGuestAuth } from './auth'; +import { type TwitterAuth, type TwitterAuthOptions, TwitterGuestAuth } from './auth'; import { TwitterUserAuth } from './auth-user'; import { getProfile, getUserIdByScreenName, getScreenNameByUserId, - Profile, + type Profile, } from './profile'; import { fetchQuotedTweetsPage, @@ -29,23 +29,23 @@ import { getFollowers, followUser, } from './relationships'; -import { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1'; +import type { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1'; import { getTrends } from './trends'; import { - Tweet, + type Tweet, getTweetAnonymous, getTweets, getLatestTweet, getTweetWhere, getTweetsWhere, getTweetsByUserId, - TweetQuery, + type TweetQuery, getTweet, fetchListTweets, getTweetsAndRepliesByUserId, getTweetsAndReplies, createCreateTweetRequest, - PollData, + type PollData, createCreateTweetRequestV2, getTweetV2, getTweetsV2, @@ -57,16 +57,16 @@ import { createCreateLongTweetRequest, getArticle, getAllRetweeters, - Retweeter, + type Retweeter, } from './tweets'; import { parseTimelineTweetsV2, - TimelineArticle, - TimelineV2, + type TimelineArticle, + type TimelineV2, } from './timeline-v2'; import { fetchHomeTimeline } from './timeline-home'; import { fetchFollowingTimeline } from './timeline-following'; -import { +import type { TTweetv2Expansion, TTweetv2MediaField, TTweetv2PlaceField, @@ -75,10 +75,10 @@ import { TTweetv2UserField, } from 'twitter-api-v2'; import { - DirectMessagesResponse, + type DirectMessagesResponse, getDirectMessageConversations, sendDirectMessage, - SendDirectMessageResponse, + type SendDirectMessageResponse, } from './messages'; import { fetchAudioSpaceById, @@ -88,7 +88,7 @@ import { fetchLiveVideoStreamStatus, fetchLoginTwitterToken, } from './spaces'; -import { +import type { AudioSpace, Community, LiveVideoStreamStatus, @@ -98,8 +98,8 @@ import { import { createGrokConversation, grokChat, - GrokChatOptions, - GrokChatResponse, + type GrokChatOptions, + type GrokChatResponse, } from './grok'; const twUrl = 'https://twitter.com'; diff --git a/packages/plugin-twitter/src/client/search.test.ts b/packages/plugin-twitter/src/client/search.test.ts index bf60fceb86a..d1a03d42670 100644 --- a/packages/plugin-twitter/src/client/search.test.ts +++ b/packages/plugin-twitter/src/client/search.test.ts @@ -1,6 +1,6 @@ import { getScraper } from './test-utils'; import { SearchMode } from './search'; -import { QueryTweetsResponse } from './timeline-v1'; +import type { QueryTweetsResponse } from './timeline-v1'; test('scraper can process search cursor', async () => { const scraper = await getScraper(); diff --git a/packages/plugin-twitter/src/client/search.ts b/packages/plugin-twitter/src/client/search.ts index 3682764c048..6041ab4c9a7 100644 --- a/packages/plugin-twitter/src/client/search.ts +++ b/packages/plugin-twitter/src/client/search.ts @@ -1,11 +1,11 @@ import { addApiFeatures, requestApi } from './api'; -import { TwitterAuth } from './auth'; -import { Profile } from './profile'; -import { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1'; +import type { TwitterAuth } from './auth'; +import type { Profile } from './profile'; +import type { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1'; import { getTweetTimeline, getUserTimeline } from './timeline-async'; -import { Tweet } from './tweets'; +import type { Tweet } from './tweets'; import { - SearchTimeline, + type SearchTimeline, parseSearchTimelineTweets, parseSearchTimelineUsers, } from './timeline-search'; @@ -15,11 +15,11 @@ import stringify from 'json-stable-stringify'; * The categories that can be used in Twitter searches. */ export enum SearchMode { - Top, - Latest, - Photos, - Videos, - Users, + Top = 0, + Latest = 1, + Photos = 2, + Videos = 3, + Users = 4, } export function searchTweets( diff --git a/packages/plugin-twitter/src/client/spaces.ts b/packages/plugin-twitter/src/client/spaces.ts index 5d0acfb8766..ff6c5dff638 100644 --- a/packages/plugin-twitter/src/client/spaces.ts +++ b/packages/plugin-twitter/src/client/spaces.ts @@ -1,6 +1,6 @@ -import { TwitterAuth } from './auth'; +import type { TwitterAuth } from './auth'; import { updateCookieJar } from './requests'; -import { +import type { AudioSpace, AudioSpaceByIdResponse, AudioSpaceByIdVariables, diff --git a/packages/plugin-twitter/src/client/spaces/core/ChatClient.ts b/packages/plugin-twitter/src/client/spaces/core/ChatClient.ts index 2117beb867e..9b2053522c8 100644 --- a/packages/plugin-twitter/src/client/spaces/core/ChatClient.ts +++ b/packages/plugin-twitter/src/client/spaces/core/ChatClient.ts @@ -3,7 +3,7 @@ import WebSocket from 'ws'; import { EventEmitter } from 'events'; import type { SpeakerRequest, OccupancyUpdate } from '../types'; -import { Logger } from '../logger'; +import type { Logger } from '../logger'; /** * Configuration object for ChatClient. diff --git a/packages/plugin-twitter/src/client/spaces/core/JanusAudio.ts b/packages/plugin-twitter/src/client/spaces/core/JanusAudio.ts index 99626be6f46..0e4d8ad641a 100644 --- a/packages/plugin-twitter/src/client/spaces/core/JanusAudio.ts +++ b/packages/plugin-twitter/src/client/spaces/core/JanusAudio.ts @@ -4,7 +4,7 @@ import { EventEmitter } from 'events'; import wrtc from '@roamhq/wrtc'; const { nonstandard } = wrtc; const { RTCAudioSource, RTCAudioSink } = nonstandard; -import { Logger } from '../logger'; +import type { Logger } from '../logger'; /** * Configuration options for the JanusAudioSource. diff --git a/packages/plugin-twitter/src/client/spaces/core/JanusClient.ts b/packages/plugin-twitter/src/client/spaces/core/JanusClient.ts index 65cdfac46fe..a965b7c4bf7 100644 --- a/packages/plugin-twitter/src/client/spaces/core/JanusClient.ts +++ b/packages/plugin-twitter/src/client/spaces/core/JanusClient.ts @@ -5,7 +5,7 @@ import wrtc from '@roamhq/wrtc'; const { RTCPeerConnection, MediaStream } = wrtc; import { JanusAudioSink, JanusAudioSource } from './JanusAudio'; import type { AudioDataWithUser, TurnServersInfo } from '../types'; -import { Logger } from '../logger'; +import type { Logger } from '../logger'; interface JanusConfig { /** @@ -213,7 +213,7 @@ export class JanusClient extends EventEmitter { */ public async subscribeSpeaker( userId: string, - feedId: number = 0, + feedId = 0, ): Promise { this.logger.debug('[JanusClient] subscribeSpeaker => userId=', userId); @@ -569,7 +569,7 @@ export class JanusClient extends EventEmitter { * Creates an SDP offer and sends "configure" to Janus with it. * Used by both host and guest after attach + join. */ - private async configurePublisher(sessionUUID: string = ''): Promise { + private async configurePublisher(sessionUUID = ''): Promise { if (!this.pc || !this.sessionId || !this.handleId) { return; } diff --git a/packages/plugin-twitter/src/client/spaces/core/Space.ts b/packages/plugin-twitter/src/client/spaces/core/Space.ts index 42a44739234..73a4f5a1982 100644 --- a/packages/plugin-twitter/src/client/spaces/core/Space.ts +++ b/packages/plugin-twitter/src/client/spaces/core/Space.ts @@ -21,7 +21,7 @@ import type { SpeakerInfo, SpaceConfig, } from '../types'; -import { Scraper } from '../../scraper'; +import type { Scraper } from '../../scraper'; import { Logger } from '../logger'; /** diff --git a/packages/plugin-twitter/src/client/spaces/core/SpaceParticipant.ts b/packages/plugin-twitter/src/client/spaces/core/SpaceParticipant.ts index 84679ee7aae..5bd7eb171a0 100644 --- a/packages/plugin-twitter/src/client/spaces/core/SpaceParticipant.ts +++ b/packages/plugin-twitter/src/client/spaces/core/SpaceParticipant.ts @@ -4,7 +4,7 @@ import { EventEmitter } from 'events'; import { Logger } from '../logger'; import { ChatClient } from './ChatClient'; import { JanusClient } from './JanusClient'; -import { Scraper } from '../../scraper'; +import type { Scraper } from '../../scraper'; import type { TurnServersInfo, Plugin, diff --git a/packages/plugin-twitter/src/client/spaces/plugins/HlsRecordPlugin.ts b/packages/plugin-twitter/src/client/spaces/plugins/HlsRecordPlugin.ts index 8b860c51d8a..946cad9a4d1 100644 --- a/packages/plugin-twitter/src/client/spaces/plugins/HlsRecordPlugin.ts +++ b/packages/plugin-twitter/src/client/spaces/plugins/HlsRecordPlugin.ts @@ -1,6 +1,6 @@ -import { spawn, ChildProcessWithoutNullStreams } from 'child_process'; -import { Plugin, OccupancyUpdate } from '../types'; -import { Space } from '../core/Space'; +import { spawn, type ChildProcessWithoutNullStreams } from 'child_process'; +import type { Plugin, OccupancyUpdate } from '../types'; +import type { Space } from '../core/Space'; import { Logger } from '../logger'; /** diff --git a/packages/plugin-twitter/src/client/spaces/plugins/IdleMonitorPlugin.ts b/packages/plugin-twitter/src/client/spaces/plugins/IdleMonitorPlugin.ts index 07c9e77a43e..3128e00507e 100644 --- a/packages/plugin-twitter/src/client/spaces/plugins/IdleMonitorPlugin.ts +++ b/packages/plugin-twitter/src/client/spaces/plugins/IdleMonitorPlugin.ts @@ -1,5 +1,5 @@ -import { Plugin, AudioDataWithUser } from '../types'; -import { Space } from '../core/Space'; +import type { Plugin, AudioDataWithUser } from '../types'; +import type { Space } from '../core/Space'; import { Logger } from '../logger'; /** @@ -21,8 +21,8 @@ export class IdleMonitorPlugin implements Plugin { * @param checkEveryMs How frequently (in ms) to check for silence. (Default: 10s) */ constructor( - private idleTimeoutMs: number = 60_000, - private checkEveryMs: number = 10_000, + private idleTimeoutMs = 60_000, + private checkEveryMs = 10_000, ) {} /** diff --git a/packages/plugin-twitter/src/client/spaces/plugins/MonitorAudioPlugin.ts b/packages/plugin-twitter/src/client/spaces/plugins/MonitorAudioPlugin.ts index 631d0e3d181..3ec261a12ad 100644 --- a/packages/plugin-twitter/src/client/spaces/plugins/MonitorAudioPlugin.ts +++ b/packages/plugin-twitter/src/client/spaces/plugins/MonitorAudioPlugin.ts @@ -1,5 +1,5 @@ -import { spawn, ChildProcessWithoutNullStreams } from 'child_process'; -import { Plugin, AudioDataWithUser } from '../types'; +import { spawn, type ChildProcessWithoutNullStreams } from 'child_process'; +import type { Plugin, AudioDataWithUser } from '../types'; import { Logger } from '../logger'; /** diff --git a/packages/plugin-twitter/src/client/spaces/plugins/RecordToDiskPlugin.ts b/packages/plugin-twitter/src/client/spaces/plugins/RecordToDiskPlugin.ts index 784eddefa7a..9e45baf5b5c 100644 --- a/packages/plugin-twitter/src/client/spaces/plugins/RecordToDiskPlugin.ts +++ b/packages/plugin-twitter/src/client/spaces/plugins/RecordToDiskPlugin.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; -import { AudioDataWithUser, Plugin } from '../types'; -import { Space } from '../core/Space'; -import { SpaceParticipant } from '../core/SpaceParticipant'; +import type { AudioDataWithUser, Plugin } from '../types'; +import type { Space } from '../core/Space'; +import type { SpaceParticipant } from '../core/SpaceParticipant'; import { Logger } from '../logger'; interface RecordToDiskPluginConfig { @@ -21,7 +21,7 @@ interface RecordToDiskPluginConfig { * - cleanup(...) => close file stream */ export class RecordToDiskPlugin implements Plugin { - private filePath: string = '/tmp/speaker_audio.raw'; + private filePath = '/tmp/speaker_audio.raw'; private outStream?: fs.WriteStream; private logger?: Logger; diff --git a/packages/plugin-twitter/src/client/spaces/plugins/SttTtsPlugin.ts b/packages/plugin-twitter/src/client/spaces/plugins/SttTtsPlugin.ts index 8cf07eb2982..b84c15654ff 100644 --- a/packages/plugin-twitter/src/client/spaces/plugins/SttTtsPlugin.ts +++ b/packages/plugin-twitter/src/client/spaces/plugins/SttTtsPlugin.ts @@ -3,10 +3,10 @@ import fs from 'fs'; import path from 'path'; import { spawn } from 'child_process'; -import { AudioDataWithUser, Plugin } from '../types'; -import { Space } from '../core/Space'; -import { SpaceParticipant } from '../core/SpaceParticipant'; -import { JanusClient } from '../core/JanusClient'; +import type { AudioDataWithUser, Plugin } from '../types'; +import type { Space } from '../core/Space'; +import type { SpaceParticipant } from '../core/SpaceParticipant'; +import type { JanusClient } from '../core/JanusClient'; import { Logger } from '../logger'; interface PluginConfig { @@ -52,12 +52,12 @@ export class SttTtsPlugin implements Plugin { // Credentials & config private openAiApiKey?: string; private elevenLabsApiKey?: string; - private sttLanguage: string = 'en'; - private gptModel: string = 'gpt-3.5-turbo'; - private voiceId: string = '21m00Tcm4TlvDq8ikWAM'; - private elevenLabsModel: string = 'eleven_monolingual_v1'; - private systemPrompt: string = 'You are a helpful AI assistant.'; - private silenceThreshold: number = 50; + private sttLanguage = 'en'; + private gptModel = 'gpt-3.5-turbo'; + private voiceId = '21m00Tcm4TlvDq8ikWAM'; + private elevenLabsModel = 'eleven_monolingual_v1'; + private systemPrompt = 'You are a helpful AI assistant.'; + private silenceThreshold = 50; /** * chatContext accumulates the conversation for GPT: @@ -85,7 +85,7 @@ export class SttTtsPlugin implements Plugin { * TTS queue for sequential playback */ private ttsQueue: string[] = []; - private isSpeaking: boolean = false; + private isSpeaking = false; /** * Called immediately after `.use(plugin)`. diff --git a/packages/plugin-twitter/src/client/spaces/test.ts b/packages/plugin-twitter/src/client/spaces/test.ts index b22803eb258..c2393d27c21 100644 --- a/packages/plugin-twitter/src/client/spaces/test.ts +++ b/packages/plugin-twitter/src/client/spaces/test.ts @@ -1,7 +1,7 @@ // src/test.ts import 'dotenv/config'; -import { Space, SpaceConfig } from './core/Space'; +import { Space, type SpaceConfig } from './core/Space'; import { Scraper } from '../scraper'; import { RecordToDiskPlugin } from './plugins/RecordToDiskPlugin'; import { SttTtsPlugin } from './plugins/SttTtsPlugin'; diff --git a/packages/plugin-twitter/src/client/spaces/types.ts b/packages/plugin-twitter/src/client/spaces/types.ts index 9b59fa50c83..da6f3940c2d 100644 --- a/packages/plugin-twitter/src/client/spaces/types.ts +++ b/packages/plugin-twitter/src/client/spaces/types.ts @@ -1,7 +1,7 @@ // src/types.ts -import { Space } from './core/Space'; -import { SpaceParticipant } from './core/SpaceParticipant'; +import type { Space } from './core/Space'; +import type { SpaceParticipant } from './core/SpaceParticipant'; /** * Basic PCM audio frame properties. diff --git a/packages/plugin-twitter/src/client/spaces/utils.ts b/packages/plugin-twitter/src/client/spaces/utils.ts index 83b8eb9468a..e98a4075af2 100644 --- a/packages/plugin-twitter/src/client/spaces/utils.ts +++ b/packages/plugin-twitter/src/client/spaces/utils.ts @@ -2,9 +2,9 @@ import { Headers } from 'headers-polyfill'; import type { BroadcastCreated, TurnServersInfo } from './types'; -import { ChatClient } from './core/ChatClient'; -import { Logger } from './logger'; -import { EventEmitter } from 'events'; +import type { ChatClient } from './core/ChatClient'; +import type { Logger } from './logger'; +import type { EventEmitter } from 'events'; /** * Authorizes a token for guest access, using the provided Periscope cookie. diff --git a/packages/plugin-twitter/src/client/timeline-async.ts b/packages/plugin-twitter/src/client/timeline-async.ts index e92f7c454a0..e06cef9bb28 100644 --- a/packages/plugin-twitter/src/client/timeline-async.ts +++ b/packages/plugin-twitter/src/client/timeline-async.ts @@ -1,5 +1,5 @@ -import { Profile } from './profile'; -import { Tweet } from './tweets'; +import type { Profile } from './profile'; +import type { Tweet } from './tweets'; export interface FetchProfilesResponse { profiles: Profile[]; diff --git a/packages/plugin-twitter/src/client/timeline-following.ts b/packages/plugin-twitter/src/client/timeline-following.ts index 8c308330a8c..966ec78a147 100644 --- a/packages/plugin-twitter/src/client/timeline-following.ts +++ b/packages/plugin-twitter/src/client/timeline-following.ts @@ -1,7 +1,7 @@ import { requestApi } from './api'; -import { TwitterAuth } from './auth'; +import type { TwitterAuth } from './auth'; import { ApiError } from './errors'; -import { TimelineInstruction } from './timeline-v2'; +import type { TimelineInstruction } from './timeline-v2'; export interface HomeLatestTimelineResponse { data?: { diff --git a/packages/plugin-twitter/src/client/timeline-home.ts b/packages/plugin-twitter/src/client/timeline-home.ts index 301137255cd..e090a9d6a3f 100644 --- a/packages/plugin-twitter/src/client/timeline-home.ts +++ b/packages/plugin-twitter/src/client/timeline-home.ts @@ -1,7 +1,7 @@ import { requestApi } from './api'; -import { TwitterAuth } from './auth'; +import type { TwitterAuth } from './auth'; import { ApiError } from './errors'; -import { TimelineInstruction } from './timeline-v2'; +import type { TimelineInstruction } from './timeline-v2'; export interface HomeTimelineResponse { data?: { diff --git a/packages/plugin-twitter/src/client/timeline-list.ts b/packages/plugin-twitter/src/client/timeline-list.ts index 6b7b0cd356f..b31bc2d2a0b 100644 --- a/packages/plugin-twitter/src/client/timeline-list.ts +++ b/packages/plugin-twitter/src/client/timeline-list.ts @@ -1,6 +1,6 @@ -import { QueryTweetsResponse } from './timeline-v1'; -import { parseAndPush, TimelineEntryRaw } from './timeline-v2'; -import { Tweet } from './tweets'; +import type { QueryTweetsResponse } from './timeline-v1'; +import { parseAndPush, type TimelineEntryRaw } from './timeline-v2'; +import type { Tweet } from './tweets'; export interface ListTimeline { data?: { diff --git a/packages/plugin-twitter/src/client/timeline-relationship.ts b/packages/plugin-twitter/src/client/timeline-relationship.ts index ed5a7db094c..b3cf18c1689 100644 --- a/packages/plugin-twitter/src/client/timeline-relationship.ts +++ b/packages/plugin-twitter/src/client/timeline-relationship.ts @@ -1,6 +1,6 @@ -import { Profile, parseProfile } from './profile'; -import { QueryProfilesResponse } from './timeline-v1'; -import { TimelineUserResultRaw } from './timeline-v2'; +import { type Profile, parseProfile } from './profile'; +import type { QueryProfilesResponse } from './timeline-v1'; +import type { TimelineUserResultRaw } from './timeline-v2'; export interface RelationshipEntryItemContentRaw { itemType?: string; diff --git a/packages/plugin-twitter/src/client/timeline-search.ts b/packages/plugin-twitter/src/client/timeline-search.ts index 530f17206a0..e9784223a33 100644 --- a/packages/plugin-twitter/src/client/timeline-search.ts +++ b/packages/plugin-twitter/src/client/timeline-search.ts @@ -1,7 +1,7 @@ -import { Profile, parseProfile } from './profile'; -import { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1'; -import { SearchEntryRaw, parseLegacyTweet } from './timeline-v2'; -import { Tweet } from './tweets'; +import { type Profile, parseProfile } from './profile'; +import type { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1'; +import { type SearchEntryRaw, parseLegacyTweet } from './timeline-v2'; +import type { Tweet } from './tweets'; export interface SearchTimeline { data?: { @@ -53,7 +53,7 @@ export function parseSearchTimelineTweets( if (tweetResult.success) { if (!tweetResult.tweet.views && tweetResultRaw?.views?.count) { - const views = parseInt(tweetResultRaw.views.count); + const views = Number.parseInt(tweetResultRaw.views.count); if (!isNaN(views)) { tweetResult.tweet.views = views; } diff --git a/packages/plugin-twitter/src/client/timeline-tweet-util.ts b/packages/plugin-twitter/src/client/timeline-tweet-util.ts index 762416cc61d..6547ec88eb8 100644 --- a/packages/plugin-twitter/src/client/timeline-tweet-util.ts +++ b/packages/plugin-twitter/src/client/timeline-tweet-util.ts @@ -1,6 +1,6 @@ -import { LegacyTweetRaw, TimelineMediaExtendedRaw } from './timeline-v1'; -import { Photo, Video } from './tweets'; -import { isFieldDefined, NonNullableField } from './type-util'; +import type { LegacyTweetRaw, TimelineMediaExtendedRaw } from './timeline-v1'; +import type { Photo, Video } from './tweets'; +import { isFieldDefined, type NonNullableField } from './type-util'; const reHashtag = /\B(\#\S+\b)/g; const reCashtag = /\B(\$\S+\b)/g; @@ -127,7 +127,7 @@ function linkUsernameHtml(username: string) { } function unwrapTcoUrlHtml(tweet: LegacyTweetRaw, foundedMedia: string[]) { - return function (tco: string) { + return (tco: string) => { for (const entity of tweet.entities?.urls ?? []) { if (tco === entity.url && entity.expanded_url != null) { return `${tco}`; diff --git a/packages/plugin-twitter/src/client/timeline-v1.ts b/packages/plugin-twitter/src/client/timeline-v1.ts index 17395805f53..298c26063b1 100644 --- a/packages/plugin-twitter/src/client/timeline-v1.ts +++ b/packages/plugin-twitter/src/client/timeline-v1.ts @@ -1,6 +1,6 @@ -import { LegacyUserRaw, parseProfile, Profile } from './profile'; +import { type LegacyUserRaw, parseProfile, type Profile } from './profile'; import { parseMediaGroups, reconstructTweetHtml } from './timeline-tweet-util'; -import { PlaceRaw, Tweet } from './tweets'; +import type { PlaceRaw, Tweet } from './tweets'; import { isFieldDefined } from './type-util'; export interface Hashtag { @@ -382,7 +382,7 @@ function parseTimelineTweet( } } - const views = parseInt(tweet.ext_views?.count ?? ''); + const views = Number.parseInt(tweet.ext_views?.count ?? ''); if (!isNaN(views)) { tw.views = views; } diff --git a/packages/plugin-twitter/src/client/timeline-v2.ts b/packages/plugin-twitter/src/client/timeline-v2.ts index 125a61d8330..e7388e8322e 100644 --- a/packages/plugin-twitter/src/client/timeline-v2.ts +++ b/packages/plugin-twitter/src/client/timeline-v2.ts @@ -1,13 +1,13 @@ -import { LegacyUserRaw } from './profile'; +import type { LegacyUserRaw } from './profile'; import { parseMediaGroups, reconstructTweetHtml } from './timeline-tweet-util'; -import { +import type { LegacyTweetRaw, ParseTweetResult, QueryTweetsResponse, SearchResultRaw, TimelineResultRaw, } from './timeline-v1'; -import { Tweet } from './tweets'; +import type { Tweet } from './tweets'; import { isFieldDefined } from './type-util'; export interface TimelineUserResultRaw { @@ -214,7 +214,7 @@ export function parseLegacyTweet( } } - const views = parseInt(tweet.ext_views?.count ?? ''); + const views = Number.parseInt(tweet.ext_views?.count ?? ''); if (!isNaN(views)) { tw.views = views; } @@ -251,7 +251,7 @@ function parseResult(result?: TimelineResultRaw): ParseTweetResult { } if (!tweetResult.tweet.views && result?.views?.count) { - const views = parseInt(result.views.count); + const views = Number.parseInt(result.views.count); if (!isNaN(views)) { tweetResult.tweet.views = views; } diff --git a/packages/plugin-twitter/src/client/trends.ts b/packages/plugin-twitter/src/client/trends.ts index c4d0e01963e..73b18e91616 100644 --- a/packages/plugin-twitter/src/client/trends.ts +++ b/packages/plugin-twitter/src/client/trends.ts @@ -1,6 +1,6 @@ import { addApiParams, requestApi } from './api'; -import { TwitterAuth } from './auth'; -import { TimelineV1 } from './timeline-v1'; +import type { TwitterAuth } from './auth'; +import type { TimelineV1 } from './timeline-v1'; export async function getTrends(auth: TwitterAuth): Promise { const params = new URLSearchParams(); diff --git a/packages/plugin-twitter/src/client/tweets.test.ts b/packages/plugin-twitter/src/client/tweets.test.ts index 3cf3e96dd17..bf1c87c151e 100644 --- a/packages/plugin-twitter/src/client/tweets.test.ts +++ b/packages/plugin-twitter/src/client/tweets.test.ts @@ -1,6 +1,6 @@ import { getScraper } from './test-utils'; -import { QueryTweetsResponse } from './timeline-v1'; -import { Mention, Tweet, getTweetAnonymous } from './tweets'; +import type { QueryTweetsResponse } from './timeline-v1'; +import { type Mention, type Tweet, getTweetAnonymous } from './tweets'; import fs from 'fs'; import path from 'path'; diff --git a/packages/plugin-twitter/src/client/tweets.ts b/packages/plugin-twitter/src/client/tweets.ts index 15ae877b0c3..1b2741f4d39 100644 --- a/packages/plugin-twitter/src/client/tweets.ts +++ b/packages/plugin-twitter/src/client/tweets.ts @@ -1,22 +1,22 @@ import { addApiFeatures, requestApi } from './api'; -import { TwitterAuth } from './auth'; +import type { TwitterAuth } from './auth'; import { getUserIdByScreenName } from './profile'; -import { QueryTweetsResponse } from './timeline-v1'; +import type { QueryTweetsResponse } from './timeline-v1'; import { parseTimelineTweetsV2, - TimelineV2, - TimelineEntryItemContentRaw, + type TimelineV2, + type TimelineEntryItemContentRaw, parseTimelineEntryItemContentRaw, - ThreadedConversation, + type ThreadedConversation, parseThreadedConversation, parseArticle, - TimelineArticle, + type TimelineArticle, } from './timeline-v2'; import { getTweetTimeline } from './timeline-async'; import { apiRequestFactory } from './api-data'; -import { ListTimeline, parseListTimelineTweets } from './timeline-list'; +import { type ListTimeline, parseListTimelineTweets } from './timeline-list'; import { updateCookieJar } from './requests'; -import { +import type { ApiV2Includes, MediaObjectV2, PlaceV2, diff --git a/packages/plugin-twitter/src/client/type-util.ts b/packages/plugin-twitter/src/client/type-util.ts index f987a4c0350..2955962d960 100644 --- a/packages/plugin-twitter/src/client/type-util.ts +++ b/packages/plugin-twitter/src/client/type-util.ts @@ -3,9 +3,7 @@ export type NonNullableField = { } & T; export function isFieldDefined(key: K) { - return function (value: T): value is NonNullableField { - return isDefined(value[key]); - }; + return (value: T): value is NonNullableField => isDefined(value[key]); } export function isDefined(value: T | null | undefined): value is T { diff --git a/packages/plugin-twitter/src/post.ts b/packages/plugin-twitter/src/post.ts index ea4d98e5532..23a24ddc576 100644 --- a/packages/plugin-twitter/src/post.ts +++ b/packages/plugin-twitter/src/post.ts @@ -14,7 +14,7 @@ import { import type { ClientBase } from "./base.ts"; import type { Tweet } from "./client/index.ts"; import { DEFAULT_MAX_TWEET_LENGTH } from "./environment.ts"; -import { MediaData } from "./types.ts"; +import type { MediaData } from "./types.ts"; import { fetchMediaData } from "./utils.ts"; const twitterPostTemplate = ` diff --git a/packages/plugin-twitter/src/spaces.ts b/packages/plugin-twitter/src/spaces.ts index d978369f5f8..f77dbbbda0a 100644 --- a/packages/plugin-twitter/src/spaces.ts +++ b/packages/plugin-twitter/src/spaces.ts @@ -5,7 +5,7 @@ import { generateText, ModelClass, type TwitterSpaceDecisionOptions, - State, + type State, } from "@elizaos/core"; import type { ClientBase } from "./base.ts"; import { diff --git a/packages/plugin-twitter/src/utils.ts b/packages/plugin-twitter/src/utils.ts index d35f5ba4457..df05318f44d 100644 --- a/packages/plugin-twitter/src/utils.ts +++ b/packages/plugin-twitter/src/utils.ts @@ -6,7 +6,7 @@ import { logger } from "@elizaos/core"; import type { Media } from "@elizaos/core"; import fs from "fs"; import path from "path"; -import { ActionResponse, MediaData } from "./types"; +import type { ActionResponse, MediaData } from "./types"; export const wait = (minTime = 1000, maxTime = 3000) => { const waitTime = diff --git a/turbo.json b/turbo.json index 35140336b1f..d425a58939b 100644 --- a/turbo.json +++ b/turbo.json @@ -16,6 +16,9 @@ "test": { "persistent": true, "dependsOn": ["build"] + }, + "lint:fix": { + "dependsOn": ["build"] } } } \ No newline at end of file From a5d7e9b5f1070369f5b97975dbbfbb7aae00d874 Mon Sep 17 00:00:00 2001 From: Shaw Date: Tue, 11 Feb 2025 00:34:51 -0500 Subject: [PATCH 3/3] update biome --- biome.json | 23 ----------------------- packages/plugin-anthropic/biome.json | 15 --------------- packages/plugin-bootstrap/biome.json | 15 --------------- packages/plugin-discord/biome.json | 15 --------------- packages/plugin-drizzle/biome.json | 15 --------------- packages/plugin-local-ai/biome.json | 15 --------------- packages/plugin-node/biome.json | 15 --------------- packages/plugin-openai/biome.json | 15 --------------- packages/plugin-sqlite/biome.json | 15 --------------- packages/plugin-tee/biome.json | 15 --------------- packages/plugin-telegram/biome.json | 15 --------------- packages/plugin-twitter/biome.json | 15 --------------- 12 files changed, 188 deletions(-) diff --git a/biome.json b/biome.json index e578c13ad0d..125fb2ecf25 100644 --- a/biome.json +++ b/biome.json @@ -8,14 +8,12 @@ "rules": { "recommended": true, "suspicious": { - "noExplicitAny": "warn", "noArrayIndexKey": "warn", "noPrototypeBuiltins": "warn", "noDuplicateObjectKeys": "warn", "noGlobalIsNan": "warn", "noSelfCompare": "warn", "noDoubleEquals": "warn", - "noImplicitAnyLet": "warn", "noAssignInExpressions": "warn", "noConstEnum": "warn", "noEmptyInterface": "warn" @@ -28,32 +26,11 @@ "noUnnecessaryContinue": "warn", "noInnerDeclarations": "warn" }, - "style": { - "useConst": "warn", - "useTemplate": "warn", - "noUselessElse": "warn", - "useSelfClosingElements": "warn", - "noUnusedTemplateLiteral": "warn", - "noInferrableTypes": "warn", - "noNonNullAssertion": "warn", - "noParameterAssign": "warn", - "useDefaultParameterLast": "warn", - "useExponentiationOperator": "warn", - "noVar": "warn", - "useSingleVarDeclarator": "warn" - }, - "a11y": { - "useAltText": "warn", - "useMediaCaption": "warn", - "noSvgWithoutTitle": "warn", - "useKeyWithClickEvents": "warn" - }, "complexity": { "noForEach": "warn", "useOptionalChain": "warn", "useArrowFunction": "warn", "useFlatMap": "warn", - "useLiteralKeys": "warn", "noBannedTypes": "warn", "noStaticOnlyClass": "warn", "noThisInStatic": "warn", diff --git a/packages/plugin-anthropic/biome.json b/packages/plugin-anthropic/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-anthropic/biome.json +++ b/packages/plugin-anthropic/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-bootstrap/biome.json b/packages/plugin-bootstrap/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-bootstrap/biome.json +++ b/packages/plugin-bootstrap/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-discord/biome.json b/packages/plugin-discord/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-discord/biome.json +++ b/packages/plugin-discord/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-drizzle/biome.json b/packages/plugin-drizzle/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-drizzle/biome.json +++ b/packages/plugin-drizzle/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-local-ai/biome.json b/packages/plugin-local-ai/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-local-ai/biome.json +++ b/packages/plugin-local-ai/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-node/biome.json b/packages/plugin-node/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-node/biome.json +++ b/packages/plugin-node/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-openai/biome.json b/packages/plugin-openai/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-openai/biome.json +++ b/packages/plugin-openai/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-sqlite/biome.json b/packages/plugin-sqlite/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-sqlite/biome.json +++ b/packages/plugin-sqlite/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-tee/biome.json b/packages/plugin-tee/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-tee/biome.json +++ b/packages/plugin-tee/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-telegram/biome.json b/packages/plugin-telegram/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-telegram/biome.json +++ b/packages/plugin-telegram/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space", diff --git a/packages/plugin-twitter/biome.json b/packages/plugin-twitter/biome.json index 7a635ccfaec..d7e0349301a 100644 --- a/packages/plugin-twitter/biome.json +++ b/packages/plugin-twitter/biome.json @@ -4,21 +4,6 @@ "organizeImports": { "enabled": false }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "correctness": { - "noUnusedVariables": "error" - }, - "suspicious": { - "noExplicitAny": "error" - }, - "style": { - "useConst": "error" - } - } - }, "formatter": { "enabled": true, "indentStyle": "space",