diff --git a/agent/package.json b/agent/package.json index 4d2e88e48b..7a2bfc7558 100644 --- a/agent/package.json +++ b/agent/package.json @@ -66,9 +66,7 @@ "@elizaos/plugin-icp": "workspace:*", "@elizaos/plugin-initia": "workspace:*", "@elizaos/plugin-image-generation": "workspace:*", - "@elizaos/plugin-intiface": "workspace:*", "@elizaos/plugin-lens-network": "workspace:*", - "@elizaos/plugin-letzai": "workspace:*", "@elizaos/plugin-lit": "workspace:*", "@elizaos/plugin-massa": "workspace:*", "@elizaos/plugin-mind-network": "workspace:*", diff --git a/docs/community/Discord/collaborations/3d-ai-tv/chat_2024-12-07.md b/docs/community/Discord/collaborations/3d-ai-tv/chat_2024-12-07.md index a41aa77f3d..062f542cb9 100644 --- a/docs/community/Discord/collaborations/3d-ai-tv/chat_2024-12-07.md +++ b/docs/community/Discord/collaborations/3d-ai-tv/chat_2024-12-07.md @@ -15,7 +15,7 @@ The conversation focused on integrating @bigdookie's artwork as bumpers in their - Do we need video producers? Why is it complicated for comfy stuff to be fast-paced? (asked by [boom](09:56)) - What are the next steps in establishing a Creative Studio and bidding on projects? How does budget influence project success? (asked by [whobody, boom](10:27)) - How will the open-source approach help us? How can Banodoco handle bids on their end? (asked by [boom (10:00)]) -- Can we prompt an engineer to help the story arch or main punchlines for AI-assisted writing? How does it come together with human and AI collaboration in filmmaking? (asked by [boom] (10:05)) +- Can we prompt an engineer to help the story arc or main punchlines for AI-assisted writing? How does it come together with human and AI collaboration in filmmaking? (asked by [boom] (10:05)) ## Who Helped Who diff --git a/docs/community/Discord/development/coders/chat_2024-10-27.md b/docs/community/Discord/development/coders/chat_2024-10-27.md index 5020d493c0..7589eb8ad9 100644 --- a/docs/community/Discord/development/coders/chat_2024-10-27.md +++ b/docs/community/Discord/development/coders/chat_2024-10-27.md @@ -2,7 +2,7 @@ ## Summary -In the chat, Cyfer785 sought assistance for creating a frontend interface that would display AI-generated content in a retro style with live scrolling text, reminiscent of Claude's capabilities but tailored to their own AI pipe output. DegenSpartan and Poe engaged in the conversation, suggesting that Cyfer785 was essentially attempting to replicate features from existing projects like Infinite Backrooms and Claude without adding original value. The discussion highlighted a divergence of interests as Cyfer785's vision did not align with what DegenSpartan and Poe were willing or able to provide, leading to the conclusion that they were not suitable collaborators for this project. Despite some initial enthusiasm from other participants like astr0x., who humorously claimed a share of non-existent profits, the technical focus remained on Cyfer785's request for a unique AI interface development. +In the chat, Cyfer785 sought assistance for creating a frontend interface that would display AI-generated content in a retro style with live scrolling text, reminiscent of Claude's capabilities but tailored to their own AI pipe output. DegenSpartan and Poe engaged in the conversation, suggesting that Cyfer785 was essentially attempting to replicate features from existing projects like Infinite Backrooms and Claude without adding original value. The discussion highlighted a divergence of interests as Cyfer785's vision did not align with what DegenSpartan and Poe were willing or able to provide, leading to the conclusion that they were not suitable collaborators for this project. Despite some initial enthusiasm from other participants like astr0x, who humorously claimed a share of non-existent profits, the technical focus remained on Cyfer785's request for a unique AI interface development. ## FAQ diff --git a/package.json b/package.json index 00d43be54d..05a1c67f86 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@polkadot/types-codec": "10.13.1", "@polkadot/keyring": "12.6.2", "@ai-sdk/provider": "1.0.6", - "@ai-sdk/provider-utils": "2.1.2", + "@ai-sdk/provider-utils": "2.1.6", "cookie": "0.7.0", "bs58": "5.0.0", "@coral-xyz/anchor": "0.28.0" diff --git a/packages/core/src/parsing.ts b/packages/core/src/parsing.ts index ebe4f61440..ffdc7f096c 100644 --- a/packages/core/src/parsing.ts +++ b/packages/core/src/parsing.ts @@ -152,7 +152,7 @@ export function parseJSONObjectFromText( } catch (e) { console.error("Error parsing JSON:", e); console.error("Text is not JSON", text); - return extractAttributes(parsingText); + return extractAttributes(text); } } else { const objectPattern = /{[\s\S]*?}/; @@ -165,7 +165,7 @@ export function parseJSONObjectFromText( } catch (e) { console.error("Error parsing JSON:", e); console.error("Text is not JSON", text); - return extractAttributes(parsingText); + return extractAttributes(text); } } } diff --git a/packages/plugin-0g/biome.json b/packages/plugin-0g/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-0g/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-0g/eslint.config.mjs b/packages/plugin-0g/eslint.config.mjs deleted file mode 100644 index 92fe5bbebe..0000000000 --- a/packages/plugin-0g/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import eslintGlobalConfig from "../../eslint.config.mjs"; - -export default [...eslintGlobalConfig]; diff --git a/packages/plugin-0g/package.json b/packages/plugin-0g/package.json index 8fddc8a82b..89b4ca1f2d 100644 --- a/packages/plugin-0g/package.json +++ b/packages/plugin-0g/package.json @@ -25,12 +25,16 @@ "tsup": "8.3.5" }, "devDependencies": { + "@biomejs/biome": "1.5.3", "vitest": "^1.2.1" }, "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", "test": "vitest run", - "lint": "eslint --fix --cache ." + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" } } diff --git a/packages/plugin-0g/src/actions/upload.ts b/packages/plugin-0g/src/actions/upload.ts index 2361583180..639da61a14 100644 --- a/packages/plugin-0g/src/actions/upload.ts +++ b/packages/plugin-0g/src/actions/upload.ts @@ -11,9 +11,9 @@ import { elizaLogger, } from "@elizaos/core"; import { Indexer, ZgFile, getFlowContract } from "@0glabs/0g-ts-sdk"; -import { ethers } from "ethers"; +import { ethers, Wallet } from "ethers"; import { composeContext } from "@elizaos/core"; -import { promises as fs } from "fs"; +import { promises as fs, type Stats } from "node:fs"; import { FileSecurityValidator } from "../utils/security"; import { logSecurityEvent, monitorUpload, monitorFileValidation, monitorCleanup } from '../utils/monitoring'; import { uploadTemplate } from "../templates/upload"; @@ -24,10 +24,10 @@ export interface UploadContent extends Content { function isUploadContent( _runtime: IAgentRuntime, - content: any + content: unknown ): content is UploadContent { elizaLogger.debug("Validating upload content", { content }); - return typeof content.filePath === "string"; + return typeof content === "object" && content !== null && "filePath" in content && typeof (content as UploadContent).filePath === "string"; } export const zgUpload: Action = { @@ -82,7 +82,7 @@ export const zgUpload: Action = { }; // Validate config values - if (isNaN(config.maxFileSize) || config.maxFileSize <= 0) { + if (Number.isNaN(config.maxFileSize) || config.maxFileSize <= 0) { elizaLogger.error("Invalid ZEROG_MAX_FILE_SIZE setting", { value: runtime.getSetting("ZEROG_MAX_FILE_SIZE"), messageId: message.id @@ -117,7 +117,7 @@ export const zgUpload: Action = { runtime: IAgentRuntime, message: Memory, state: State, - _options: any, + _options: Record, callback: HandlerCallback ) => { elizaLogger.info("ZG_UPLOAD action started", { @@ -131,18 +131,20 @@ export const zgUpload: Action = { try { // Update state if needed - if (!state) { + // Initialize or update state + let currentState = state; + if (!currentState) { elizaLogger.debug("No state provided, composing new state"); - state = (await runtime.composeState(message)) as State; + currentState = (await runtime.composeState(message)) as State; } else { elizaLogger.debug("Updating existing state"); - state = await runtime.updateRecentMessageState(state); + currentState = await runtime.updateRecentMessageState(currentState); } // Compose upload context elizaLogger.debug("Composing upload context"); const uploadContext = composeContext({ - state, + state: currentState, template: uploadTemplate, }); @@ -307,7 +309,7 @@ export const zgUpload: Action = { // Start upload monitoring const startTime = Date.now(); - let fileStats; + let fileStats: Stats; try { fileStats = await fs.stat(sanitizedPath); elizaLogger.debug("File stats retrieved", { @@ -365,7 +367,7 @@ export const zgUpload: Action = { const provider = new ethers.JsonRpcProvider(runtime.getSetting("ZEROG_EVM_RPC")); const signer = new ethers.Wallet(runtime.getSetting("ZEROG_PRIVATE_KEY"), provider); const indexer = new Indexer(runtime.getSetting("ZEROG_INDEXER_RPC")); - const flowContract = getFlowContract(runtime.getSetting("ZEROG_FLOW_ADDRESS"), signer); + const flowContract = getFlowContract(runtime.getSetting("ZEROG_FLOW_ADDRESS"), signer as any); // Upload file to ZeroG elizaLogger.info("Starting file upload to ZeroG", { diff --git a/packages/plugin-0g/src/utils/security.ts b/packages/plugin-0g/src/utils/security.ts index 2f84af5dc2..6b2e3df898 100644 --- a/packages/plugin-0g/src/utils/security.ts +++ b/packages/plugin-0g/src/utils/security.ts @@ -1,5 +1,5 @@ -import { promises as fs } from 'fs'; -import path from 'path'; +import { promises as fs } from 'node:fs'; +import path from 'node:path'; export interface SecurityConfig { maxFileSize: number; diff --git a/packages/plugin-0x/biome.json b/packages/plugin-0x/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-0x/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-0x/package.json b/packages/plugin-0x/package.json index 517c3f96fe..975b84a391 100644 --- a/packages/plugin-0x/package.json +++ b/packages/plugin-0x/package.json @@ -21,13 +21,21 @@ "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", - "test": "vitest run" + "test": "vitest run", + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" }, "dependencies": { + "@elizaos/core": "workspace:*", + "whatwg-url": "7.1.0", "@0x/swap-ts-sdk": "2.1.1" }, "devDependencies": { - "tsup": "^8.0.1" + "tsup": "^8.0.1", + "@biomejs/biome": "1.5.3", + "vitest": "^2.1.5" }, "peerDependencies": { "@elizaos/core": "workspace:*", diff --git a/packages/plugin-0x/src/EVMtokenRegistry.ts b/packages/plugin-0x/src/EVMtokenRegistry.ts index 51a674b941..39075f71a5 100644 --- a/packages/plugin-0x/src/EVMtokenRegistry.ts +++ b/packages/plugin-0x/src/EVMtokenRegistry.ts @@ -1,9 +1,9 @@ import { elizaLogger } from "@elizaos/core"; import { Chains, - TokenMetadata, - TrustWalletGithubJson, - TrustWalletTokenMetadata, + type TokenMetadata, + type TrustWalletGithubJson, + type TrustWalletTokenMetadata, } from "./types"; import { NATIVE_TOKENS } from "./constants"; diff --git a/packages/plugin-0x/src/actions/getIndicativePrice.ts b/packages/plugin-0x/src/actions/getIndicativePrice.ts index b73ceee4ca..7a9a469d46 100644 --- a/packages/plugin-0x/src/actions/getIndicativePrice.ts +++ b/packages/plugin-0x/src/actions/getIndicativePrice.ts @@ -1,9 +1,9 @@ import { - Action, - IAgentRuntime, - Memory, - State, - HandlerCallback, + type Action, + type IAgentRuntime, + type Memory, + type State, + type HandlerCallback, elizaLogger, composeContext, ModelClass, @@ -13,7 +13,7 @@ import { import { createClientV2 } from "@0x/swap-ts-sdk"; import { getIndicativePriceTemplate } from "../templates"; import { z } from "zod"; -import { Chains, GetIndicativePriceResponse, PriceInquiry } from "../types"; +import { Chains, type GetIndicativePriceResponse, type PriceInquiry } from "../types"; import { parseUnits } from "viem"; import { CHAIN_NAMES, ZX_MEMORY } from "../constants"; import { EVMTokenRegistry } from "../EVMtokenRegistry"; @@ -45,17 +45,17 @@ export const getIndicativePrice: Action = { runtime: IAgentRuntime, message: Memory, state: State, - options: Record, + _options: Record, callback: HandlerCallback ) => { const supportedChains = Object.keys(Chains).join(" | "); - state = !state + const localState = !state ? await runtime.composeState(message, { supportedChains }) : await runtime.updateRecentMessageState(state); const context = composeContext({ - state, + state: localState, template: getIndicativePriceTemplate, }); @@ -86,7 +86,7 @@ export const getIndicativePrice: Action = { text: `Unsupported chain: ${chain}. Supported chains are: ${Object.keys( Chains ) - .filter((k) => isNaN(Number(k))) + .filter((k) => !Number.isNaN(Number(k))) .join(", ")}`, }); return; @@ -148,10 +148,10 @@ export const getIndicativePrice: Action = { // Format amounts to human-readable numbers const buyAmount = Number(price.buyAmount) / - Math.pow(10, buyTokenMetadata.decimals); + (10 ** buyTokenMetadata.decimals); const sellAmount = Number(price.sellAmount) / - Math.pow(10, sellTokenMetadata.decimals); + (10 ** sellTokenMetadata.decimals); await storePriceInquiryToMemory(runtime, message, { sellTokenObject: sellTokenMetadata, @@ -163,13 +163,13 @@ export const getIndicativePrice: Action = { // Updated formatted response to include chain const formattedResponse = [ - `💱 Swap Details:`, - `────────────────`, + "💱 Swap Details:", + "────────────────", `📤 Sell: ${sellAmount.toFixed(4)} ${sellTokenMetadata.symbol}`, `📥 Buy: ${buyAmount.toFixed(4)} ${buyTokenMetadata.symbol}`, `📊 Rate: 1 ${sellTokenMetadata.symbol} = ${(buyAmount / sellAmount).toFixed(4)} ${buyTokenMetadata.symbol}`, `🔗 Chain: ${CHAIN_NAMES[chainId]}`, - `────────────────`, + "────────────────", `💫 Happy with the price? Type 'quote' to continue`, ].join("\n"); diff --git a/packages/plugin-0x/src/actions/getQuote.ts b/packages/plugin-0x/src/actions/getQuote.ts index 9d50beb94d..d4df553fe7 100644 --- a/packages/plugin-0x/src/actions/getQuote.ts +++ b/packages/plugin-0x/src/actions/getQuote.ts @@ -1,13 +1,13 @@ import { - Action, - IAgentRuntime, - Memory, - State, - HandlerCallback, + type Action, + type IAgentRuntime, + type Memory, + type State, + type HandlerCallback, elizaLogger, MemoryManager, } from "@elizaos/core"; -import { GetQuoteResponse, PriceInquiry, Quote } from "../types"; +import type { GetQuoteResponse, PriceInquiry, Quote } from "../types"; import { formatTokenAmount } from "../utils"; import { CHAIN_NAMES, NATIVE_TOKENS, ZX_MEMORY } from "../constants"; import { createClientV2 } from "@0x/swap-ts-sdk"; @@ -25,8 +25,8 @@ export const getQuote: Action = { handler: async ( runtime: IAgentRuntime, message: Memory, - state: State, - options: Record, + _state: State, + _options: Record, callback: HandlerCallback ) => { const latestPriceInquiry = await retrieveLatestPriceInquiry( @@ -89,7 +89,7 @@ export const getQuote: Action = { const warnings = []; if (quote.issues?.balance) { warnings.push( - `⚠️ Warnings:`, + "⚠️ Warnings:", ` • Insufficient balance (Have ${formatTokenAmount( quote.issues.balance.actual, quote.issues.balance.token, @@ -99,8 +99,8 @@ export const getQuote: Action = { } const formattedResponse = [ - `🎯 Firm Quote Details:`, - `────────────────`, + "🎯 Firm Quote Details:", + "────────────────", // Basic swap details (same as price) `📤 Sell: ${formatTokenAmount( quote.sellAmount, @@ -125,7 +125,7 @@ export const getQuote: Action = { )}`, // Fee breakdown - `💰 Fees Breakdown:`, + "💰 Fees Breakdown:", ` • 0x Protocol Fee: ${formatTokenAmount( quote.fees.zeroExFee?.amount, quote.fees.zeroExFee?.token, @@ -153,8 +153,8 @@ export const getQuote: Action = { ...(warnings.length > 0 ? warnings : []), - `────────────────`, - `💫 Ready to execute? Type 'execute' to continue`, + "────────────────", + "💫 Ready to execute? Type 'execute' to continue", ] .filter(Boolean) .join("\n"); @@ -223,20 +223,20 @@ export const getQuote: Action = { ], }; -const formatTime = (time: string) => { - const expirationDate = new Date(parseInt(time) * 1000); +// const formatTime = (time: string) => { +// const expirationDate = new Date(parseInt(time) * 1000); - // Format: "Mar 15, 2:30 PM" - const formattedTime = expirationDate.toLocaleString(undefined, { - month: "short", - day: "numeric", - hour: "numeric", - minute: "2-digit", - hour12: true, - }); +// // Format: "Mar 15, 2:30 PM" +// const formattedTime = expirationDate.toLocaleString(undefined, { +// month: "short", +// day: "numeric", +// hour: "numeric", +// minute: "2-digit", +// hour12: true, +// }); - return `${formattedTime}`; -}; +// return `${formattedTime}`; +// }; export const retrieveLatestPriceInquiry = async ( runtime: IAgentRuntime, @@ -260,7 +260,7 @@ export const retrieveLatestPriceInquiry = async ( } return null; } catch (error) { - elizaLogger.error(`Failed to retrieve price inquiry: ${error.message}`); + elizaLogger.error("Failed to retrieve price inquiry:", error.message); return null; } }; diff --git a/packages/plugin-0x/src/actions/swap.ts b/packages/plugin-0x/src/actions/swap.ts index 17da272144..2183d8be3d 100644 --- a/packages/plugin-0x/src/actions/swap.ts +++ b/packages/plugin-0x/src/actions/swap.ts @@ -1,16 +1,16 @@ import { - Action, - IAgentRuntime, - Memory, - State, - HandlerCallback, + type Action, + type IAgentRuntime, + type Memory, + type State, + type HandlerCallback, elizaLogger, MemoryManager, } from "@elizaos/core"; -import { Hex, numberToHex, concat } from "viem"; +import { type Hex, numberToHex, concat } from "viem"; import { CHAIN_EXPLORERS, ZX_MEMORY } from "../constants"; import { getWalletClient } from "../hooks.ts/useGetWalletClient"; -import { Quote } from "../types"; +import type { Quote } from "../types"; export const swap: Action = { name: "EXECUTE_SWAP_0X", @@ -31,8 +31,8 @@ export const swap: Action = { handler: async ( runtime: IAgentRuntime, message: Memory, - state: State, - options: Record, + _state: State, + _options: Record, callback: HandlerCallback ) => { const latestQuote = await retrieveLatestQuote(runtime, message); @@ -99,13 +99,12 @@ export const swap: Action = { content: { hash: txHash, status: "success" }, }); return true; - } else { - callback({ - text: `❌ Swap failed! Check transaction: ${CHAIN_EXPLORERS[chainId]}/tx/${txHash}`, - content: { hash: txHash, status: "failed" }, - }); - return false; } + callback({ + text: `❌ Swap failed! Check transaction: ${CHAIN_EXPLORERS[chainId]}/tx/${txHash}`, + content: { hash: txHash, status: "failed" }, + }); + return false; } catch (error) { elizaLogger.error("Swap execution failed:", error); callback({ diff --git a/packages/plugin-0x/src/constants.ts b/packages/plugin-0x/src/constants.ts index f51220a229..f161bbb072 100644 --- a/packages/plugin-0x/src/constants.ts +++ b/packages/plugin-0x/src/constants.ts @@ -1,4 +1,4 @@ -import { Chains, TokenMetadata } from "./types"; +import { Chains, type TokenMetadata } from "./types"; export const ZX_MEMORY = { price: { diff --git a/packages/plugin-0x/src/hooks.ts/useGetWalletClient.ts b/packages/plugin-0x/src/hooks.ts/useGetWalletClient.ts index 5bc7238bb3..25516a3808 100644 --- a/packages/plugin-0x/src/hooks.ts/useGetWalletClient.ts +++ b/packages/plugin-0x/src/hooks.ts/useGetWalletClient.ts @@ -3,8 +3,8 @@ import { http, publicActions, createTestClient, - WalletClient, - PublicClient, + type WalletClient, + type PublicClient, walletActions, } from "viem"; diff --git a/packages/plugin-0x/src/index.ts b/packages/plugin-0x/src/index.ts index f7f493eae2..a8fd147113 100644 --- a/packages/plugin-0x/src/index.ts +++ b/packages/plugin-0x/src/index.ts @@ -1,4 +1,4 @@ -import { Plugin } from "@elizaos/core"; +import type { Plugin } from "@elizaos/core"; import { getIndicativePrice } from "./actions/getIndicativePrice"; import { getQuote } from "./actions/getQuote"; import { swap } from "./actions/swap"; diff --git a/packages/plugin-3d-generation/biome.json b/packages/plugin-3d-generation/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-3d-generation/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-3d-generation/package.json b/packages/plugin-3d-generation/package.json index cd3e821b0a..85e57b6121 100644 --- a/packages/plugin-3d-generation/package.json +++ b/packages/plugin-3d-generation/package.json @@ -24,12 +24,17 @@ "whatwg-url": "7.1.0" }, "devDependencies": { + "@biomejs/biome": "1.5.3", "vitest": "^2.1.5" }, "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", - "test": "vitest run" + "test": "vitest run", + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" }, "peerDependencies": { "whatwg-url": "7.1.0" diff --git a/packages/plugin-3d-generation/src/index.ts b/packages/plugin-3d-generation/src/index.ts index f0d2bb2e5d..b6b516bee1 100644 --- a/packages/plugin-3d-generation/src/index.ts +++ b/packages/plugin-3d-generation/src/index.ts @@ -10,10 +10,10 @@ import type { import { fal } from "@fal-ai/client"; import { FAL_CONSTANTS } from "./constants"; -import * as fs from "fs"; -import { Buffer } from "buffer"; -import * as path from "path"; -import * as process from "process"; +import * as fs from "node:fs"; +import { Buffer } from "node:buffer"; +import * as path from "node:path"; +import * as process from "node:process"; const generate3D = async (prompt: string, runtime: IAgentRuntime) => { process.env["FAL_KEY"] = @@ -84,7 +84,7 @@ const ThreeDGeneration: Action = { runtime: IAgentRuntime, message: Memory, _state: State, - _options: any, + _options: Record, callback: HandlerCallback ) => { elizaLogger.log("3D generation request:", message); diff --git a/packages/plugin-abstract/biome.json b/packages/plugin-abstract/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-abstract/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-abstract/eslint.config.mjs b/packages/plugin-abstract/eslint.config.mjs deleted file mode 100644 index 92fe5bbebe..0000000000 --- a/packages/plugin-abstract/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import eslintGlobalConfig from "../../eslint.config.mjs"; - -export default [...eslintGlobalConfig]; diff --git a/packages/plugin-abstract/package.json b/packages/plugin-abstract/package.json index 4795f922ca..65a97d495e 100644 --- a/packages/plugin-abstract/package.json +++ b/packages/plugin-abstract/package.json @@ -25,7 +25,10 @@ "viem": "2.22.2" }, "scripts": { - "lint": "eslint --fix --cache .", + "lint": "biome lint .", + "lint:fix": "biome check --apply .", + "format": "biome format .", + "format:fix": "biome format --write .", "build": "tsup --format esm --no-dts", "dev": "tsup --format esm --no-dts --watch", "test": "vitest run", @@ -33,6 +36,7 @@ "test:coverage": "vitest run --coverage" }, "devDependencies": { + "@biomejs/biome": "1.9.4", "tsup": "8.3.5", "typescript": "4.9", "vitest": "^1.0.0" diff --git a/packages/plugin-abstract/src/actions/deployTokenAction.ts b/packages/plugin-abstract/src/actions/deployTokenAction.ts index e06a22fbf8..83c30946a2 100644 --- a/packages/plugin-abstract/src/actions/deployTokenAction.ts +++ b/packages/plugin-abstract/src/actions/deployTokenAction.ts @@ -99,15 +99,17 @@ export const deployTokenAction: Action = { ): Promise => { elizaLogger.log("Starting Abstract DEPLOY_TOKEN handler..."); - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); - } + // Initialize or update state + let currentState = state; + if (!currentState) { + currentState = (await runtime.composeState(message)) as State; + } else { + currentState = await runtime.updateRecentMessageState(currentState); + } - state.currentMessage = `${state.recentMessagesData[1].content.text}`; + currentState.currentMessage = `${currentState.recentMessagesData[1].content.text}`; const deployContext = composeContext({ - state, + state: currentState, template: deployTemplate, }); diff --git a/packages/plugin-abstract/src/actions/getBalanceAction.ts b/packages/plugin-abstract/src/actions/getBalanceAction.ts index 9b4c68ecd0..602fe37567 100644 --- a/packages/plugin-abstract/src/actions/getBalanceAction.ts +++ b/packages/plugin-abstract/src/actions/getBalanceAction.ts @@ -76,7 +76,7 @@ export const getBalanceAction: Action = { "BALANCE_CHECK", "TOKEN_BALANCE", ], - validate: async (runtime: IAgentRuntime, message: Memory) => { + validate: async (runtime: IAgentRuntime, _message: Memory) => { await validateAbstractConfig(runtime); return true; }, @@ -90,17 +90,18 @@ export const getBalanceAction: Action = { ): Promise => { elizaLogger.log("Starting Abstract GET_BALANCE handler..."); - // Initialize or update state - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); - } + // Initialize or update state + let currentState = state; + if (!currentState) { + currentState = (await runtime.composeState(message)) as State; + } else { + currentState = await runtime.updateRecentMessageState(currentState); + } // Compose balance context - state.currentMessage = `${state.recentMessagesData[1].content.text}`; + currentState.currentMessage = `${currentState.recentMessagesData[1].content.text}`; const balanceContext = composeContext({ - state, + state: currentState, template: balanceTemplate, }); diff --git a/packages/plugin-abstract/src/actions/transferAction.ts b/packages/plugin-abstract/src/actions/transferAction.ts index a11eeef810..4cc3f996cc 100644 --- a/packages/plugin-abstract/src/actions/transferAction.ts +++ b/packages/plugin-abstract/src/actions/transferAction.ts @@ -26,6 +26,27 @@ import { getTokenByName, } from "../utils/viemHelpers"; +// Define types for Abstract client +interface AbstractTransactionRequest { + chain: typeof abstractTestnet; + to: string; + value: bigint; + kzg: undefined; +} + +interface AbstractContractRequest { + chain: typeof abstractTestnet; + address: string; + abi: typeof erc20Abi; + functionName: string; + args: [string, bigint]; +} + +interface AbstractClient { + sendTransaction: (request: AbstractTransactionRequest) => Promise; + writeContract: (request: AbstractContractRequest) => Promise; +} + const TransferSchema = z.object({ tokenAddress: z.string().optional().nullable(), recipient: z.string(), @@ -107,17 +128,18 @@ export const transferAction: Action = { ): Promise => { elizaLogger.log("Starting Abstract SEND_TOKEN handler..."); - // Initialize or update state - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); - } + // Initialize or update state + let currentState = state; + if (!currentState) { + currentState = (await runtime.composeState(message)) as State; + } else { + currentState = await runtime.updateRecentMessageState(currentState); + } // Compose transfer context - state.currentMessage = `${state.recentMessagesData[1].content.text}`; + currentState.currentMessage = `${currentState.recentMessagesData[1].content.text}`; const transferContext = composeContext({ - state, + state: currentState, template: transferTemplate, }); @@ -204,7 +226,7 @@ export const transferAction: Action = { const abstractClient = (await createAbstractClient({ chain: abstractTestnet, signer: account, - })) as any; // biome-ignore lint/suspicious/noExplicitAny: type being exported as never + })) as AbstractClient; if (isEthTransfer) { hash = await abstractClient.sendTransaction({ diff --git a/packages/plugin-agentkit/biome.json b/packages/plugin-agentkit/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-agentkit/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-agentkit/package.json b/packages/plugin-agentkit/package.json index 19a716a51e..12b633ddc3 100644 --- a/packages/plugin-agentkit/package.json +++ b/packages/plugin-agentkit/package.json @@ -12,6 +12,7 @@ "tsup": "8.3.5" }, "devDependencies": { + "@biomejs/biome": "1.9.4", "vitest": "^1.0.0" }, "scripts": { @@ -19,6 +20,10 @@ "dev": "tsup --format esm --dts --watch", "test": "vitest run", "test:watch": "vitest watch", - "test:coverage": "vitest run --coverage" + "test:coverage": "vitest run --coverage", + "lint": "biome lint .", + "lint:fix": "biome check --apply .", + "format": "biome format .", + "format:fix": "biome format --write ." } } diff --git a/packages/plugin-agentkit/src/actions.ts b/packages/plugin-agentkit/src/actions.ts index 7bf3827c51..b08c65f927 100644 --- a/packages/plugin-agentkit/src/actions.ts +++ b/packages/plugin-agentkit/src/actions.ts @@ -37,7 +37,7 @@ export async function getAgentKitActions({ runtime: IAgentRuntime, message: Memory, state: State | undefined, - options?: Record, + _options?: Record, callback?: HandlerCallback ): Promise => { try { @@ -93,7 +93,7 @@ export async function getAgentKitActions({ async function executeToolAction( tool: Tool, - parameters: any, + parameters: unknown, client: CdpAgentkit ): Promise { const toolkit = new CdpToolkit(client); @@ -107,7 +107,7 @@ async function executeToolAction( return await selectedTool.call(parameters); } -function composeParameterContext(tool: any, state: State): string { +function composeParameterContext(tool: Tool, state: State): string { const contextTemplate = `{{recentMessages}} Given the recent messages, extract the following information for the action "${tool.name}": diff --git a/packages/plugin-agentkit/src/provider.ts b/packages/plugin-agentkit/src/provider.ts index f55719e2c1..038a9c0143 100644 --- a/packages/plugin-agentkit/src/provider.ts +++ b/packages/plugin-agentkit/src/provider.ts @@ -1,6 +1,6 @@ import type { Provider, IAgentRuntime } from "@elizaos/core"; import { CdpAgentkit } from "@coinbase/cdp-agentkit-core"; -import * as fs from "fs"; +import * as fs from "node:fs"; const WALLET_DATA_FILE = "wallet_data.txt"; @@ -46,10 +46,11 @@ export async function getClient(): Promise { } export const walletProvider: Provider = { - async get(runtime: IAgentRuntime): Promise { + async get(_runtime: IAgentRuntime): Promise { try { const client = await getClient(); - const address = (await (client as any).wallet.addresses)[0].id; + // Access wallet addresses using type assertion based on the known structure + const address = (client as unknown as { wallet: { addresses: Array<{ id: string }> } }).wallet.addresses[0].id; return `AgentKit Wallet Address: ${address}`; } catch (error) { console.error("Error in AgentKit provider:", error); diff --git a/packages/plugin-akash/biome.json b/packages/plugin-akash/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-akash/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-akash/package.json b/packages/plugin-akash/package.json index 8da2ca91a7..40bac98c74 100644 --- a/packages/plugin-akash/package.json +++ b/packages/plugin-akash/package.json @@ -9,7 +9,10 @@ "build": "tsup", "dev": "tsup --watch", "clean": "rm -rf dist", - "lint:fix": "eslint . --fix", + "lint": "biome lint .", + "lint:fix": "biome check --apply .", + "format": "biome format .", + "format:fix": "biome format --write .", "test": "vitest", "test:watch": "vitest watch", "test:coverage": "vitest run --coverage", @@ -32,15 +35,13 @@ "ora": "^8.0.1" }, "devDependencies": { + "@biomejs/biome": "1.9.4", "@types/dotenv": "^8.2.0", "@types/jest": "^29.5.11", "@types/js-yaml": "^4.0.9", "@types/node": "^20.10.5", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", "@vitest/coverage-v8": "^0.34.6", "@vitest/ui": "^0.34.6", - "eslint": "^9.16.0", "tsup": "^8.0.1", "typescript": "^5.3.3", "vite": "^5.0.10", diff --git a/packages/plugin-akash/src/actions/closeDeployment.ts b/packages/plugin-akash/src/actions/closeDeployment.ts index 47c7e17a64..1b6b9a9fc6 100644 --- a/packages/plugin-akash/src/actions/closeDeployment.ts +++ b/packages/plugin-akash/src/actions/closeDeployment.ts @@ -311,7 +311,7 @@ export const closeDeploymentAction: Action = { handler: async ( runtime: IAgentRuntime, message: Memory, - state: State | undefined, + _state: State | undefined, _options: { [key: string]: unknown } = {}, callback?: HandlerCallback ): Promise => { diff --git a/packages/plugin-akash/src/actions/createCertificate.ts b/packages/plugin-akash/src/actions/createCertificate.ts index 6e02659662..801d0e9863 100644 --- a/packages/plugin-akash/src/actions/createCertificate.ts +++ b/packages/plugin-akash/src/actions/createCertificate.ts @@ -8,8 +8,8 @@ import type { CertificatePem } from "@akashnetwork/akashjs/build/certificates/ce import { getAkashTypeRegistry } from "@akashnetwork/akashjs/build/stargate"; import { validateAkashConfig } from "../environment"; import { AkashError, AkashErrorCode, withRetry } from "../error/error"; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { Registry } from "@cosmjs/proto-signing"; import type { SigningStargateClient as AkashSigningStargateClient } from "@akashnetwork/akashjs/node_modules/@cosmjs/stargate"; import { getCertificatePath } from "../utils/paths"; @@ -271,7 +271,7 @@ export const createCertificateAction: Action = { handler: async ( runtime: IAgentRuntime, message: Memory, - state: State | undefined, + _state: State | undefined, options: { callback?: HandlerCallback } = {} ): Promise => { const actionId = Date.now().toString(); diff --git a/packages/plugin-akash/src/actions/createDeployment.ts b/packages/plugin-akash/src/actions/createDeployment.ts index 2032961fe4..d21cecd416 100644 --- a/packages/plugin-akash/src/actions/createDeployment.ts +++ b/packages/plugin-akash/src/actions/createDeployment.ts @@ -13,8 +13,8 @@ import { DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing"; import { SigningStargateClient } from "@cosmjs/stargate"; import { validateAkashConfig } from "../environment"; import { AkashError, AkashErrorCode, withRetry } from "../error/error"; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { getCertificatePath, getDefaultSDLPath } from "../utils/paths"; // import { fileURLToPath } from 'url'; import { inspectRuntime, isPluginLoaded } from "../runtime_inspect"; diff --git a/packages/plugin-akash/src/actions/getDeploymentApi.ts b/packages/plugin-akash/src/actions/getDeploymentApi.ts index 503be32530..fc46ac9ed3 100644 --- a/packages/plugin-akash/src/actions/getDeploymentApi.ts +++ b/packages/plugin-akash/src/actions/getDeploymentApi.ts @@ -3,8 +3,8 @@ import type { IAgentRuntime, Memory, State, HandlerCallback, Content, ActionExam import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import { validateAkashConfig } from "../environment"; import { AkashError, AkashErrorCode } from "../error/error"; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { getDeploymentsPath } from "../utils/paths"; export interface DeploymentInfo { @@ -48,7 +48,7 @@ async function fetchWithRetry(url: string, options: RequestInit, retries = 3, de }); if (i < retries - 1) { - await sleep(delay * Math.pow(2, i)); // Exponential backoff + await sleep(delay * (2 ** i)); // Exponential backoff continue; } @@ -63,7 +63,7 @@ async function fetchWithRetry(url: string, options: RequestInit, retries = 3, de elizaLogger.warn(`API request error (attempt ${i + 1}/${retries})`, { error: error instanceof Error ? error.message : String(error) }); - await sleep(delay * Math.pow(2, i)); + await sleep(delay * (2 ** i)); } } throw new AkashError( @@ -336,7 +336,7 @@ export const getDeploymentApiAction: Action = { } as ActionExample ]], - validate: async (runtime: IAgentRuntime, message: Memory): Promise => { + validate: async (_runtime: IAgentRuntime, message: Memory): Promise => { elizaLogger.debug("Validating get deployments request", { message }); try { const params = message.content as Partial; @@ -381,7 +381,7 @@ export const getDeploymentApiAction: Action = { handler: async ( runtime: IAgentRuntime, message: Memory, - state: State | undefined, + _state: State | undefined, _options: { [key: string]: unknown } = {}, callback?: HandlerCallback ): Promise => { diff --git a/packages/plugin-akash/src/actions/getDeploymentStatus.ts b/packages/plugin-akash/src/actions/getDeploymentStatus.ts index b927a8961d..2dfd2abae5 100644 --- a/packages/plugin-akash/src/actions/getDeploymentStatus.ts +++ b/packages/plugin-akash/src/actions/getDeploymentStatus.ts @@ -143,7 +143,7 @@ export const getDeploymentStatusAction: Action = { handler: async ( runtime: IAgentRuntime, message: Memory, - state: State | undefined, + _state: State | undefined, _options: { [key: string]: unknown } = {}, callback?: HandlerCallback ): Promise => { diff --git a/packages/plugin-akash/src/actions/getGPUPricing.ts b/packages/plugin-akash/src/actions/getGPUPricing.ts index a3a4073a2e..395fa5796e 100644 --- a/packages/plugin-akash/src/actions/getGPUPricing.ts +++ b/packages/plugin-akash/src/actions/getGPUPricing.ts @@ -58,23 +58,23 @@ export const getGPUPricingAction: Action = { } as ActionExample ]], - validate: async (runtime: IAgentRuntime, message: Memory): Promise => { + validate: async (_runtime: IAgentRuntime, message: Memory): Promise => { elizaLogger.debug("Validating GPU pricing request", { message }); try { const params = message.content as Partial; // Validate CPU if provided - if (params.cpu !== undefined && (isNaN(params.cpu) || params.cpu <= 0)) { + if (params.cpu !== undefined && (Number.isNaN(params.cpu) || params.cpu <= 0)) { throw new GPUPricingError("CPU units must be a positive number", "INVALID_CPU"); } // Validate memory if provided - if (params.memory !== undefined && (isNaN(params.memory) || params.memory <= 0)) { + if (params.memory !== undefined && (Number.isNaN(params.memory) || params.memory <= 0)) { throw new GPUPricingError("Memory must be a positive number", "INVALID_MEMORY"); } // Validate storage if provided - if (params.storage !== undefined && (isNaN(params.storage) || params.storage <= 0)) { + if (params.storage !== undefined && (Number.isNaN(params.storage) || params.storage <= 0)) { throw new GPUPricingError("Storage must be a positive number", "INVALID_STORAGE"); } @@ -91,9 +91,9 @@ export const getGPUPricingAction: Action = { }, handler: async ( - runtime: IAgentRuntime, + _runtime: IAgentRuntime, message: Memory, - state: State | undefined, + _state: State | undefined, _options: { [key: string]: unknown; } = {}, callback?: HandlerCallback ): Promise => { diff --git a/packages/plugin-akash/src/actions/getManifest.ts b/packages/plugin-akash/src/actions/getManifest.ts index 099ae0f412..5760617d26 100644 --- a/packages/plugin-akash/src/actions/getManifest.ts +++ b/packages/plugin-akash/src/actions/getManifest.ts @@ -3,8 +3,8 @@ import type { IAgentRuntime, Memory, State, HandlerCallback, Content, ActionExam import { SDL } from "@akashnetwork/akashjs/build/sdl"; import { validateAkashConfig } from "../environment"; import { AkashError, AkashErrorCode } from "../error/error"; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import yaml from 'js-yaml'; // import { getAkashTypeRegistry } from "@akashnetwork/akashjs/build/stargate"; import { getDefaultSDLPath } from "../utils/paths"; @@ -70,7 +70,7 @@ const loadSDLFromFile = (filePath: string): string => { // If we get here, none of the paths worked throw new AkashError( - `SDL file not found in any of the possible locations`, + 'SDL file not found in any of the possible locations', AkashErrorCode.VALIDATION_SDL_FAILED, { filePath, @@ -185,7 +185,7 @@ export const getManifestAction: Action = { handler: async ( runtime: IAgentRuntime, message: Memory, - state: State | undefined, + _state: State | undefined, _options: { [key: string]: unknown; } = {}, callback?: HandlerCallback ): Promise => { diff --git a/packages/plugin-akash/src/actions/getProvidersList.ts b/packages/plugin-akash/src/actions/getProvidersList.ts index 8a01783fd2..e8449b82e5 100644 --- a/packages/plugin-akash/src/actions/getProvidersList.ts +++ b/packages/plugin-akash/src/actions/getProvidersList.ts @@ -162,7 +162,7 @@ export const getProvidersListAction: Action = { } as ActionExample ]], - validate: async (runtime: IAgentRuntime, message: Memory): Promise => { + validate: async (_runtime: IAgentRuntime, message: Memory): Promise => { elizaLogger.debug("Validating get providers list request", { message }); try { const params = message.content as Partial; @@ -210,7 +210,7 @@ export const getProvidersListAction: Action = { handler: async ( runtime: IAgentRuntime, message: Memory, - state: State | undefined, + _state: State | undefined, _options: { [key: string]: unknown; } = {}, callback?: HandlerCallback ): Promise => { diff --git a/packages/plugin-akash/src/error/error.ts b/packages/plugin-akash/src/error/error.ts index 0ddbdc603e..9b6adeed19 100644 --- a/packages/plugin-akash/src/error/error.ts +++ b/packages/plugin-akash/src/error/error.ts @@ -1,4 +1,3 @@ - export enum AkashErrorCategory { WALLET = 'WALLET', DEPLOYMENT = 'DEPLOYMENT', @@ -117,7 +116,7 @@ export async function withRetry( } catch (error) { lastError = error as Error; if (i < maxRetries - 1) { - await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, i))); + await new Promise(resolve => setTimeout(resolve, delay * (2 ** i))); } } } diff --git a/packages/plugin-akash/src/index.ts b/packages/plugin-akash/src/index.ts index fdd7f59f84..5ff80a4aca 100644 --- a/packages/plugin-akash/src/index.ts +++ b/packages/plugin-akash/src/index.ts @@ -1,4 +1,4 @@ -import { Plugin} from "@elizaos/core"; +import type { Plugin} from "@elizaos/core"; import chalk from 'chalk'; import Table from 'cli-table3'; import ora from 'ora'; diff --git a/packages/plugin-akash/src/runtime_inspect.ts b/packages/plugin-akash/src/runtime_inspect.ts index 25b5aee39f..985b6d0d46 100644 --- a/packages/plugin-akash/src/runtime_inspect.ts +++ b/packages/plugin-akash/src/runtime_inspect.ts @@ -56,7 +56,7 @@ export function isPluginLoaded(runtime: IAgentRuntime, pluginName: string): bool // Check plugins array const plugins = (runtime as any).plugins as Plugin[]; if (!plugins) { - elizaLogger.warn(`No plugins array found in runtime`); + elizaLogger.warn('No plugins array found in runtime'); return false; } @@ -70,7 +70,7 @@ export function isPluginLoaded(runtime: IAgentRuntime, pluginName: string): bool // Check if actions are registered const actions = (runtime as any).actions as Action[]; if (!actions || !actions.length) { - elizaLogger.warn(`No actions found in runtime`); + elizaLogger.warn('No actions found in runtime'); return false; } diff --git a/packages/plugin-allora/biome.json b/packages/plugin-allora/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-allora/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-allora/package.json b/packages/plugin-allora/package.json index 6dee6303b4..754cf4e881 100644 --- a/packages/plugin-allora/package.json +++ b/packages/plugin-allora/package.json @@ -5,16 +5,24 @@ "type": "module", "types": "dist/index.d.ts", "dependencies": { - "@alloralabs/allora-sdk": "0.0.4", "@elizaos/core": "workspace:*", "node-cache": "5.1.2", "vitest": "2.1.8", "@alloralabs/allora-sdk": "^0.1.0" }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "tsup": "8.3.5", + "vitest": "2.1.8" + }, "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", - "test": "vitest run" + "test": "vitest run", + "lint": "biome lint .", + "lint:fix": "biome check --apply .", + "format": "biome format .", + "format:fix": "biome format --write ." }, "peerDependencies": { "whatwg-url": "7.1.0" diff --git a/packages/plugin-allora/src/actions/getInference.ts b/packages/plugin-allora/src/actions/getInference.ts index d875aa3599..9b1c7acb82 100644 --- a/packages/plugin-allora/src/actions/getInference.ts +++ b/packages/plugin-allora/src/actions/getInference.ts @@ -36,22 +36,23 @@ export const getInferenceAction: Action = { runtime: IAgentRuntime, message: Memory, state: State, - options: { [key: string]: unknown }, + _options: { [key: string]: unknown }, callback: HandlerCallback ): Promise => { // Initialize or update state - if (!state) { - state = (await runtime.composeState(message)) as State; + let currentState = state; + if (!currentState) { + currentState = (await runtime.composeState(message)) as State; } else { - state = await runtime.updateRecentMessageState(state); + currentState = await runtime.updateRecentMessageState(currentState); } // Get Allora topics information from the provider - state.alloraTopics = await topicsProvider.get(runtime, message, state); + currentState.alloraTopics = await topicsProvider.get(runtime, message, currentState); // Compose context for extracting the inference fields const inferenceTopicContext = composeContext({ - state, + state: currentState, template: getInferenceTemplate, }); diff --git a/packages/plugin-allora/src/providers/topics.ts b/packages/plugin-allora/src/providers/topics.ts index 6dd72da36a..2288c3f22e 100644 --- a/packages/plugin-allora/src/providers/topics.ts +++ b/packages/plugin-allora/src/providers/topics.ts @@ -23,14 +23,14 @@ export class TopicsProvider implements Provider { const alloraTopics = await this.getAlloraTopics(runtime); // Format the topics into a string to be added to the prompt context - let output = `Allora Network Topics: \n`; + let output = 'Allora Network Topics: \n'; for (const topic of alloraTopics) { output += `Topic Name: ${topic.topic_name}\n`; output += `Topic Description: ${topic.description}\n`; output += `Topic ID: ${topic.topic_id}\n`; output += `Topic is Active: ${topic.is_active}\n`; output += `Topic Updated At: ${topic.updated_at}\n`; - output += `\n`; + output += '\n'; } return output; diff --git a/packages/plugin-coingecko/__tests__/actions/getMarkets.test.ts b/packages/plugin-coingecko/__tests__/actions/getMarkets.test.ts new file mode 100644 index 0000000000..d6c937e2b2 --- /dev/null +++ b/packages/plugin-coingecko/__tests__/actions/getMarkets.test.ts @@ -0,0 +1,281 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { elizaLogger, ModelClass, generateObject, composeContext } from '@elizaos/core'; +import getMarketsAction, { formatCategory } from '../../src/actions/getMarkets'; +import axios from 'axios'; +import * as environment from '../../src/environment'; +import * as categoriesProvider from '../../src/providers/categoriesProvider'; + +vi.mock('axios'); +vi.mock('@elizaos/core', () => ({ + elizaLogger: { + log: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + success: vi.fn(), + }, + generateObject: vi.fn(), + composeContext: vi.fn(), + ModelClass: { LARGE: 'LARGE', SMALL: 'SMALL' } +})); +vi.mock('../../src/environment', () => ({ + validateCoingeckoConfig: vi.fn(), + getApiConfig: vi.fn() +})); +vi.mock('../../src/providers/categoriesProvider'); + +describe('getMarkets action', () => { + const mockRuntime = { + composeState: vi.fn(), + updateRecentMessageState: vi.fn(), + getPluginConfig: vi.fn(), + }; + + const mockMessage = {}; + const mockState = {}; + const mockCallback = vi.fn(); + const mockConfig = { + COINGECKO_API_KEY: 'test-api-key', + COINGECKO_PRO_API_KEY: null + }; + + const mockCategories = [ + { category_id: 'defi', name: 'DeFi' }, + { category_id: 'nft', name: 'NFT' } + ]; + + beforeEach(() => { + vi.clearAllMocks(); + + // Mock environment validation + vi.mocked(environment.validateCoingeckoConfig).mockResolvedValue(mockConfig); + vi.mocked(environment.getApiConfig).mockReturnValue({ + baseUrl: 'https://api.coingecko.com/api/v3', + apiKey: 'test-api-key', + headerKey: 'x-cg-demo-api-key' + }); + + // Mock categories provider + vi.mocked(categoriesProvider.getCategoriesData).mockResolvedValue(mockCategories); + + // Mock runtime functions + mockRuntime.composeState.mockResolvedValue(mockState); + mockRuntime.updateRecentMessageState.mockResolvedValue(mockState); + mockRuntime.getPluginConfig.mockResolvedValue({ + apiKey: 'test-api-key', + baseUrl: 'https://api.coingecko.com/api/v3' + }); + + // Mock the core functions + vi.mocked(elizaLogger.log).mockImplementation(() => {}); + vi.mocked(elizaLogger.error).mockImplementation(() => {}); + vi.mocked(elizaLogger.success).mockImplementation(() => {}); + vi.mocked(composeContext).mockReturnValue({}); + }); + + describe('formatCategory', () => { + it('should return undefined for undefined input', () => { + expect(formatCategory(undefined, mockCategories)).toBeUndefined(); + }); + + it('should find exact match by category_id', () => { + expect(formatCategory('defi', mockCategories)).toBe('defi'); + }); + + it('should find match by name', () => { + expect(formatCategory('DeFi', mockCategories)).toBe('defi'); + }); + + it('should find partial match', () => { + expect(formatCategory('nf', mockCategories)).toBe('nft'); + }); + + it('should return undefined for no match', () => { + expect(formatCategory('invalid-category', mockCategories)).toBeUndefined(); + }); + }); + + it('should validate coingecko config', async () => { + await getMarketsAction.validate(mockRuntime, mockMessage); + expect(environment.validateCoingeckoConfig).toHaveBeenCalledWith(mockRuntime); + }); + + it('should fetch and format market data', async () => { + const mockResponse = { + data: [ + { + id: 'bitcoin', + symbol: 'btc', + name: 'Bitcoin', + image: 'image_url', + current_price: 50000, + market_cap: 1000000000000, + market_cap_rank: 1, + fully_diluted_valuation: 1100000000000, + total_volume: 30000000000, + high_24h: 51000, + low_24h: 49000, + price_change_24h: 1000, + price_change_percentage_24h: 2, + market_cap_change_24h: 20000000000, + market_cap_change_percentage_24h: 2, + circulating_supply: 19000000, + total_supply: 21000000, + max_supply: 21000000, + ath: 69000, + ath_change_percentage: -27.5, + ath_date: '2021-11-10T14:24:11.849Z', + atl: 67.81, + atl_change_percentage: 73623.12, + atl_date: '2013-07-06T00:00:00.000Z', + last_updated: '2024-01-31T23:00:00.000Z' + } + ] + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + category: 'defi', + order: 'market_cap_desc', + per_page: 20, + page: 1, + sparkline: false + }, + modelClass: ModelClass.SMALL + }); + + await getMarketsAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(axios.get).toHaveBeenCalledWith( + 'https://api.coingecko.com/api/v3/coins/markets', + expect.objectContaining({ + params: { + vs_currency: 'usd', + category: 'defi', + order: 'market_cap_desc', + per_page: 20, + page: 1, + sparkline: false + } + }) + ); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Bitcoin (BTC)'), + content: expect.objectContaining({ + markets: expect.arrayContaining([ + expect.objectContaining({ + name: 'Bitcoin', + symbol: 'BTC', + marketCapRank: 1, + currentPrice: 50000 + }) + ]) + }) + })); + }); + + it('should handle invalid category', async () => { + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + category: 'invalid-category', + order: 'market_cap_desc', + per_page: 20, + page: 1, + sparkline: false + }, + modelClass: ModelClass.SMALL + }); + + await getMarketsAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Invalid category'), + error: expect.objectContaining({ + message: expect.stringContaining('Invalid category') + }) + })); + }); + + it('should handle API errors gracefully', async () => { + vi.mocked(axios.get).mockRejectedValueOnce(new Error('API Error')); + + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + order: 'market_cap_desc', + per_page: 20, + page: 1, + sparkline: false + }, + modelClass: ModelClass.SMALL + }); + + await getMarketsAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Error fetching market data'), + error: expect.objectContaining({ + message: expect.stringContaining('API Error') + }) + })); + }); + + it('should handle rate limit errors', async () => { + const rateLimitError = new Error('Rate limit exceeded'); + Object.assign(rateLimitError, { + response: { status: 429 } + }); + vi.mocked(axios.get).mockRejectedValueOnce(rateLimitError); + + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + order: 'market_cap_desc', + per_page: 20, + page: 1, + sparkline: false + }, + modelClass: ModelClass.SMALL + }); + + await getMarketsAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Rate limit exceeded'), + error: expect.objectContaining({ + message: expect.stringContaining('Rate limit exceeded'), + statusCode: 429 + }) + })); + }); + + it('should handle empty response data', async () => { + vi.mocked(axios.get).mockResolvedValueOnce({ data: [] }); + + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + order: 'market_cap_desc', + per_page: 20, + page: 1, + sparkline: false + }, + modelClass: ModelClass.SMALL + }); + + await getMarketsAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('No market data received'), + error: expect.objectContaining({ + message: expect.stringContaining('No market data received') + }) + })); + }); +}); diff --git a/packages/plugin-coingecko/__tests__/actions/getPrice.test.ts b/packages/plugin-coingecko/__tests__/actions/getPrice.test.ts new file mode 100644 index 0000000000..3c371be397 --- /dev/null +++ b/packages/plugin-coingecko/__tests__/actions/getPrice.test.ts @@ -0,0 +1,208 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { elizaLogger, ModelClass, generateObject, composeContext } from '@elizaos/core'; +import getPriceAction from '../../src/actions/getPrice'; +import axios from 'axios'; +import * as environment from '../../src/environment'; +import * as coinsProvider from '../../src/providers/coinsProvider'; + +vi.mock('axios'); +vi.mock('@elizaos/core', () => ({ + elizaLogger: { + log: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + success: vi.fn(), + }, + generateObject: vi.fn(), + composeContext: vi.fn(), + ModelClass: { LARGE: 'LARGE' } +})); +vi.mock('../../src/environment', () => ({ + validateCoingeckoConfig: vi.fn(), + getApiConfig: vi.fn() +})); +vi.mock('../../src/providers/coinsProvider'); + +describe('getPrice action', () => { + const mockRuntime = { + composeState: vi.fn(), + updateRecentMessageState: vi.fn(), + getPluginConfig: vi.fn(), + }; + + const mockMessage = {}; + const mockState = {}; + const mockCallback = vi.fn(); + const mockConfig = { + COINGECKO_API_KEY: 'test-api-key', + COINGECKO_PRO_API_KEY: null + }; + + beforeEach(() => { + vi.clearAllMocks(); + + // Mock environment validation + vi.mocked(environment.validateCoingeckoConfig).mockResolvedValue(mockConfig); + vi.mocked(environment.getApiConfig).mockReturnValue({ + baseUrl: 'https://api.coingecko.com/api/v3', + apiKey: 'test-api-key', + headerKey: 'x-cg-demo-api-key' + }); + + // Mock runtime functions + mockRuntime.composeState.mockResolvedValue(mockState); + mockRuntime.updateRecentMessageState.mockResolvedValue(mockState); + mockRuntime.getPluginConfig.mockResolvedValue({ + apiKey: 'test-api-key', + baseUrl: 'https://api.coingecko.com/api/v3' + }); + + // Mock the core functions + vi.mocked(elizaLogger.log).mockImplementation(() => {}); + vi.mocked(elizaLogger.error).mockImplementation(() => {}); + vi.mocked(elizaLogger.success).mockImplementation(() => {}); + vi.mocked(composeContext).mockReturnValue({}); + }); + + it('should validate coingecko config', async () => { + await getPriceAction.validate(mockRuntime, mockMessage); + expect(environment.validateCoingeckoConfig).toHaveBeenCalledWith(mockRuntime); + }); + + it('should fetch and format price data for a single coin', async () => { + const mockPriceResponse = { + data: { + bitcoin: { + usd: 50000, + eur: 42000 + } + } + }; + + const mockCoinsData = [{ + id: 'bitcoin', + name: 'Bitcoin', + symbol: 'btc' + }]; + + vi.mocked(axios.get).mockResolvedValueOnce(mockPriceResponse); + vi.mocked(coinsProvider.getCoinsData).mockResolvedValueOnce(mockCoinsData); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + coinIds: 'bitcoin', + currency: ['usd', 'eur'], + include_market_cap: false, + include_24hr_vol: false, + include_24hr_change: false, + include_last_updated_at: false + }, + modelClass: ModelClass.LARGE + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(axios.get).toHaveBeenCalledWith( + 'https://api.coingecko.com/api/v3/simple/price', + expect.any(Object) + ); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Bitcoin (BTC)') + })); + }); + + it('should handle API errors gracefully', async () => { + vi.mocked(axios.get).mockRejectedValueOnce(new Error('API Error')); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + coinIds: 'invalid-coin', + currency: ['usd'], + include_market_cap: false, + include_24hr_vol: false, + include_24hr_change: false, + include_last_updated_at: false + }, + modelClass: ModelClass.LARGE + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + content: expect.objectContaining({ + error: expect.stringContaining('API Error') + }) + })); + }); + + it('should handle empty response data', async () => { + vi.mocked(axios.get).mockResolvedValueOnce({ data: {} }); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + coinIds: 'non-existent-coin', + currency: ['usd'], + include_market_cap: false, + include_24hr_vol: false, + include_24hr_change: false, + include_last_updated_at: false + }, + modelClass: ModelClass.LARGE + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + content: expect.objectContaining({ + error: expect.stringContaining('No price data available') + }) + })); + }); + + it('should include additional market data when requested', async () => { + const mockPriceResponse = { + data: { + ethereum: { + usd: 3000, + usd_market_cap: 350000000000, + usd_24h_vol: 20000000000, + usd_24h_change: 5.5, + last_updated_at: 1643673600 + } + } + }; + + const mockCoinsData = [{ + id: 'ethereum', + name: 'Ethereum', + symbol: 'eth' + }]; + + vi.mocked(axios.get).mockResolvedValueOnce(mockPriceResponse); + vi.mocked(coinsProvider.getCoinsData).mockResolvedValueOnce(mockCoinsData); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + coinIds: 'ethereum', + currency: ['usd'], + include_market_cap: true, + include_24hr_vol: true, + include_24hr_change: true, + include_last_updated_at: true + }, + modelClass: ModelClass.LARGE + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Market Cap') + })); + }); +}); diff --git a/packages/plugin-coingecko/__tests__/actions/getTopGainersLosers.test.ts b/packages/plugin-coingecko/__tests__/actions/getTopGainersLosers.test.ts new file mode 100644 index 0000000000..3854e59335 --- /dev/null +++ b/packages/plugin-coingecko/__tests__/actions/getTopGainersLosers.test.ts @@ -0,0 +1,251 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { elizaLogger, ModelClass, generateObject, composeContext } from '@elizaos/core'; +import getTopGainersLosersAction from '../../src/actions/getTopGainersLosers'; +import axios from 'axios'; +import * as environment from '../../src/environment'; + +vi.mock('axios'); +vi.mock('@elizaos/core', () => ({ + elizaLogger: { + log: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + success: vi.fn(), + }, + generateObject: vi.fn(), + composeContext: vi.fn(), + ModelClass: { LARGE: 'LARGE' } +})); +vi.mock('../../src/environment', () => ({ + validateCoingeckoConfig: vi.fn(), + getApiConfig: vi.fn() +})); + +describe('getTopGainersLosers action', () => { + const mockRuntime = { + composeState: vi.fn(), + updateRecentMessageState: vi.fn(), + getPluginConfig: vi.fn(), + }; + + const mockMessage = {}; + const mockState = {}; + const mockCallback = vi.fn(); + const mockConfig = { + COINGECKO_API_KEY: 'test-api-key', + COINGECKO_PRO_API_KEY: null + }; + + beforeEach(() => { + vi.clearAllMocks(); + + // Mock environment validation + vi.mocked(environment.validateCoingeckoConfig).mockResolvedValue(mockConfig); + vi.mocked(environment.getApiConfig).mockReturnValue({ + baseUrl: 'https://api.coingecko.com/api/v3', + apiKey: 'test-api-key', + headerKey: 'x-cg-demo-api-key' + }); + + // Mock runtime functions + mockRuntime.composeState.mockResolvedValue(mockState); + mockRuntime.updateRecentMessageState.mockResolvedValue(mockState); + mockRuntime.getPluginConfig.mockResolvedValue({ + apiKey: 'test-api-key', + baseUrl: 'https://api.coingecko.com/api/v3' + }); + + // Mock the core functions + vi.mocked(elizaLogger.log).mockImplementation(() => {}); + vi.mocked(elizaLogger.error).mockImplementation(() => {}); + vi.mocked(elizaLogger.success).mockImplementation(() => {}); + vi.mocked(composeContext).mockReturnValue({}); + }); + + it('should validate coingecko config', async () => { + await getTopGainersLosersAction.validate(mockRuntime, mockMessage); + expect(environment.validateCoingeckoConfig).toHaveBeenCalledWith(mockRuntime); + }); + + it('should fetch and format top gainers and losers data', async () => { + const mockResponse = { + data: { + top_gainers: [ + { + id: 'bitcoin', + symbol: 'btc', + name: 'Bitcoin', + image: 'image_url', + market_cap_rank: 1, + usd: 50000, + usd_24h_vol: 30000000000, + usd_24h_change: 5.5 + } + ], + top_losers: [ + { + id: 'ethereum', + symbol: 'eth', + name: 'Ethereum', + image: 'image_url', + market_cap_rank: 2, + usd: 2500, + usd_24h_vol: 20000000000, + usd_24h_change: -3.2 + } + ] + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + duration: '24h', + top_coins: '1000' + }, + modelClass: ModelClass.LARGE + }); + + await getTopGainersLosersAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(axios.get).toHaveBeenCalledWith( + 'https://api.coingecko.com/api/v3/coins/top_gainers_losers', + expect.objectContaining({ + params: { + vs_currency: 'usd', + duration: '24h', + top_coins: '1000' + } + }) + ); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Bitcoin (BTC)'), + content: expect.objectContaining({ + data: expect.objectContaining({ + top_gainers: expect.arrayContaining([ + expect.objectContaining({ + name: 'Bitcoin', + symbol: 'btc', + usd_24h_change: 5.5 + }) + ]), + top_losers: expect.arrayContaining([ + expect.objectContaining({ + name: 'Ethereum', + symbol: 'eth', + usd_24h_change: -3.2 + }) + ]) + }) + }) + })); + }); + + it('should handle API errors gracefully', async () => { + vi.mocked(axios.get).mockRejectedValueOnce(new Error('API Error')); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + duration: '24h', + top_coins: '1000' + }, + modelClass: ModelClass.LARGE + }); + + await getTopGainersLosersAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Error fetching top gainers/losers data'), + content: expect.objectContaining({ + error: expect.stringContaining('API Error') + }) + })); + }); + + it('should handle rate limit errors', async () => { + const rateLimitError = new Error('Rate limit exceeded'); + Object.assign(rateLimitError, { + response: { status: 429 } + }); + vi.mocked(axios.get).mockRejectedValueOnce(rateLimitError); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + duration: '24h', + top_coins: '1000' + }, + modelClass: ModelClass.LARGE + }); + + await getTopGainersLosersAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Rate limit exceeded'), + content: expect.objectContaining({ + error: expect.stringContaining('Rate limit exceeded'), + statusCode: 429 + }) + })); + }); + + it('should handle pro plan requirement errors', async () => { + const proPlanError = new Error('Pro plan required'); + Object.assign(proPlanError, { + response: { status: 403 } + }); + vi.mocked(axios.get).mockRejectedValueOnce(proPlanError); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + duration: '24h', + top_coins: '1000' + }, + modelClass: ModelClass.LARGE + }); + + await getTopGainersLosersAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('requires a CoinGecko Pro API key'), + content: expect.objectContaining({ + error: expect.stringContaining('Pro plan required'), + statusCode: 403, + requiresProPlan: true + }) + })); + }); + + it('should handle empty response data', async () => { + vi.mocked(axios.get).mockResolvedValueOnce({ data: null }); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + vs_currency: 'usd', + duration: '24h', + top_coins: '1000' + }, + modelClass: ModelClass.LARGE + }); + + await getTopGainersLosersAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('No data received'), + content: expect.objectContaining({ + error: expect.stringContaining('No data received') + }) + })); + }); +}); diff --git a/packages/plugin-coingecko/__tests__/actions/getTrending.test.ts b/packages/plugin-coingecko/__tests__/actions/getTrending.test.ts new file mode 100644 index 0000000000..32b51f36aa --- /dev/null +++ b/packages/plugin-coingecko/__tests__/actions/getTrending.test.ts @@ -0,0 +1,220 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { elizaLogger, ModelClass, generateObject, composeContext } from '@elizaos/core'; +import getTrendingAction from '../../src/actions/getTrending'; +import axios from 'axios'; +import * as environment from '../../src/environment'; + +vi.mock('axios'); +vi.mock('@elizaos/core', () => ({ + elizaLogger: { + log: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + success: vi.fn(), + }, + generateObject: vi.fn(), + composeContext: vi.fn(), + ModelClass: { LARGE: 'LARGE' } +})); +vi.mock('../../src/environment', () => ({ + validateCoingeckoConfig: vi.fn(), + getApiConfig: vi.fn() +})); + +describe('getTrending action', () => { + const mockRuntime = { + composeState: vi.fn(), + updateRecentMessageState: vi.fn(), + getPluginConfig: vi.fn(), + }; + + const mockMessage = {}; + const mockState = {}; + const mockCallback = vi.fn(); + const mockConfig = { + COINGECKO_API_KEY: 'test-api-key', + COINGECKO_PRO_API_KEY: null + }; + + beforeEach(() => { + vi.clearAllMocks(); + + // Mock environment validation + vi.mocked(environment.validateCoingeckoConfig).mockResolvedValue(mockConfig); + vi.mocked(environment.getApiConfig).mockReturnValue({ + baseUrl: 'https://api.coingecko.com/api/v3', + apiKey: 'test-api-key', + headerKey: 'x-cg-demo-api-key' + }); + + // Mock runtime functions + mockRuntime.composeState.mockResolvedValue(mockState); + mockRuntime.updateRecentMessageState.mockResolvedValue(mockState); + mockRuntime.getPluginConfig.mockResolvedValue({ + apiKey: 'test-api-key', + baseUrl: 'https://api.coingecko.com/api/v3' + }); + + // Mock the core functions + vi.mocked(elizaLogger.log).mockImplementation(() => {}); + vi.mocked(elizaLogger.error).mockImplementation(() => {}); + vi.mocked(elizaLogger.success).mockImplementation(() => {}); + vi.mocked(composeContext).mockReturnValue({}); + }); + + it('should validate coingecko config', async () => { + await getTrendingAction.validate(mockRuntime, mockMessage); + expect(environment.validateCoingeckoConfig).toHaveBeenCalledWith(mockRuntime); + }); + + it('should fetch and format trending data', async () => { + const mockTrendingResponse = { + data: { + coins: [ + { + item: { + id: 'bitcoin', + name: 'Bitcoin', + symbol: 'btc', + market_cap_rank: 1, + thumb: 'thumb_url', + large: 'large_url' + } + } + ], + nfts: [ + { + id: 'bored-ape', + name: 'Bored Ape Yacht Club', + symbol: 'BAYC', + thumb: 'thumb_url' + } + ], + categories: [ + { + id: 'defi', + name: 'DeFi' + } + ], + exchanges: [], + icos: [] + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockTrendingResponse); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + include_nfts: true, + include_categories: true + }, + modelClass: ModelClass.LARGE + }); + + await getTrendingAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(axios.get).toHaveBeenCalledWith( + 'https://api.coingecko.com/api/v3/search/trending', + expect.any(Object) + ); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Bitcoin (BTC)'), + content: expect.objectContaining({ + trending: expect.objectContaining({ + coins: expect.arrayContaining([ + expect.objectContaining({ + name: 'Bitcoin', + symbol: 'BTC', + marketCapRank: 1 + }) + ]), + nfts: expect.arrayContaining([ + expect.objectContaining({ + name: 'Bored Ape Yacht Club', + symbol: 'BAYC' + }) + ]), + categories: expect.arrayContaining([ + expect.objectContaining({ + name: 'DeFi' + }) + ]) + }) + }) + })); + }); + + it('should handle API errors gracefully', async () => { + vi.mocked(axios.get).mockRejectedValueOnce(new Error('API Error')); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + include_nfts: true, + include_categories: true + }, + modelClass: ModelClass.LARGE + }); + + await getTrendingAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Error fetching trending data'), + content: expect.objectContaining({ + error: expect.stringContaining('API Error') + }) + })); + }); + + it('should handle rate limit errors', async () => { + const rateLimitError = new Error('Rate limit exceeded'); + Object.assign(rateLimitError, { + response: { status: 429 } + }); + vi.mocked(axios.get).mockRejectedValueOnce(rateLimitError); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + include_nfts: true, + include_categories: true + }, + modelClass: ModelClass.LARGE + }); + + await getTrendingAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Rate limit exceeded'), + content: expect.objectContaining({ + error: expect.stringContaining('Rate limit exceeded'), + statusCode: 429 + }) + })); + }); + + it('should handle empty response data', async () => { + vi.mocked(axios.get).mockResolvedValueOnce({ data: null }); + + // Mock the content generation + vi.mocked(generateObject).mockResolvedValueOnce({ + object: { + include_nfts: true, + include_categories: true + }, + modelClass: ModelClass.LARGE + }); + + await getTrendingAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Error fetching trending data'), + content: expect.objectContaining({ + error: expect.stringContaining('No data received') + }) + })); + }); +}); diff --git a/packages/plugin-coingecko/__tests__/setup.ts b/packages/plugin-coingecko/__tests__/setup.ts new file mode 100644 index 0000000000..ba8257dbe3 --- /dev/null +++ b/packages/plugin-coingecko/__tests__/setup.ts @@ -0,0 +1,20 @@ +import { vi } from 'vitest'; +import { elizaLogger } from '@elizaos/core'; + +// Mock elizaLogger +vi.mock('@elizaos/core', () => ({ + elizaLogger: { + log: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + generateObject: vi.fn(), + } +})); + +// Mock fetch +global.fetch = vi.fn(); + +beforeEach(() => { + vi.clearAllMocks(); +}); diff --git a/packages/plugin-coingecko/package.json b/packages/plugin-coingecko/package.json index 1283821cb2..b5086ec15a 100644 --- a/packages/plugin-coingecko/package.json +++ b/packages/plugin-coingecko/package.json @@ -10,12 +10,16 @@ "tsup": "^8.3.5" }, "devDependencies": { - "@biomejs/biome": "1.9.4" + "@biomejs/biome": "1.9.4", + "@vitest/coverage-v8": "^1.2.2", + "vitest": "^1.2.2" }, "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", "test": "vitest run", + "test:watch": "vitest watch", + "test:coverage": "vitest run --coverage", "clean": "rm -rf dist", "lint": "biome lint .", "lint:fix": "biome check --apply .", diff --git a/packages/plugin-coingecko/vitest.config.ts b/packages/plugin-coingecko/vitest.config.ts new file mode 100644 index 0000000000..419efc958f --- /dev/null +++ b/packages/plugin-coingecko/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + setupFiles: ['./__tests__/setup.ts'], + include: ['**/__tests__/**/*.test.ts'], + } +}); diff --git a/packages/plugin-coinmarketcap/__tests__/actions/getPrice.service.test.ts b/packages/plugin-coinmarketcap/__tests__/actions/getPrice.service.test.ts new file mode 100644 index 0000000000..e695eea392 --- /dev/null +++ b/packages/plugin-coinmarketcap/__tests__/actions/getPrice.service.test.ts @@ -0,0 +1,151 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import axios from 'axios'; +import { createPriceService } from '../../src/actions/getPrice/service'; + +vi.mock('axios'); + +describe('PriceService', () => { + const API_KEY = 'test-api-key'; + let priceService: ReturnType; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(axios.create).mockReturnValue(axios); + priceService = createPriceService(API_KEY); + }); + + it('should create axios instance with correct config', () => { + expect(axios.create).toHaveBeenCalledWith({ + baseURL: 'https://pro-api.coinmarketcap.com/v1', + headers: { + 'X-CMC_PRO_API_KEY': API_KEY, + 'Accept': 'application/json' + } + }); + }); + + it('should normalize symbol and currency', async () => { + const mockResponse = { + data: { + data: { + BTC: { + quote: { + USD: { + price: 50000, + market_cap: 1000000000000, + volume_24h: 30000000000, + percent_change_24h: 2.5 + } + } + } + } + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + await priceService.getPrice(' btc ', ' usd '); + + expect(axios.get).toHaveBeenCalledWith( + '/cryptocurrency/quotes/latest', + expect.objectContaining({ + params: { + symbol: 'BTC', + convert: 'USD' + } + }) + ); + }); + + it('should return formatted price data', async () => { + const mockResponse = { + data: { + data: { + BTC: { + quote: { + USD: { + price: 50000, + market_cap: 1000000000000, + volume_24h: 30000000000, + percent_change_24h: 2.5 + } + } + } + } + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + const result = await priceService.getPrice('BTC', 'USD'); + + expect(result).toEqual({ + price: 50000, + marketCap: 1000000000000, + volume24h: 30000000000, + percentChange24h: 2.5 + }); + }); + + it('should handle missing symbol data', async () => { + const mockResponse = { + data: { + data: {} + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + await expect(priceService.getPrice('INVALID', 'USD')) + .rejects + .toThrow('No data found for symbol: INVALID'); + }); + + it('should handle missing quote data', async () => { + const mockResponse = { + data: { + data: { + BTC: { + quote: {} + } + } + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + await expect(priceService.getPrice('BTC', 'INVALID')) + .rejects + .toThrow('No quote data found for currency: INVALID'); + }); + + it('should handle API errors', async () => { + const errorMessage = 'API rate limit exceeded'; + const apiError = new Error(errorMessage); + Object.assign(apiError, { + isAxiosError: true, + response: { + data: { + status: { + error_message: errorMessage + } + } + } + }); + + vi.mocked(axios.get).mockRejectedValueOnce(apiError); + + await expect(priceService.getPrice('BTC', 'USD')) + .rejects + .toThrow(`${errorMessage}`); + }); + + it('should handle non-axios errors', async () => { + const error = new Error('Network error'); + vi.mocked(axios.get).mockRejectedValueOnce(error); + + await expect(priceService.getPrice('BTC', 'USD')) + .rejects + .toThrow('Network error'); + }); +}); diff --git a/packages/plugin-coinmarketcap/__tests__/actions/getPrice.test.ts b/packages/plugin-coinmarketcap/__tests__/actions/getPrice.test.ts new file mode 100644 index 0000000000..f737c282c4 --- /dev/null +++ b/packages/plugin-coinmarketcap/__tests__/actions/getPrice.test.ts @@ -0,0 +1,234 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { elizaLogger, ModelClass, generateObjectDeprecated, composeContext } from '@elizaos/core'; +import getPriceAction from '../../src/actions/getPrice'; +import axios from 'axios'; +import * as environment from '../../src/environment'; + +vi.mock('axios'); +vi.mock('@elizaos/core', () => ({ + elizaLogger: { + log: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + success: vi.fn(), + }, + generateObjectDeprecated: vi.fn(), + composeContext: vi.fn(), + ModelClass: { SMALL: 'SMALL' } +})); +vi.mock('../../src/environment', () => ({ + validateCoinMarketCapConfig: vi.fn() +})); + +describe('getPrice action', () => { + const mockRuntime = { + composeState: vi.fn(), + updateRecentMessageState: vi.fn(), + getPluginConfig: vi.fn(), + }; + + const mockMessage = {}; + const mockState = {}; + const mockCallback = vi.fn(); + const mockConfig = { + COINMARKETCAP_API_KEY: 'test-api-key' + }; + + beforeEach(() => { + vi.clearAllMocks(); + + // Mock environment validation + vi.mocked(environment.validateCoinMarketCapConfig).mockResolvedValue(mockConfig); + + // Mock runtime functions + mockRuntime.composeState.mockResolvedValue(mockState); + mockRuntime.updateRecentMessageState.mockResolvedValue(mockState); + mockRuntime.getPluginConfig.mockResolvedValue({ + apiKey: 'test-api-key' + }); + + // Mock axios create + vi.mocked(axios.create).mockReturnValue(axios); + + // Mock the core functions + vi.mocked(elizaLogger.log).mockImplementation(() => {}); + vi.mocked(elizaLogger.error).mockImplementation(() => {}); + vi.mocked(elizaLogger.success).mockImplementation(() => {}); + vi.mocked(composeContext).mockReturnValue({}); + }); + + it('should validate coinmarketcap config', async () => { + await getPriceAction.validate(mockRuntime, mockMessage); + expect(environment.validateCoinMarketCapConfig).toHaveBeenCalledWith(mockRuntime); + }); + + it('should fetch and format price data', async () => { + const mockResponse = { + data: { + data: { + BTC: { + quote: { + USD: { + price: 50000, + market_cap: 1000000000000, + volume_24h: 30000000000, + percent_change_24h: 2.5 + } + } + } + } + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + // Mock the content generation + vi.mocked(generateObjectDeprecated).mockResolvedValueOnce({ + symbol: 'BTC', + currency: 'USD' + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(axios.get).toHaveBeenCalledWith( + '/cryptocurrency/quotes/latest', + expect.objectContaining({ + params: { + symbol: 'BTC', + convert: 'USD' + } + }) + ); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('50000 USD'), + content: expect.objectContaining({ + symbol: 'BTC', + currency: 'USD', + price: 50000, + marketCap: 1000000000000, + volume24h: 30000000000, + percentChange24h: 2.5 + }) + })); + }); + + it('should handle invalid symbol', async () => { + const mockResponse = { + data: { + data: {} + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + // Mock the content generation + vi.mocked(generateObjectDeprecated).mockResolvedValueOnce({ + symbol: 'INVALID', + currency: 'USD' + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('No data found for symbol'), + content: expect.objectContaining({ + error: expect.stringContaining('No data found for symbol') + }) + })); + }); + + it('should handle invalid currency', async () => { + const mockResponse = { + data: { + data: { + BTC: { + quote: {} + } + } + } + }; + + vi.mocked(axios.get).mockResolvedValueOnce(mockResponse); + + // Mock the content generation + vi.mocked(generateObjectDeprecated).mockResolvedValueOnce({ + symbol: 'BTC', + currency: 'INVALID' + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('No quote data found for currency'), + content: expect.objectContaining({ + error: expect.stringContaining('No quote data found for currency') + }) + })); + }); + + it('should handle API errors gracefully', async () => { + vi.mocked(axios.get).mockRejectedValueOnce(new Error('API Error')); + + // Mock the content generation + vi.mocked(generateObjectDeprecated).mockResolvedValueOnce({ + symbol: 'BTC', + currency: 'USD' + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('API Error'), + content: expect.objectContaining({ + error: expect.stringContaining('API Error') + }) + })); + }); + + it('should handle rate limit errors', async () => { + const errorMessage = 'Rate limit exceeded'; + const rateLimitError = new Error(`API Error: ${errorMessage}`); + Object.assign(rateLimitError, { + isAxiosError: true, + response: { + data: { + status: { + error_message: errorMessage + } + } + } + }); + vi.mocked(axios.get).mockRejectedValueOnce(rateLimitError); + + // Mock the content generation + vi.mocked(generateObjectDeprecated).mockResolvedValueOnce({ + symbol: 'BTC', + currency: 'USD' + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith({ + text: `Error fetching price: API Error: ${errorMessage}`, + content: { error: `API Error: ${errorMessage}` } + }); + }); + + it('should handle invalid content generation', async () => { + // Mock invalid content generation + vi.mocked(generateObjectDeprecated).mockResolvedValueOnce({ + invalidField: 'invalid' + }); + + await getPriceAction.handler(mockRuntime, mockMessage, mockState, {}, mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(expect.objectContaining({ + text: expect.stringContaining('Invalid price check content'), + content: expect.objectContaining({ + error: expect.stringContaining('Invalid price check content') + }) + })); + }); +}); diff --git a/packages/plugin-coinmarketcap/__tests__/setup.ts b/packages/plugin-coinmarketcap/__tests__/setup.ts new file mode 100644 index 0000000000..bbc49909c1 --- /dev/null +++ b/packages/plugin-coinmarketcap/__tests__/setup.ts @@ -0,0 +1,10 @@ +import { vi } from 'vitest'; + +// Mock console methods +global.console = { + ...console, + log: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + info: vi.fn() +}; diff --git a/packages/plugin-coinmarketcap/package.json b/packages/plugin-coinmarketcap/package.json index 47f4666006..cf504b6840 100644 --- a/packages/plugin-coinmarketcap/package.json +++ b/packages/plugin-coinmarketcap/package.json @@ -20,6 +20,9 @@ "lint": "biome lint .", "lint:fix": "biome check --apply .", "format": "biome format .", - "format:fix": "biome format --write ." + "format:fix": "biome format --write .", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" } } diff --git a/packages/plugin-coinmarketcap/vitest.config.ts b/packages/plugin-coinmarketcap/vitest.config.ts new file mode 100644 index 0000000000..5c5066f7c5 --- /dev/null +++ b/packages/plugin-coinmarketcap/vitest.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + setupFiles: ['__tests__/setup.ts'], + include: ['__tests__/**/*.test.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + include: ['src/**/*.ts'], + exclude: ['**/*.d.ts', '**/*.test.ts', '**/examples.ts', '**/template.ts'] + } + } +}); diff --git a/packages/plugin-twitter/biome.json b/packages/plugin-twitter/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-twitter/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-twitter/eslint.config.mjs b/packages/plugin-twitter/eslint.config.mjs deleted file mode 100644 index 92fe5bbebe..0000000000 --- a/packages/plugin-twitter/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import eslintGlobalConfig from "../../eslint.config.mjs"; - -export default [...eslintGlobalConfig]; diff --git a/packages/plugin-twitter/package.json b/packages/plugin-twitter/package.json index 09cbeb222a..a6b181a3d9 100644 --- a/packages/plugin-twitter/package.json +++ b/packages/plugin-twitter/package.json @@ -24,6 +24,7 @@ "tsup": "8.3.5" }, "devDependencies": { + "@biomejs/biome": "1.5.3", "vitest": "^1.0.0" }, "scripts": { @@ -31,6 +32,9 @@ "dev": "tsup --format esm --dts --watch", "test": "vitest run", "test:watch": "vitest", - "lint": "eslint --fix --cache ." + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" } } diff --git a/packages/plugin-udio/biome.json b/packages/plugin-udio/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-udio/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-udio/eslint.config.mjs b/packages/plugin-udio/eslint.config.mjs deleted file mode 100644 index 92fe5bbebe..0000000000 --- a/packages/plugin-udio/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import eslintGlobalConfig from "../../eslint.config.mjs"; - -export default [...eslintGlobalConfig]; diff --git a/packages/plugin-udio/package.json b/packages/plugin-udio/package.json index 12ba10cb4f..994fda5b36 100644 --- a/packages/plugin-udio/package.json +++ b/packages/plugin-udio/package.json @@ -3,19 +3,26 @@ "version": "0.1.9", "description": "Suno AI Music Generation Plugin for Eliza", "main": "dist/index.js", + "type": "module", "types": "dist/index.d.ts", "scripts": { - "build": "tsup", + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/", "test": "jest" }, "dependencies": { - "@elizaos/core": "^0.1.0" + "@elizaos/core": "workspace:*" }, "devDependencies": { + "@biomejs/biome": "1.5.3", "typescript": "^5.0.0", "@types/node": "^16.0.0", "jest": "^27.0.0", "@types/jest": "^27.0.0", - "tsup": "^7.2.0" + "tsup": "^8.3.5" } } diff --git a/packages/plugin-video-generation/biome.json b/packages/plugin-video-generation/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-video-generation/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-video-generation/package.json b/packages/plugin-video-generation/package.json index 8656816e84..78626b94f1 100644 --- a/packages/plugin-video-generation/package.json +++ b/packages/plugin-video-generation/package.json @@ -22,9 +22,17 @@ "@elizaos/core": "workspace:*", "tsup": "8.3.5" }, + "devDependencies": { + "@biomejs/biome": "1.5.3", + "tsup": "^8.3.5" + }, "scripts": { "build": "tsup --format esm --dts", - "dev": "tsup --format esm --dts --watch" + "dev": "tsup --format esm --dts --watch", + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" }, "peerDependencies": { "whatwg-url": "7.1.0" diff --git a/packages/plugin-web-search/biome.json b/packages/plugin-web-search/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-web-search/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-web-search/eslint.config.mjs b/packages/plugin-web-search/eslint.config.mjs deleted file mode 100644 index 92fe5bbebe..0000000000 --- a/packages/plugin-web-search/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import eslintGlobalConfig from "../../eslint.config.mjs"; - -export default [...eslintGlobalConfig]; diff --git a/packages/plugin-web-search/package.json b/packages/plugin-web-search/package.json index d72a50372b..5e73a37231 100644 --- a/packages/plugin-web-search/package.json +++ b/packages/plugin-web-search/package.json @@ -24,10 +24,16 @@ "tsup": "8.3.5", "js-tiktoken": "1.0.15" }, + "devDependencies": { + "@biomejs/biome": "1.5.3" + }, "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", - "lint": "eslint --fix --cache ." + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" }, "peerDependencies": { "whatwg-url": "7.1.0" diff --git a/packages/plugin-whatsapp/biome.json b/packages/plugin-whatsapp/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-whatsapp/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-whatsapp/package.json b/packages/plugin-whatsapp/package.json index 13853dc3c1..0520cb7a9b 100644 --- a/packages/plugin-whatsapp/package.json +++ b/packages/plugin-whatsapp/package.json @@ -23,13 +23,18 @@ "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", "test": "vitest run", - "coverage": "vitest run --coverage" + "coverage": "vitest run --coverage", + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" }, "dependencies": { "@elizaos/core": "workspace:*", "axios": "1.7.8" }, "devDependencies": { + "@biomejs/biome": "1.5.3", "@types/node": "20.17.9", "@typescript-eslint/eslint-plugin": "8.16.0", "@typescript-eslint/parser": "8.16.0", diff --git a/packages/plugin-zerion/biome.json b/packages/plugin-zerion/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-zerion/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-zerion/eslint.config.mjs b/packages/plugin-zerion/eslint.config.mjs deleted file mode 100644 index 92fe5bbebe..0000000000 --- a/packages/plugin-zerion/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import eslintGlobalConfig from "../../eslint.config.mjs"; - -export default [...eslintGlobalConfig]; diff --git a/packages/plugin-zerion/package.json b/packages/plugin-zerion/package.json index e4dc7d3480..6d668a0a13 100644 --- a/packages/plugin-zerion/package.json +++ b/packages/plugin-zerion/package.json @@ -8,11 +8,15 @@ "@elizaos/core": "workspace:*" }, "devDependencies": { + "@biomejs/biome": "1.5.3", "tsup": "^8.3.5" }, "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", - "lint": "eslint --fix --cache ." + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" } } diff --git a/packages/plugin-zilliqa/biome.json b/packages/plugin-zilliqa/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-zilliqa/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-zilliqa/package.json b/packages/plugin-zilliqa/package.json index b83ac6845b..ed4c74e627 100644 --- a/packages/plugin-zilliqa/package.json +++ b/packages/plugin-zilliqa/package.json @@ -16,9 +16,16 @@ "@zilliqa-js/zilliqa": "^3.5.0", "tsup": "8.3.5" }, + "devDependencies": { + "@biomejs/biome": "1.5.3" + }, "scripts": { "build": "tsup --format esm --dts", - "dev": "tsup --format esm --dts --watch" + "dev": "tsup --format esm --dts --watch", + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" }, "peerDependencies": { "whatwg-url": "7.1.0" diff --git a/packages/plugin-zksync-era/biome.json b/packages/plugin-zksync-era/biome.json new file mode 100644 index 0000000000..818716a621 --- /dev/null +++ b/packages/plugin-zksync-era/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "suspicious": { + "noExplicitAny": "error" + }, + "style": { + "useConst": "error", + "useImportType": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "files": { + "ignore": [ + "dist/**/*", + "extra/**/*", + "node_modules/**/*" + ] + } +} \ No newline at end of file diff --git a/packages/plugin-zksync-era/eslint.config.mjs b/packages/plugin-zksync-era/eslint.config.mjs deleted file mode 100644 index 92fe5bbebe..0000000000 --- a/packages/plugin-zksync-era/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import eslintGlobalConfig from "../../eslint.config.mjs"; - -export default [...eslintGlobalConfig]; diff --git a/packages/plugin-zksync-era/package.json b/packages/plugin-zksync-era/package.json index 3ef1637369..da568e5fdb 100644 --- a/packages/plugin-zksync-era/package.json +++ b/packages/plugin-zksync-era/package.json @@ -23,9 +23,15 @@ "tsup": "^8.3.5", "viem": "2.22.2" }, + "devDependencies": { + "@biomejs/biome": "1.5.3" + }, "scripts": { "build": "tsup --format esm --dts", - "lint": "eslint --fix --cache ." + "lint": "biome check src/", + "lint:fix": "biome check --apply src/", + "format": "biome format src/", + "format:fix": "biome format --write src/" }, "peerDependencies": { "whatwg-url": "7.1.0"