UNPKG

create-eliza

Version:

Initialize an Eliza project

573 lines (566 loc) 19.1 kB
import { createRequire } from 'module'; const require = createRequire(import.meta.url); import { handleError } from "./chunk-WDKH5BMD.js"; import { AgentServer, character, checkEnvVarsForPlugin, jsonToCharacter, loadCharacterTryPath, promptForEnvVars, promptForServices } from "./chunk-2QIFMC6Z.js"; import { require_main } from "./chunk-ZMJ3QLUC.js"; import { source_default } from "./chunk-BY3DNMXE.js"; import { AgentRuntime, logger, settings, stringToUuid } from "./chunk-UIEY2LL5.js"; import { Command } from "./chunk-CKY7YPIS.js"; import { __toESM } from "./chunk-WCMDOJQK.js"; // src/commands/start.ts import fs2 from "node:fs"; import net from "node:net"; import os2 from "node:os"; import path2 from "node:path"; import { fileURLToPath } from "node:url"; var dotenv = __toESM(require_main(), 1); // src/utils/character-generator.ts var PLUGIN_MAP = { openai: "@elizaos/plugin-openai", anthropic: "@elizaos/plugin-anthropic", discord: "@elizaos/plugin-discord", twitter: "@elizaos/plugin-twitter", telegram: "@elizaos/plugin-telegram", sql: "@elizaos/plugin-sql" }; function generateCustomCharacter(services = [], aiModels = []) { const plugins = ["sql"]; plugins.push(...aiModels); plugins.push(...services); const uniquePlugins = [...new Set(plugins)]; const packageNames = uniquePlugins.map((plugin) => PLUGIN_MAP[plugin]); const customCharacter = { ...JSON.parse(JSON.stringify(character)), plugins: packageNames }; return customCharacter; } // src/utils/config-manager.ts import fs from "node:fs"; import os from "node:os"; import path from "node:path"; function getConfigFilePath() { const homeDir = os.homedir(); const elizaDir = path.join(homeDir, ".eliza"); return path.join(elizaDir, "config.json"); } function loadConfig() { try { const configPath = getConfigFilePath(); if (!fs.existsSync(configPath)) { return { services: [], aiModels: ["openai"], // Default to OpenAI lastUpdated: (/* @__PURE__ */ new Date()).toISOString(), isDefault: true // Mark as default config }; } const content = fs.readFileSync(configPath, "utf8"); return JSON.parse(content); } catch (error) { logger.warn(`Error loading configuration: ${error}`); return { services: [], aiModels: ["openai"], lastUpdated: (/* @__PURE__ */ new Date()).toISOString(), isDefault: true // Mark as default config }; } } function saveConfig(config2) { try { const configPath = getConfigFilePath(); const elizaDir = path.dirname(configPath); if (!fs.existsSync(elizaDir)) { fs.mkdirSync(elizaDir, { recursive: true }); } config2.lastUpdated = (/* @__PURE__ */ new Date()).toISOString(); fs.writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf8"); logger.info(`Configuration saved to ${configPath}`); } catch (error) { logger.error(`Error saving configuration: ${error}`); } } function getPluginStatus() { const allPlugins = [ "openai", "anthropic", "discord", "twitter", "telegram", "pglite" ]; const status = {}; for (const plugin of allPlugins) { status[plugin] = checkEnvVarsForPlugin(plugin); } return status; } function displayConfigStatus() { const config2 = loadConfig(); const pluginStatus = getPluginStatus(); logger.info("\n=== Current Configuration ==="); if (config2.isDefault) { logger.info( source_default.yellow( "Using default configuration - you will be prompted to customize your setup." ) ); } logger.info("Services:"); if (config2.services.length) { for (const service of config2.services) { const status = pluginStatus[service] ? source_default.green("\u2713 configured") : source_default.yellow("\u26A0 missing environment variables"); logger.info(` ${source_default.cyan(service)}: ${status}`); } } else { logger.info(` ${source_default.gray("No services configured")}`); } logger.info("AI Models:"); if (config2.aiModels.length) { for (const model of config2.aiModels) { const status = pluginStatus[model] ? source_default.green("\u2713 configured") : source_default.yellow("\u26A0 missing environment variables"); logger.info(` ${source_default.cyan(model)}: ${status}`); } } else { logger.info(` ${source_default.gray("No AI models configured")}`); } if (config2.lastUpdated && !config2.isDefault) { logger.info( `Last updated: ${source_default.gray(new Date(config2.lastUpdated).toLocaleString())}` ); } if (!config2.isDefault) { logger.info( "\nTo change this configuration, run with the --configure flag" ); } logger.info(""); } // src/commands/start.ts var __filename = fileURLToPath(import.meta.url); var __dirname = path2.dirname(__filename); var wait = (minTime = 1e3, maxTime = 3e3) => { const waitTime = Math.floor(Math.random() * (maxTime - minTime + 1)) + minTime; return new Promise((resolve) => setTimeout(resolve, waitTime)); }; async function promptForProjectPlugins(project, pluginToLoad) { const pluginsToPrompt = /* @__PURE__ */ new Set(); if (pluginToLoad?.name) { pluginsToPrompt.add(pluginToLoad.name.toLowerCase()); } if (project) { const agents = Array.isArray(project.agents) ? project.agents : project.agent ? [project.agent] : []; for (const agent of agents) { if (agent.plugins?.length) { for (const plugin of agent.plugins) { const pluginName = typeof plugin === "string" ? plugin : plugin.name; if (pluginName) { const simpleName = pluginName.split("/").pop()?.replace("plugin-", "") || pluginName; pluginsToPrompt.add(simpleName.toLowerCase()); } } } } } pluginsToPrompt.add("pglite"); for (const pluginName of pluginsToPrompt) { try { await promptForEnvVars(pluginName); } catch (error) { logger.warn( `Failed to prompt for ${pluginName} environment variables: ${error}` ); } } } async function startAgent(character2, server, init, plugins = [], options = {}) { character2.id ??= stringToUuid(character2.name); const runtime = new AgentRuntime({ character: character2, plugins }); if (init) { await init(runtime); } await runtime.initialize(); server.registerAgent(runtime); logger.debug(`Started ${runtime.character.name} as ${runtime.agentId}`); return runtime; } async function stopAgent(runtime, server) { await runtime.close(); server.unregisterAgent(runtime.agentId); } var checkPortAvailable = (port) => { return new Promise((resolve) => { const server = net.createServer(); server.once("error", (err) => { if (err.code === "EADDRINUSE") { resolve(false); } }); server.once("listening", () => { server.close(); resolve(true); }); server.listen(port); }); }; var startAgents = async (options) => { const homeDir = os2.homedir(); const elizaDir = path2.join(homeDir, ".eliza"); const elizaDbDir = path2.join(elizaDir, "db"); const envFilePath = path2.join(elizaDir, ".env"); if (!fs2.existsSync(elizaDir)) { fs2.mkdirSync(elizaDir, { recursive: true }); logger.info(`Created directory: ${elizaDir}`); } if (!fs2.existsSync(elizaDbDir)) { fs2.mkdirSync(elizaDbDir, { recursive: true }); logger.info(`Created database directory: ${elizaDbDir}`); } process.env.PGLITE_DATA_DIR = elizaDbDir; logger.info(`Using database directory: ${elizaDbDir}`); if (fs2.existsSync(envFilePath)) { dotenv.config({ path: envFilePath }); } try { await promptForEnvVars("pglite"); } catch (error) { logger.warn(`Error configuring database: ${error}`); } const existingConfig = loadConfig(); const pluginStatus = getPluginStatus(); let selectedServices = []; let selectedAiModels = []; console.log("*** existingConfig", existingConfig); const shouldConfigure = options.configure || existingConfig.isDefault; if (shouldConfigure) { displayConfigStatus(); if (existingConfig.isDefault) { logger.info("First time setup. Let's configure your Eliza agent."); } else { logger.info("Reconfiguration requested."); } await new Promise((resolve) => setTimeout(resolve, 100)); const userSelections = await promptForServices(); selectedServices = userSelections.services; selectedAiModels = userSelections.aiModels; saveConfig({ services: selectedServices, aiModels: selectedAiModels, lastUpdated: (/* @__PURE__ */ new Date()).toISOString() // isDefault is not included to indicate this is now a user-configured setup }); } else { selectedServices = existingConfig.services; selectedAiModels = existingConfig.aiModels; } const pluginsToPrompt = [ "pglite", ...selectedServices, ...selectedAiModels ].filter((plugin, index, self) => self.indexOf(plugin) === index); const missingEnvVars = pluginsToPrompt.filter( (plugin) => !pluginStatus[plugin] ); if (missingEnvVars.length > 0) { logger.info( `${missingEnvVars.length} plugins need configuration. Let's set them up.` ); for (const plugin of missingEnvVars) { logger.info(`Configuring ${plugin}...`); await promptForEnvVars(plugin); } logger.info("All required plugin configurations complete!"); } const customCharacter = generateCustomCharacter( selectedServices, selectedAiModels ); const postgresUrl = process.env.POSTGRES_URL; const server = new AgentServer({ dataDir: elizaDbDir, postgresUrl }); server.startAgent = async (character2) => { logger.info(`Starting agent for character ${character2.name}`); return startAgent(character2, server); }; server.stopAgent = (runtime) => { stopAgent(runtime, server); }; server.loadCharacterTryPath = loadCharacterTryPath; server.jsonToCharacter = jsonToCharacter; let serverPort = options.port || Number.parseInt(settings.SERVER_PORT || "3000"); let isProject = false; let isPlugin = false; let pluginModule = null; let projectModule = null; logger.info("Checking for project or plugin in current directory..."); const currentDir = process.cwd(); logger.info(`Current directory: ${currentDir}`); try { const packageJsonPath = path2.join(process.cwd(), "package.json"); if (fs2.existsSync(packageJsonPath)) { const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8")); if (packageJson.eliza?.type && packageJson.eliza.type === "plugin") { isPlugin = true; logger.info("Found Eliza plugin in current directory"); } if (packageJson.eliza?.type && packageJson.eliza.type === "project") { isProject = true; logger.info("Found Eliza project in current directory"); } if (!isProject && !isPlugin) { if (packageJson.description?.toLowerCase().includes("project")) { isProject = true; logger.info("Found project by description in package.json"); } } const mainEntry = packageJson.main; if (mainEntry) { const mainPath = path2.resolve(process.cwd(), mainEntry); if (fs2.existsSync(mainPath)) { try { const importedModule = await import(mainPath); if (isPlugin || importedModule.default && typeof importedModule.default === "object" && importedModule.default.name && typeof importedModule.default.init === "function") { isPlugin = true; pluginModule = importedModule.default; logger.info(`Loaded plugin: ${pluginModule?.name || "unnamed"}`); if (!pluginModule) { logger.warn( "Plugin loaded but no default export found, looking for other exports" ); for (const key in importedModule) { if (importedModule[key] && typeof importedModule[key] === "object" && importedModule[key].name && typeof importedModule[key].init === "function") { pluginModule = importedModule[key]; logger.info(`Found plugin export under key: ${key}`); break; } } } } else if (isProject || importedModule.default && typeof importedModule.default === "object" && importedModule.default.agents) { isProject = true; projectModule = importedModule; logger.info( `Loaded project with ${projectModule.default?.agents?.length || 0} agents` ); } } catch (importError) { logger.error(`Error importing module: ${importError}`); } } else { logger.error(`Main entry point ${mainPath} does not exist`); } } } else { const projectFiles = ["project.json", "eliza.json", "agents.json"]; for (const file of projectFiles) { const filePath = path2.join(process.cwd(), file); if (fs2.existsSync(filePath)) { try { const fileContent = fs2.readFileSync(filePath, "utf-8"); const projectData = JSON.parse(fileContent); if (projectData.agents || projectData.agent) { isProject = true; projectModule = { default: projectData }; logger.info(`Found project in ${file}`); break; } } catch (error) { logger.warn( `Error reading possible project file ${file}: ${error}` ); } } } if (!isProject && !isPlugin) { logger.info( "No package.json or project files found, using custom character" ); } } } catch (error) { logger.error(`Error checking for project/plugin: ${error}`); } if (isProject) { logger.info("Found project configuration"); if (projectModule?.default) { const project = projectModule.default; const agents = Array.isArray(project.agents) ? project.agents : project.agent ? [project.agent] : []; logger.info(`Project contains ${agents.length} agent(s)`); if (agents.length > 0) { logger.info( `Agents: ${agents.map((a) => a.character?.name || "unnamed").join(", ")}` ); } } else { logger.warn("Project module doesn't contain a valid default export"); } } else if (isPlugin) { logger.info(`Found plugin: ${pluginModule?.name || "unnamed"}`); } else { logger.info("No project or plugin found, will use custom character"); } if (isProject && projectModule?.default) { const project = projectModule.default; const agents = Array.isArray(project.agents) ? project.agents : project.agent ? [project.agent] : []; if (agents.length > 0) { logger.info(`Found ${agents.length} agents in project`); try { await promptForProjectPlugins(project); } catch (error) { logger.warn( `Failed to prompt for project environment variables: ${error}` ); } const startedAgents = []; for (const agent of agents) { try { logger.info(`Starting agent: ${agent.character.name}`); const runtime = await startAgent( agent.character, server, agent.init, agent.plugins || [] ); startedAgents.push(runtime); await new Promise((resolve) => setTimeout(resolve, 500)); } catch (agentError) { logger.error( `Error starting agent ${agent.character.name}: ${agentError}` ); } } if (startedAgents.length === 0) { logger.warn( "Failed to start any agents from project, falling back to custom character" ); await startAgent(customCharacter, server); } else { logger.info( `Successfully started ${startedAgents.length} agents from project` ); } } else { logger.warn( "Project found but no agents defined, falling back to custom character" ); await startAgent(customCharacter, server); } } else if (isPlugin && pluginModule) { if (pluginModule.name) { try { await promptForEnvVars(pluginModule.name); } catch (error) { logger.warn( `Failed to prompt for plugin environment variables: ${error}` ); } } logger.info( `Starting custom character with plugin: ${pluginModule.name || "unnamed plugin"}` ); const pluginsToLoad = [pluginModule]; await startAgent(customCharacter, server, void 0, pluginsToLoad); logger.info("Character started with plugin successfully"); } else { logger.info("Starting with custom character"); await startAgent(customCharacter, server); } while (!await checkPortAvailable(serverPort)) { logger.warn(`Port ${serverPort} is in use, trying ${serverPort + 1}`); serverPort++; } server.start(serverPort); if (serverPort !== Number.parseInt(settings.SERVER_PORT || "3000")) { logger.log(`Server started on alternate port ${serverPort}`); } let clientPath = path2.join(__dirname, "../../client"); if (!fs2.existsSync(clientPath)) { clientPath = path2.join(__dirname, "../../../../..", "packages/client/dist"); } if (fs2.existsSync(clientPath)) { logger.success( `Client UI is available at http://localhost:${serverPort}/client` ); } else { const clientSrcPath = path2.join( __dirname, "../../../..", "packages/client" ); if (fs2.existsSync(clientSrcPath)) { logger.info( "Client build not found. You can build it with: cd packages/client && npm run build" ); } } }; var start = new Command().name("start").description("Start the Eliza agent with configurable plugins and services").option( "-p, --port <port>", "Port to listen on", (val) => Number.parseInt(val) ).option( "-c, --configure", "Reconfigure services and AI models (skips using saved configuration)" ).option("--dev", "Start with development settings").option( "--character <character>", "Path or URL to character file to use instead of default" ).action(async (options) => { try { const characterPath = options.character; if (characterPath) { logger.info(`Loading character from ${characterPath}`); try { const characterData = await loadCharacterTryPath(characterPath); await startAgents(options); } catch (error) { logger.error(`Failed to load character: ${error}`); process.exit(1); } } else { await startAgents(options); } } catch (error) { handleError(error); } }); function registerCommand(cli) { return cli.addCommand(start); } export { wait, promptForProjectPlugins, start, registerCommand }; //# sourceMappingURL=chunk-USKCXWDY.js.map