diff --git a/typescript/.changeset/wide-lions-fly.md b/typescript/.changeset/wide-lions-fly.md new file mode 100644 index 000000000..e55e95110 --- /dev/null +++ b/typescript/.changeset/wide-lions-fly.md @@ -0,0 +1,11 @@ +--- +"create-onchain-agent": minor +--- + +Added code generation for individual building blocks + +Projects bootstrapped with the `create-onchain-agent` CLI will also have the `agenkit` CLI installed. +- `agentkit generate wallet-provider`: Generate a custom wallet provider +- `agentkit generate action-provider`: Generate a custom action provider +- `agentkit generate prepare`: Generate framework-agnostic AgentKit setup +- `agentkit generate create-agent`: Generate framework-specific agent creation diff --git a/typescript/create-onchain-agent/README.md b/typescript/create-onchain-agent/README.md index 02153a3e6..8798f66b9 100644 --- a/typescript/create-onchain-agent/README.md +++ b/typescript/create-onchain-agent/README.md @@ -2,7 +2,7 @@ ## Overview -`create-onchain-agent` is a CLI tool powered by [AgentKit](https://github.com/coinbase/agentkit) that allows developers to quickly scaffold an **onchain agent** project. This tool simplifies the setup process by generating a project with predefined configurations, including blockchain network selection, wallet providers, and framework setup. +`create-onchain-agent` is a CLI tool powered by [AgentKit](https://github.com/coinbase/agentkit) that provides multiple utilities for creating and integrating onchain agents into your projects. Whether you're starting a new project or adding AgentKit to an existing application, this tool offers flexible options to meet your needs. ## Prerequisites @@ -13,61 +13,47 @@ Before using `create-onchain-agent`, ensure you have the following installed: ## Usage -To use `create-onchain-agent`, simply run: +### Quickstart Templates + +To create a new project from scratch: ```sh npm create onchain-agent@latest ``` -This command will guide you through setting up an onchain agent project by prompting for necessary configuration options. - -## Features - -- **Guided setup**: Interactive prompts help configure the project. -- **Supports multiple AI frameworks**. -- **Supports multiple blockchain networks**. -- **Select your preferred wallet provider**. -- **Supports a preconfigured Next.js project with React, Tailwind CSS, and ESLint**. -- **Supports a preconfigured Model Context Protocol Server project**. -- **Automates environment setup**. - -### **Setup Process** -The CLI will prompt you for the following details: - -1. **Project Name**: The name of your new onchain agent project. -2. **Package Name**: The npm package name (auto-formatted if needed). -3. **Framework**: Choose from available AI frameworks. -4. **Network**: Choose from available blockchain networks. -5. **Chain ID**: Specify if using a custom network. -6. **Wallet Provider**: Select a preferred method for wallet management. - -After answering the prompts, the CLI will: +This command lets you choose between two project templates: +- **Next.js Template**: A full-stack web application with React, Tailwind CSS, and ESLint +- **MCP Template**: A Model Context Protocol server project -- Generate the project structure. -- Copy necessary template files. -- Configure the selected settings. -- Display next steps to get started. +### Component Generation -## Getting Started - -Once your project is created, navigate into the directory and install dependencies: +After installing the `create-onchain-agent` CLI, you will also have the `agenkit` CLI installed. This allows you to generate components for your project. ```sh -cd my-project -npm install -``` +# Generate a custom wallet provider +agentkit generate wallet-provider -Then, configure your environment variables: +# Generate a custom action provider +agentkit generate action-provider -```sh -mv .env.local .env +# Generate framework-agnostic AgentKit setup. +agentkit generate prepare + +# Generate framework-specific agent creation +agentkit generate create-agent ``` -Run the development server: +## Features -```sh -npm run dev -``` +- **Multiple project templates**: + - Next.js template for web applications + - MCP template for Model Context Protocol servers +- **Component generators** for existing projects +- **Guided setup** with interactive prompts +- **Support for multiple AI frameworks** +- **Support for multiple blockchain networks** +- **Flexible wallet provider options** +- **Automated environment setup** ## Documentation & Support diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index f40169353..54cb791a2 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -25,7 +25,8 @@ "templates/**" ], "bin": { - "create-onchain-agent": "./dist/esm/cli.js" + "create-onchain-agent": "./dist/esm/create.js", + "agentkit": "./dist/esm/generate.js" }, "sideEffects": false, "type": "module", @@ -33,8 +34,10 @@ "./package.json": "./package.json" }, "dependencies": { + "@types/nunjucks": "^3.2.6", "cac": "^6.7.14", "cross-spawn": "^7.0.3", + "nunjucks": "^3.2.4", "ora": "^8.1.0", "picocolors": "^1.1.0", "prompts": "^2.4.2" diff --git a/typescript/create-onchain-agent/src/actions/createActionProvider.ts b/typescript/create-onchain-agent/src/actions/createActionProvider.ts new file mode 100644 index 000000000..99b9caec5 --- /dev/null +++ b/typescript/create-onchain-agent/src/actions/createActionProvider.ts @@ -0,0 +1,75 @@ +import fs from "fs/promises"; +import nunjucks from "nunjucks"; +import path from "path"; +import pc from "picocolors"; +import prompts from "prompts"; +import { fileURLToPath } from "url"; +import { toCamelCase, toClassName, toSnakeCase } from "../common/utils.js"; + +// Get current file's directory in ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Creates a new action provider + */ +export async function createActionProvider() { + // Configure nunjucks with the correct path + nunjucks.configure(path.join(__dirname, "../../../templates"), { + autoescape: false, + trimBlocks: true, + lstripBlocks: true, + }); + + // Get user input + const answers = await prompts([ + { + type: "text", + name: "name", + message: "What is the name of your action provider?", + validate: (value: string) => (value.length > 0 ? true : "Name is required"), + }, + { + type: "select", + name: "walletProvider", + message: "Which wallet provider is expected?", + choices: [ + { title: "None (No wallet/network required)", value: "none" }, + { title: "WalletProvider (Any network)", value: "WalletProvider" }, + { title: "EvmWalletProvider (EVM networks)", value: "EvmWalletProvider" }, + { title: "SvmWalletProvider (Solana networks)", value: "SvmWalletProvider" }, + ], + }, + ]); + + // Process the name variations + const baseName = toClassName(answers.name); + const className = `${baseName}ActionProvider`; + const fileName = `${toCamelCase(baseName)}ActionProvider`; + const snakeName = toSnakeCase(answers.name); + const exportName = `${toCamelCase(baseName)}ActionProvider`; + + const walletProvider = answers.walletProvider; + + // Generate code using nunjucks + const generatedCode = nunjucks.render("actionProvider/actionProvider.njk", { + name: exportName, + className, + walletProvider, + actionName: `${snakeName}_action`, + schemaName: `${baseName}ActionSchema`, + }); + + // Write files directly to current directory + await fs.writeFile(`./${fileName}.ts`, generatedCode); + + // Generate schema file using nunjucks with updated schema name + const schemaCode = nunjucks.render("actionProvider/schema.njk", { + className: `${baseName}Action`, + actionName: `${snakeName}_action`, + }); + + await fs.writeFile(`./schemas.ts`, schemaCode); + + console.log(pc.green(`Successfully created ${className}.ts and schemas.ts`)); +} diff --git a/typescript/create-onchain-agent/src/actions/createAgent.ts b/typescript/create-onchain-agent/src/actions/createAgent.ts new file mode 100644 index 000000000..a94b87022 --- /dev/null +++ b/typescript/create-onchain-agent/src/actions/createAgent.ts @@ -0,0 +1,63 @@ +import fs from "fs/promises"; +import path from "path"; +import pc from "picocolors"; +import prompts from "prompts"; +import { copyTemplate } from "../common/fileSystem.js"; + +type AgentFramework = "langchain" | "vercelAISDK"; + +/** + * Creates an agent by selecting a framework and copying the appropriate template. + * + * - Prompts user to select between LangChain and Vercel AI SDK frameworks + * - Copies the selected framework's agent implementation + * - Cleans up unused framework files + */ +export async function createAgent() { + let result: prompts.Answers<"framework">; + + try { + result = await prompts( + [ + { + type: "select", + name: "framework", + message: pc.reset("Choose a framework:"), + choices: [ + { title: "LangChain", value: "langchain" }, + { title: "Vercel AI SDK", value: "vercelAISDK" }, + ] as { title: string; value: AgentFramework }[], + }, + ], + { + onCancel: () => { + console.log("\nAgent creation cancelled."); + process.exit(0); + }, + }, + ); + } catch (error) { + console.error("An error occurred during agent creation", error); + process.exit(1); + } + + const { framework } = result; + + try { + const root = await copyTemplate("createAgent", "createAgent"); + + // Copy the selected framework's implementation to the destination + const selectedRoutePath = path.join(root, "framework", framework, "createAgent.ts"); + const newRoutePath = path.join(process.cwd(), "createAgent.ts"); + + await fs.copyFile(selectedRoutePath, newRoutePath); + + // Clean up the temporary directory + await fs.rm(root, { recursive: true, force: true }); + + console.log(pc.green("Successfully created createAgent.ts")); + } catch (error) { + console.error("Error setting up createAgent:", error); + process.exit(1); + } +} diff --git a/typescript/create-onchain-agent/src/actions/createWalletProvider.ts b/typescript/create-onchain-agent/src/actions/createWalletProvider.ts new file mode 100644 index 000000000..aa79f68ae --- /dev/null +++ b/typescript/create-onchain-agent/src/actions/createWalletProvider.ts @@ -0,0 +1,57 @@ +import fs from "fs/promises"; +import nunjucks from "nunjucks"; +import path from "path"; +import pc from "picocolors"; +import prompts from "prompts"; +import { fileURLToPath } from "url"; +import { toCamelCase, toClassName, toSnakeCase } from "../common/utils.js"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Creates a new wallet provider + */ +export async function createWalletProvider() { + nunjucks.configure(path.join(__dirname, "../../../templates"), { + autoescape: false, + trimBlocks: true, + lstripBlocks: true, + }); + + const answers = await prompts([ + { + type: "text", + name: "name", + message: "What is the name of your wallet provider?", + validate: (value: string) => (value.length > 0 ? true : "Name is required"), + }, + { + type: "select", + name: "protocolFamily", + message: "Which protocol family will this support?", + choices: [ + { title: "EVM", value: "EVM" }, + { title: "SVM", value: "SVM" }, + ], + }, + ]); + + const className = `${toClassName(answers.name)}WalletProvider`; + const fileName = `${toCamelCase(answers.name)}WalletProvider`; + const snakeName = toSnakeCase(answers.name); + + const baseClass = answers.protocolFamily === "EVM" ? "EvmWalletProvider" : "SvmWalletProvider"; + + const generatedCode = nunjucks.render("walletProvider/walletProvider.njk", { + name: snakeName, + className, + protocolFamily: answers.protocolFamily, + baseClass, + }); + + // Write file directly to current directory + await fs.writeFile(`./${fileName}.ts`, generatedCode); + + console.log(pc.green(`Successfully created ${fileName}.ts`)); +} diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/actions/initProject.ts similarity index 96% rename from typescript/create-onchain-agent/src/cli.ts rename to typescript/create-onchain-agent/src/actions/initProject.ts index 8bf28f960..e3e4730fa 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/actions/initProject.ts @@ -1,20 +1,24 @@ -#!/usr/bin/env node import fs from "fs"; import ora from "ora"; import path from "path"; import pc from "picocolors"; import prompts from "prompts"; -import { EVM_NETWORKS, NetworkToWalletProviders, SVM_NETWORKS } from "./constants.js"; -import { Network, WalletProviderChoice, Framework } from "./types.js"; -import { copyTemplate } from "./fileSystem.js"; import { + EVM_NETWORKS, + Frameworks, + FrameworkToTemplates, + NetworkToWalletProviders, + SVM_NETWORKS, +} from "../common/constants.js"; +import { copyTemplate } from "../common/fileSystem.js"; +import { Framework, Network, WalletProviderChoice } from "../common/types.js"; +import { + getWalletProviders, + handleMcpSelection, handleNextSelection, isValidPackageName, toValidPackageName, - getWalletProviders, - handleMcpSelection, -} from "./utils.js"; -import { Frameworks, FrameworkToTemplates } from "./constants.js"; +} from "../common/utils.js"; /** * Initializes the project creation process. @@ -25,7 +29,7 @@ import { Frameworks, FrameworkToTemplates } from "./constants.js"; * - Handles network and wallet provider selection logic. * - Displays a summary of the created project along with next steps. */ -async function init() { +export async function initProject() { console.log( `${pc.blue(` █████ ██████ ███████ ███ ██ ████████ ██ ██ ██ ████████ @@ -240,7 +244,7 @@ async function init() { const spinner = ora(`Creating ${projectName}...`).start(); // Copy template over to new project - const root = await copyTemplate(projectName, packageName, template); + const root = await copyTemplate(projectName, template, packageName); // Handle selection-specific logic over copied-template switch (template) { @@ -314,7 +318,3 @@ async function init() { console.log(" - Join the community"); console.log(pc.blueBright(" - https://discord.gg/CDP\n")); } - -init().catch(e => { - console.error(e); -}); diff --git a/typescript/create-onchain-agent/src/actions/prepareAgentkit.ts b/typescript/create-onchain-agent/src/actions/prepareAgentkit.ts new file mode 100644 index 000000000..ae6ee514a --- /dev/null +++ b/typescript/create-onchain-agent/src/actions/prepareAgentkit.ts @@ -0,0 +1,172 @@ +import fs from "fs/promises"; +import path from "path"; +import pc from "picocolors"; +import prompts from "prompts"; +import { + EVM_NETWORKS, + PrepareAgentkitRouteConfigurations, + SVM_NETWORKS, +} from "../common/constants.js"; +import { copyTemplate } from "../common/fileSystem.js"; +import { Network, WalletProviderChoice } from "../common/types.js"; +import { getNetworkType, getWalletProviders } from "../common/utils.js"; + +/** + * Prompts user for network and wallet provider selection, then sets up the prepare-agentkit file + * by promoting the correct template and cleaning up unused files. + */ +export async function prepareAgentKit() { + let result: prompts.Answers< + "networkFamily" | "networkType" | "network" | "chainId" | "rpcUrl" | "walletProvider" + >; + + try { + result = await prompts( + [ + { + type: "select", + name: "networkFamily", + message: pc.reset("Choose a network family:"), + choices: [ + { title: "Ethereum Virtual Machine (EVM)", value: "EVM" }, + { title: "Solana Virtual Machine (SVM)", value: "SVM" }, + ], + }, + { + type: (prev, { networkFamily }) => (networkFamily === "EVM" ? "select" : null), + name: "networkType", + message: pc.reset("Choose network type:"), + choices: [ + { title: "Mainnet", value: "mainnet" }, + { title: "Testnet", value: "testnet" }, + { title: "Custom Chain ID", value: "custom" }, + ], + }, + { + type: (prev, { networkFamily, networkType }) => { + // For SVM, always show network selection + if (networkFamily === "SVM") return "select"; + // For EVM, show network selection only if not custom + return networkType === "custom" ? null : "select"; + }, + name: "network", + message: pc.reset("Choose a network:"), + choices: (prev, { networkFamily, networkType }) => { + if (networkFamily === "SVM") { + return SVM_NETWORKS.map(network => ({ + title: network, + value: network as Network, + })); + } else { + return EVM_NETWORKS.filter(n => { + const isMainnet = n.includes("mainnet"); + return networkType === "mainnet" ? isMainnet : !isMainnet; + }).map(network => ({ + title: network === "base-sepolia" ? `${network} (default)` : network, + value: network as Network, + })); + } + }, + }, + { + type: (prev, { networkFamily, networkType }) => + networkFamily === "EVM" && networkType === "custom" ? "text" : null, + name: "chainId", + message: pc.reset("Enter your chain ID:"), + validate: value => + value.trim() + ? Number.parseInt(value) + ? true + : "Chain ID must be a number." + : "Chain ID cannot be empty.", + }, + { + type: (prev, { networkFamily, networkType }) => + networkFamily === "EVM" && networkType === "custom" ? "text" : null, + name: "rpcUrl", + message: pc.reset("Enter your RPC URL:"), + validate: value => + value.trim() + ? value.startsWith("http") + ? true + : "RPC URL must start with http:// or https://" + : "RPC URL cannot be empty.", + }, + { + type: (prev, { networkFamily, networkType }) => { + // For custom EVM networks, auto-select Viem by returning null + if (networkFamily === "EVM" && networkType === "custom") { + return null; + } + return "select"; + }, + name: "walletProvider", + message: (prev, { network }) => { + const walletDescriptions: Record = { + CDP: "Uses Coinbase Developer Platform (CDP)'s managed wallet.", + SmartWallet: "Uses Coinbase Developer Platform (CDP)'s Smart Wallet.", + Viem: "Client-side Ethereum wallet.", + Privy: "Authentication and wallet infrastructure.", + SolanaKeypair: "Client-side Solana wallet.", + }; + + const providerDescriptions = getWalletProviders(network as Network) + .map(provider => ` - ${provider}: ${walletDescriptions[provider]}`) + .join("\n"); + + return pc.reset(`Choose a wallet provider:\n${providerDescriptions}\n`); + }, + choices: (prev, { network }) => { + const walletProviders = getWalletProviders(network as Network); + return walletProviders.map(provider => ({ + title: provider === walletProviders[0] ? `${provider} (default)` : provider, + value: provider, + })); + }, + }, + ], + { + onCancel: () => { + console.log("\nPrepare-agentkit creation cancelled."); + process.exit(0); + }, + }, + ); + } catch (error) { + console.error("An error occurred during prepare-agentkit creation", error); + process.exit(1); + } + + const { network, chainId, walletProvider = "Viem" } = result; + + // Determine network type (EVM, CUSTOM_EVM, or SVM) + const networkFamily = getNetworkType(network, chainId); + if (!networkFamily) { + throw new Error("Unsupported network and chainId selected"); + } + + // Get the route configuration for the selected wallet provider + const routeConfig = + PrepareAgentkitRouteConfigurations[networkFamily][walletProvider as WalletProviderChoice]; + if (!routeConfig) { + throw new Error("Selected invalid network & wallet provider combination"); + } + + try { + const root = await copyTemplate("prepareAgentkit", "prepareAgentkit"); + + // Copy the selected route to the destination + const selectedRoutePath = path.join(root, "agentkit", routeConfig.route); + const newRoutePath = path.join(process.cwd(), "prepareAgentkit.ts"); + + await fs.copyFile(selectedRoutePath, newRoutePath); + + const agentkitDir = path.join(root); + await fs.rm(agentkitDir, { recursive: true, force: true }); + + console.log(pc.green("Successfully created prepareAgentkit.ts")); + } catch (error) { + console.error("Error setting up prepareAgentkit:", error); + process.exit(1); + } +} diff --git a/typescript/create-onchain-agent/src/constants.ts b/typescript/create-onchain-agent/src/common/constants.ts similarity index 88% rename from typescript/create-onchain-agent/src/constants.ts rename to typescript/create-onchain-agent/src/common/constants.ts index 16f12cd3a..e6bd44936 100644 --- a/typescript/create-onchain-agent/src/constants.ts +++ b/typescript/create-onchain-agent/src/common/constants.ts @@ -1,4 +1,8 @@ -import { AgentkitRouteConfiguration, MCPRouteConfiguration } from "./types"; +import { + AgentkitRouteConfiguration, + PrepareAgentkitRouteConfiguration, + MCPRouteConfiguration, +} from "./types.js"; export const EVM_NETWORKS = [ "base-mainnet", @@ -159,7 +163,7 @@ export const Frameworks = ["Langchain", "Vercel AI SDK", "Model Context Protocol export type Framework = (typeof Frameworks)[number]; -export const Templates = ["next", "mcp"] as const; +export const Templates = ["next", "mcp", "prepareAgentkit", "createAgent"] as const; export type Template = (typeof Templates)[number]; @@ -226,3 +230,36 @@ export const MCPRouteConfigurations: Record< }, }, }; + +export const PrepareAgentkitRouteConfigurations: Record< + "EVM" | "CUSTOM_EVM" | "SVM", + Partial> +> = { + EVM: { + CDP: { + route: "evm/cdp/prepareAgentkit.ts", + }, + Viem: { + route: "evm/viem/prepareAgentkit.ts", + }, + Privy: { + route: "evm/privy/prepareAgentkit.ts", + }, + SmartWallet: { + route: "evm/smart/prepareAgentkit.ts", + }, + }, + CUSTOM_EVM: { + Viem: { + route: "custom-evm/viem/prepareAgentkit.ts", + }, + }, + SVM: { + SolanaKeypair: { + route: "svm/solana-keypair/prepareAgentkit.ts", + }, + Privy: { + route: "svm/privy/prepareAgentkit.ts", + }, + }, +}; diff --git a/typescript/create-onchain-agent/src/fileSystem.ts b/typescript/create-onchain-agent/src/common/fileSystem.ts similarity index 74% rename from typescript/create-onchain-agent/src/fileSystem.ts rename to typescript/create-onchain-agent/src/common/fileSystem.ts index c4e54432a..9debca45f 100644 --- a/typescript/create-onchain-agent/src/fileSystem.ts +++ b/typescript/create-onchain-agent/src/common/fileSystem.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from "url"; import { optimizedCopy } from "./utils.js"; import { Template } from "./types.js"; -const sourceDir = path.resolve(fileURLToPath(import.meta.url), "../../../templates"); +const sourceDir = path.resolve(fileURLToPath(import.meta.url), "../../../../templates"); const renameFiles: Record = { _gitignore: ".gitignore", @@ -57,29 +57,38 @@ async function copyDir(src: string, dest: string): Promise { } /** - * Copies a project template to a new directory and updates the package name. + * Copies a project template to a new directory and optionally updates the package name. * * - Copies the template from the source directory. - * - Updates the `package.json` file with the provided package name. + * - If packageName is provided, updates the `package.json` file with the provided package name. * * @param {string} projectName - The name of the new project directory. - * @param {string} packageName - The npm package name to use (auto-formatted if empty). * @param {Template} template - The template to use. + * @param {string} [packageName] - Optional npm package name to use. * @returns {Promise} The path of the newly created project directory. */ export async function copyTemplate( projectName: string, - packageName: string, template: Template, + packageName?: string, ): Promise { const root = path.join(process.cwd(), projectName); await copyDir(getSourceDir(template), root); - const pkgPath = path.join(root, "package.json"); - const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8")); - pkg.name = packageName; - - await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); + // Only update package.json if packageName is provided + if (packageName) { + const pkgPath = path.join(root, "package.json"); + try { + const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8")); + pkg.name = packageName; + await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); + } catch (error) { + // Ignore errors if package.json doesn't exist + if ((error as NodeJS.ErrnoException).code !== "ENOENT") { + throw error; + } + } + } return root; } diff --git a/typescript/create-onchain-agent/src/types.ts b/typescript/create-onchain-agent/src/common/types.ts similarity index 81% rename from typescript/create-onchain-agent/src/types.ts rename to typescript/create-onchain-agent/src/common/types.ts index add17ac10..81b366dff 100644 --- a/typescript/create-onchain-agent/src/types.ts +++ b/typescript/create-onchain-agent/src/common/types.ts @@ -1,4 +1,4 @@ -import { Network } from "./constants"; +import { Network } from "./constants.js"; export type AgentkitRouteConfiguration = { env: { @@ -14,6 +14,10 @@ export type MCPRouteConfiguration = { configRoute: `${string}.json`; }; +export type PrepareAgentkitRouteConfiguration = { + route: `${string}/prepareAgentkit.ts`; +}; + export type NetworkSelection = { networkFamily: "EVM" | "SVM"; networkType: "mainnet" | "testnet" | "custom"; diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/common/utils.ts similarity index 92% rename from typescript/create-onchain-agent/src/utils.ts rename to typescript/create-onchain-agent/src/common/utils.ts index e326e2b8e..7543f1b75 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/common/utils.ts @@ -1,6 +1,6 @@ import fs from "fs/promises"; import path from "path"; -import { EVMNetwork, Framework, Network, SVMNetwork, WalletProviderChoice } from "./types"; +import { EVMNetwork, Framework, Network, SVMNetwork, WalletProviderChoice } from "./types.js"; import { EVM_NETWORKS, NetworkToWalletProviders, @@ -376,3 +376,41 @@ export async function handleMcpSelection( await fs.rm(path.join(srcDir, "agentkit"), { recursive: true, force: true }); } + +/** + * Converts a string to camel case + * + * @param str - The string to convert to camel case + * @returns The camel case version of the string + */ +export function toCamelCase(str: string): string { + // First character should be lowercase + return str.charAt(0).toLowerCase() + str.slice(1); +} + +/** + * Converts a string to snake case + * + * @param str - The string to convert to snake case + * @returns The snake case version of the string + */ +export function toSnakeCase(str: string): string { + return str + .replace(/([a-z])([A-Z])/g, "$1_$2") + .replace(/([A-Z])([A-Z][a-z])/g, "$1_$2") + .toLowerCase(); +} + +/** + * Converts a string to a class name + * + * @param str - The string to convert to class name + * @returns The class name version of the string + */ +export function toClassName(str: string): string { + // Capitalize first letter of each word, remove any "Provider" suffix + return str + .split(/(?=[A-Z])|[\s_-]/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(""); +} diff --git a/typescript/create-onchain-agent/src/create.ts b/typescript/create-onchain-agent/src/create.ts new file mode 100644 index 000000000..6e0891b45 --- /dev/null +++ b/typescript/create-onchain-agent/src/create.ts @@ -0,0 +1,13 @@ +#!/usr/bin/env node +import { initProject } from "./actions/initProject.js"; + +/** + * Handles command line arguments and executes the appropriate action + */ +async function handleArgs() { + await initProject(); +} + +handleArgs().catch(e => { + console.error(e); +}); diff --git a/typescript/create-onchain-agent/src/generate.ts b/typescript/create-onchain-agent/src/generate.ts new file mode 100644 index 000000000..1d3d4df64 --- /dev/null +++ b/typescript/create-onchain-agent/src/generate.ts @@ -0,0 +1,93 @@ +#!/usr/bin/env node + +import { createActionProvider } from "./actions/createActionProvider.js"; +import { createAgent } from "./actions/createAgent.js"; +import { prepareAgentKit } from "./actions/prepareAgentkit.js"; +import { createWalletProvider } from "./actions/createWalletProvider.js"; + +const VALID_GENERATE_TYPES = [ + "action-provider", + "wallet-provider", + "prepare", + "create-agent", +] as const; +type GenerateType = (typeof VALID_GENERATE_TYPES)[number]; + +/** + * Finds command arguments regardless of script invocation method + * + * @param args - The command line arguments + * @returns The command and type + */ +function findCommands(args: string[]): { command: string | null; type: string | null } { + const generateIndex = args.findIndex(arg => arg === "generate"); + if (generateIndex === -1) { + return { command: null, type: null }; + } + + const type = args[generateIndex + 1]; + return { command: "generate", type }; +} + +/** + * Checks if a value is a valid generate type + * + * @param value - The value to check + * @returns True if the value is a valid generate type, false otherwise + */ +function isGenerateType(value: string | null): value is GenerateType { + return Boolean(value && VALID_GENERATE_TYPES.includes(value as GenerateType)); +} + +/** + * Handles command line arguments and executes the appropriate action + */ +async function handleArgs() { + const { command, type } = findCommands(process.argv); + + if (!command) { + console.error("Error: Please provide a valid command (generate)"); + process.exit(1); + } + + switch (command) { + case "generate": { + if (!type) { + console.error("Error: Please specify what to generate"); + console.error(`Valid options: ${VALID_GENERATE_TYPES.join(", ")}`); + break; + } + + if (!isGenerateType(type)) { + console.error(`Error: Unknown generate argument: ${type}`); + console.error(`Valid options: ${VALID_GENERATE_TYPES.join(", ")}`); + break; + } + + switch (type) { + case "action-provider": + await createActionProvider(); + break; + case "wallet-provider": + await createWalletProvider(); + break; + case "prepare": + await prepareAgentKit(); + break; + case "create-agent": + await createAgent(); + break; + } + break; + } + default: { + console.error(`Error: Unknown command: ${command}`); + break; + } + } +} + +handleArgs().catch(e => { + console.error(e); + process.exit(1); +}); diff --git a/typescript/create-onchain-agent/templates/actionProvider/actionProvider.njk b/typescript/create-onchain-agent/templates/actionProvider/actionProvider.njk new file mode 100644 index 000000000..e1750b293 --- /dev/null +++ b/typescript/create-onchain-agent/templates/actionProvider/actionProvider.njk @@ -0,0 +1,22 @@ +import { customActionProvider{% if walletProvider !== "none" %}, {{ walletProvider }}{% endif %} } from "@coinbase/agentkit"; +import { {{ schemaName }} } from "./schemas"; +import { z } from "zod"; + +/** + * Creates a {{ className }} action provider. + * To create multiple actions, pass in an array of actions to createActionProvider. + */ +export const {{ name }} = () => + customActionProvider{% if walletProvider !== "none" %}<{{ walletProvider }}>{% endif %}({ + name: "{{ actionName }}", + description: `This tool will perform a {{ className }} operation.`, + schema: {{ schemaName }}, + invoke: async ({% if walletProvider !== "none" %}wallet: {{ walletProvider }}, {% endif %}args: z.infer) => { + try { + // Do work here + return `Successfully performed {{ actionName }} and returned the response`; + } catch (error) { + return `Error performing {{ actionName }}: Error: ${error}`; + } + }, + }); diff --git a/typescript/create-onchain-agent/templates/actionProvider/schema.njk b/typescript/create-onchain-agent/templates/actionProvider/schema.njk new file mode 100644 index 000000000..0a460808c --- /dev/null +++ b/typescript/create-onchain-agent/templates/actionProvider/schema.njk @@ -0,0 +1,11 @@ +import { z } from "zod"; + +/** + * Input schema for {{ className }}'s {{ actionName }} action. + */ +export const {{ className }}Schema = z + .object({ + payload: z.string().describe("The payload to send to the action provider"), + }) + .strip() + .describe("Instructions for {{ actionName }}"); diff --git a/typescript/create-onchain-agent/templates/createAgent/framework/langchain/createAgent.ts b/typescript/create-onchain-agent/templates/createAgent/framework/langchain/createAgent.ts new file mode 100644 index 000000000..2ed9d4728 --- /dev/null +++ b/typescript/create-onchain-agent/templates/createAgent/framework/langchain/createAgent.ts @@ -0,0 +1,76 @@ +import { getLangChainTools } from "@coinbase/agentkit-langchain"; +import { MemorySaver } from "@langchain/langgraph"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { ChatOpenAI } from "@langchain/openai"; +import { prepareAgentkitAndWalletProvider } from "./prepareAgentkit"; + +/** + * Agent Configuration Guide + * + * This file handles the core configuration of your AI agent's behavior and capabilities. + * + * Key Steps to Customize Your Agent: + * + * 1. Select your LLM: + * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM + * - Configure model parameters like temperature and max tokens + * + * 2. Instantiate your Agent: + * - Pass the LLM, tools, and memory into `createReactAgent()` + * - Configure agent-specific parameters + */ + +// The agent +let agent: ReturnType; + +/** + * Initializes and returns an instance of the AI agent. + * If an agent instance already exists, it returns the existing one. + * + * @function getOrInitializeAgent + * @returns {Promise>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function createAgent(): Promise> { + // If agent has already been initialized, return it + if (agent) { + return agent; + } + + try { + const { agentkit, walletProvider } = await prepareAgentkitAndWalletProvider(); + + // Initialize LLM: https://platform.openai.com/docs/models#gpt-4o + const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); + + const tools = await getLangChainTools(agentkit); + const memory = new MemorySaver(); + + const canUseFaucet = walletProvider.getNetwork().networkId == "base-sepolia"; + const faucetMessage = `If you ever need funds, you can request them from the faucet.`; + const cantUseFaucetMessage = `If you need funds, you can provide your wallet details and request funds from the user.`; + agent = createReactAgent({ + llm, + tools, + checkpointSaver: memory, + messageModifier: ` + You are a helpful agent that can interact onchain using the Coinbase Developer Platform AgentKit. You are + empowered to interact onchain using your tools. ${canUseFaucet ? faucetMessage : cantUseFaucetMessage}. + Before executing your first action, get the wallet details to see what network + you're on. If there is a 5XX (internal) HTTP error code, ask the user to try again later. If someone + asks you to do something you can't do with your currently available tools, you must say so, and + encourage them to implement it themselves using the CDP SDK + Agentkit, recommend they go to + docs.cdp.coinbase.com for more information. Be concise and helpful with your responses. Refrain from + restating your tools' descriptions unless it is explicitly requested. + `, + }); + + return agent; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/createAgent/framework/vercelAISDK/createAgent.ts b/typescript/create-onchain-agent/templates/createAgent/framework/vercelAISDK/createAgent.ts new file mode 100644 index 000000000..860a15402 --- /dev/null +++ b/typescript/create-onchain-agent/templates/createAgent/framework/vercelAISDK/createAgent.ts @@ -0,0 +1,80 @@ +import { openai } from "@ai-sdk/openai"; +import { getVercelAITools } from "@coinbase/agentkit-vercel-ai-sdk"; +import { prepareAgentkitAndWalletProvider } from "./prepareAgentkit"; + +/** + * Agent Configuration Guide + * + * This file handles the core configuration of your AI agent's behavior and capabilities. + * + * Key Steps to Customize Your Agent: + * + * 1. Select your LLM: + * - Modify the `openai` instantiation to choose your preferred LLM + * - Configure model parameters like temperature and max tokens + * + * 2. Instantiate your Agent: + * - Pass the LLM, tools, and memory into `createReactAgent()` + * - Configure agent-specific parameters + */ + +// The agent +type Agent = { + tools: ReturnType; + system: string; + model: ReturnType; + maxSteps?: number; +}; +let agent: Agent; + +/** + * Initializes and returns an instance of the AI agent. + * If an agent instance already exists, it returns the existing one. + * + * @function getOrInitializeAgent + * @returns {Promise>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function createAgent(): Promise { + // If agent has already been initialized, return it + if (agent) { + return agent; + } + + try { + // Initialize LLM: https://platform.openai.com/docs/models#gpt-4o + const model = openai("gpt-4o-mini"); + + const { agentkit, walletProvider } = await prepareAgentkitAndWalletProvider(); + + const canUseFaucet = walletProvider.getNetwork().networkId == "base-sepolia"; + const faucetMessage = `If you ever need funds, you can request them from the faucet.`; + const cantUseFaucetMessage = `If you need funds, you can provide your wallet details and request funds from the user.`; + const system = ` + You are a helpful agent that can interact onchain using the Coinbase Developer Platform AgentKit. You are + empowered to interact onchain using your tools. ${canUseFaucet ? faucetMessage : cantUseFaucetMessage}. + Before executing your first action, get the wallet details to see what network + you're on. If there is a 5XX (internal) HTTP error code, ask the user to try again later. If someone + asks you to do something you can't do with your currently available tools, you must say so, and + encourage them to implement it themselves using the CDP SDK + Agentkit, recommend they go to + docs.cdp.coinbase.com for more information. Be concise and helpful with your responses. Refrain from + restating your tools' descriptions unless it is explicitly requested. + `; + const tools = getVercelAITools(agentkit); + + agent = { + tools, + system, + model, + maxSteps: 10, + }; + + return agent; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/customEvm/viem/prepareAgentkit.ts b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/customEvm/viem/prepareAgentkit.ts new file mode 100644 index 000000000..cede3ead2 --- /dev/null +++ b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/customEvm/viem/prepareAgentkit.ts @@ -0,0 +1,129 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + erc20ActionProvider, + pythActionProvider, + ViemWalletProvider, + walletActionProvider, + WalletProvider, + wethActionProvider, +} from "@coinbase/agentkit"; +import fs from "fs"; +import { createWalletClient, http } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; + +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the core capabilities of your agent through WalletProvider + * and ActionProvider configuration. + * + * Key Components: + * 1. WalletProvider Setup: + * - Configures the blockchain wallet integration + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 2. ActionProviders Setup: + * - Defines the specific actions your agent can perform + * - Choose from built-in providers or create custom ones: + * - Built-in: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers + * - Custom: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Experiment with different LLM configurations + * - Fine-tune agent parameters for your use case + * + * ## Want to contribute? + * Join us in shaping AgentKit! Check out the contribution guide: + * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md + * - https://discord.gg/CDP + */ + +// Configure a file to persist a user's private key if none provided +const WALLET_DATA_FILE = "wallet_data.txt"; + +/** + * Prepares the AgentKit and WalletProvider. + * + * @function prepareAgentkitAndWalletProvider + * @returns {Promise<{ agentkit: AgentKit, walletProvider: WalletProvider }>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function prepareAgentkitAndWalletProvider(): Promise<{ + agentkit: AgentKit; + walletProvider: WalletProvider; +}> { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + let privateKey = process.env.PRIVATE_KEY as `0x${string}`; + if (!privateKey) { + if (fs.existsSync(WALLET_DATA_FILE)) { + privateKey = JSON.parse(fs.readFileSync(WALLET_DATA_FILE, "utf8")).privateKey; + console.info("Found private key in wallet_data.txt"); + } else { + privateKey = generatePrivateKey(); + fs.writeFileSync(WALLET_DATA_FILE, JSON.stringify({ privateKey })); + console.log("Created new private key and saved to wallet_data.txt"); + console.log( + "We recommend you save this private key to your .env file and delete wallet_data.txt afterwards.", + ); + } + } + const account = privateKeyToAccount(privateKey); + + const rpcUrl = process.env.RPC_URL as string; + const chainId = process.env.CHAIN_ID as string; + const client = createWalletClient({ + account, + // Customize the chain metadata to match your custom chain + chain: { + id: parseInt(chainId), + rpcUrls: { + default: { + http: [rpcUrl], + }, + }, + name: "Custom Chain", + nativeCurrency: { + name: "Ether", + symbol: "ETH", + decimals: 18, + }, + }, + transport: http(), + }); + const walletProvider = new ViemWalletProvider(client); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + return { agentkit, walletProvider }; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/cdp/prepareAgentkit.ts b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/cdp/prepareAgentkit.ts new file mode 100644 index 000000000..b0fca2bce --- /dev/null +++ b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/cdp/prepareAgentkit.ts @@ -0,0 +1,109 @@ +import { + AgentKit, + cdpApiActionProvider, + cdpWalletActionProvider, + CdpWalletProvider, + erc20ActionProvider, + pythActionProvider, + walletActionProvider, + WalletProvider, + wethActionProvider, +} from "@coinbase/agentkit"; +import * as fs from "fs"; + +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the core capabilities of your agent through WalletProvider + * and ActionProvider configuration. + * + * Key Components: + * 1. WalletProvider Setup: + * - Configures the blockchain wallet integration + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 2. ActionProviders Setup: + * - Defines the specific actions your agent can perform + * - Choose from built-in providers or create custom ones: + * - Built-in: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers + * - Custom: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Experiment with different LLM configurations + * - Fine-tune agent parameters for your use case + * + * ## Want to contribute? + * Join us in shaping AgentKit! Check out the contribution guide: + * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md + * - https://discord.gg/CDP + */ + +// Configure a file to persist the agent's CDP MPC Wallet Data +const WALLET_DATA_FILE = "wallet_data.txt"; + +/** + * Prepares the AgentKit and WalletProvider. + * + * @function prepareAgentkitAndWalletProvider + * @returns {Promise<{ agentkit: AgentKit, walletProvider: WalletProvider }>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function prepareAgentkitAndWalletProvider(): Promise<{ + agentkit: AgentKit; + walletProvider: WalletProvider; +}> { + try { + let walletDataStr: string | null = null; + + // Read existing wallet data if available + if (fs.existsSync(WALLET_DATA_FILE)) { + try { + walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8"); + } catch (error) { + console.error("Error reading wallet data:", error); + // Continue without wallet data + } + } + + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + const walletProvider = await CdpWalletProvider.configureWithWallet({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + networkId: process.env.NETWORK_ID || "base-sepolia", + cdpWalletData: walletDataStr || undefined, + }); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + cdpWalletActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ], + }); + + // Save wallet data + const exportedWallet = await walletProvider.exportWallet(); + fs.writeFileSync(WALLET_DATA_FILE, JSON.stringify(exportedWallet)); + + return { agentkit, walletProvider }; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/privy/prepareAgentkit.ts b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/privy/prepareAgentkit.ts new file mode 100644 index 000000000..aac839956 --- /dev/null +++ b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/privy/prepareAgentkit.ts @@ -0,0 +1,118 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + erc20ActionProvider, + PrivyWalletConfig, + PrivyWalletProvider, + pythActionProvider, + walletActionProvider, + WalletProvider, + wethActionProvider, +} from "@coinbase/agentkit"; +import fs from "fs"; + +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the core capabilities of your agent through WalletProvider + * and ActionProvider configuration. + * + * Key Components: + * 1. WalletProvider Setup: + * - Configures the blockchain wallet integration + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 2. ActionProviders Setup: + * - Defines the specific actions your agent can perform + * - Choose from built-in providers or create custom ones: + * - Built-in: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers + * - Custom: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Experiment with different LLM configurations + * - Fine-tune agent parameters for your use case + * + * ## Want to contribute? + * Join us in shaping AgentKit! Check out the contribution guide: + * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md + * - https://discord.gg/CDP + */ + +// Configure a file to persist the agent's Privy Wallet Data +const WALLET_DATA_FILE = "wallet_data.txt"; + +/** + * Prepares the AgentKit and WalletProvider. + * + * @function prepareAgentkitAndWalletProvider + * @returns {Promise<{ agentkit: AgentKit, walletProvider: WalletProvider }>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function prepareAgentkitAndWalletProvider(): Promise<{ + agentkit: AgentKit; + walletProvider: WalletProvider; +}> { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + const config: PrivyWalletConfig = { + appId: process.env.PRIVY_APP_ID as string, + appSecret: process.env.PRIVY_APP_SECRET as string, + walletId: process.env.PRIVY_WALLET_ID as string, + chainId: process.env.CHAIN_ID, + authorizationPrivateKey: process.env.PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY, + authorizationKeyId: process.env.PRIVY_WALLET_AUTHORIZATION_KEY_ID, + }; + // Try to load saved wallet data + if (fs.existsSync(WALLET_DATA_FILE)) { + const savedWallet = JSON.parse(fs.readFileSync(WALLET_DATA_FILE, "utf8")); + config.walletId = savedWallet.walletId; + config.authorizationPrivateKey = savedWallet.authorizationPrivateKey; + + if (savedWallet.chainId) { + console.log("Found chainId in wallet_data.txt:", savedWallet.chainId); + config.chainId = savedWallet.chainId; + } + } + if (!config.chainId) { + console.log("Warning: CHAIN_ID not set, defaulting to 84532 (base-sepolia)"); + config.chainId = "84532"; + } + const walletProvider = await PrivyWalletProvider.configureWithWallet(config); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + // Save wallet data + const exportedWallet = walletProvider.exportWallet(); + fs.writeFileSync(WALLET_DATA_FILE, JSON.stringify(exportedWallet)); + + return { agentkit, walletProvider }; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/smart/prepareAgentkit.ts b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/smart/prepareAgentkit.ts new file mode 100644 index 000000000..e872ac6f8 --- /dev/null +++ b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/smart/prepareAgentkit.ts @@ -0,0 +1,135 @@ +import { + AgentKit, + cdpApiActionProvider, + cdpWalletActionProvider, + erc20ActionProvider, + pythActionProvider, + SmartWalletProvider, + walletActionProvider, + WalletProvider, + wethActionProvider, +} from "@coinbase/agentkit"; +import * as fs from "fs"; +import { Address, Hex } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; + +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the core capabilities of your agent through WalletProvider + * and ActionProvider configuration. + * + * Key Components: + * 1. WalletProvider Setup: + * - Configures the blockchain wallet integration + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 2. ActionProviders Setup: + * - Defines the specific actions your agent can perform + * - Choose from built-in providers or create custom ones: + * - Built-in: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers + * - Custom: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Experiment with different LLM configurations + * - Fine-tune agent parameters for your use case + * + * ## Want to contribute? + * Join us in shaping AgentKit! Check out the contribution guide: + * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md + * - https://discord.gg/CDP + */ + +// Configure a file to persist the agent's Smart Wallet + Private Key data +const WALLET_DATA_FILE = "wallet_data.txt"; + +type WalletData = { + privateKey: Hex; + smartWalletAddress: Address; +}; + +/** + * Prepares the AgentKit and WalletProvider. + * + * @function prepareAgentkitAndWalletProvider + * @returns {Promise<{ agentkit: AgentKit, walletProvider: WalletProvider }>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function prepareAgentkitAndWalletProvider(): Promise<{ + agentkit: AgentKit; + walletProvider: WalletProvider; +}> { + try { + let walletData: WalletData | null = null; + let privateKey: Hex | null = null; + + // Read existing wallet data if available + if (fs.existsSync(WALLET_DATA_FILE)) { + try { + walletData = JSON.parse(fs.readFileSync(WALLET_DATA_FILE, "utf8")) as WalletData; + privateKey = walletData.privateKey; + } catch (error) { + console.error("Error reading wallet data:", error); + // Continue without wallet data + } + } + + if (!privateKey) { + if (walletData?.smartWalletAddress) { + throw new Error( + `Smart wallet found but no private key provided. Either provide the private key, or delete ${WALLET_DATA_FILE} and try again.`, + ); + } + privateKey = (process.env.PRIVATE_KEY || generatePrivateKey()) as Hex; + } + + const signer = privateKeyToAccount(privateKey); + + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + const walletProvider = await SmartWalletProvider.configureWithWallet({ + networkId: process.env.NETWORK_ID || "base-sepolia", + signer, + smartWalletAddress: walletData?.smartWalletAddress, + paymasterUrl: undefined, // Sponsor transactions: https://docs.cdp.coinbase.com/paymaster/docs/welcome + }); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + cdpWalletActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ], + }); + + // Save wallet data + const smartWalletAddress = await walletProvider.getAddress(); + fs.writeFileSync( + WALLET_DATA_FILE, + JSON.stringify({ + privateKey, + smartWalletAddress, + } as WalletData), + ); + + return { agentkit, walletProvider }; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/viem/prepareAgentkit.ts b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/viem/prepareAgentkit.ts new file mode 100644 index 000000000..83a3af09c --- /dev/null +++ b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/evm/viem/prepareAgentkit.ts @@ -0,0 +1,116 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + erc20ActionProvider, + NETWORK_ID_TO_VIEM_CHAIN, + pythActionProvider, + ViemWalletProvider, + walletActionProvider, + WalletProvider, + wethActionProvider, +} from "@coinbase/agentkit"; +import fs from "fs"; +import { createWalletClient, Hex, http } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; + +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the core capabilities of your agent through WalletProvider + * and ActionProvider configuration. + * + * Key Components: + * 1. WalletProvider Setup: + * - Configures the blockchain wallet integration + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 2. ActionProviders Setup: + * - Defines the specific actions your agent can perform + * - Choose from built-in providers or create custom ones: + * - Built-in: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers + * - Custom: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Experiment with different LLM configurations + * - Fine-tune agent parameters for your use case + * + * ## Want to contribute? + * Join us in shaping AgentKit! Check out the contribution guide: + * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md + * - https://discord.gg/CDP + */ + +// Configure a file to persist a user's private key if none provided +const WALLET_DATA_FILE = "wallet_data.txt"; + +/** + * Prepares the AgentKit and WalletProvider. + * + * @function prepareAgentkitAndWalletProvider + * @returns {Promise<{ agentkit: AgentKit, walletProvider: WalletProvider }>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function prepareAgentkitAndWalletProvider(): Promise<{ + agentkit: AgentKit; + walletProvider: WalletProvider; +}> { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + let privateKey = process.env.PRIVATE_KEY as Hex; + if (!privateKey) { + if (fs.existsSync(WALLET_DATA_FILE)) { + privateKey = JSON.parse(fs.readFileSync(WALLET_DATA_FILE, "utf8")).privateKey; + console.info("Found private key in wallet_data.txt"); + } else { + privateKey = generatePrivateKey(); + fs.writeFileSync(WALLET_DATA_FILE, JSON.stringify({ privateKey })); + console.log("Created new private key and saved to wallet_data.txt"); + console.log( + "We recommend you save this private key to your .env file and delete wallet_data.txt afterwards.", + ); + } + } + + const account = privateKeyToAccount(privateKey); + const networkId = process.env.NETWORK_ID as string; + + const client = createWalletClient({ + account, + chain: NETWORK_ID_TO_VIEM_CHAIN[networkId], + transport: http(), + }); + const walletProvider = new ViemWalletProvider(client); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + return { agentkit, walletProvider }; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/svm/privy/prepareAgentkit.ts b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/svm/privy/prepareAgentkit.ts new file mode 100644 index 000000000..eb304fbac --- /dev/null +++ b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/svm/privy/prepareAgentkit.ts @@ -0,0 +1,109 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + jupiterActionProvider, + PrivyWalletConfig, + PrivyWalletProvider, + splActionProvider, + walletActionProvider, + WalletProvider, +} from "@coinbase/agentkit"; +import fs from "fs"; + +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the core capabilities of your agent through WalletProvider + * and ActionProvider configuration. + * + * Key Components: + * 1. WalletProvider Setup: + * - Configures the blockchain wallet integration + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 2. ActionProviders Setup: + * - Defines the specific actions your agent can perform + * - Choose from built-in providers or create custom ones: + * - Built-in: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers + * - Custom: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Experiment with different LLM configurations + * - Fine-tune agent parameters for your use case + * + * ## Want to contribute? + * Join us in shaping AgentKit! Check out the contribution guide: + * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md + * - https://discord.gg/CDP + */ + +// Configure a file to persist the agent's Prviy Wallet Data +const WALLET_DATA_FILE = "wallet_data.txt"; + +/** + * Prepares the AgentKit and WalletProvider. + * + * @function prepareAgentkitAndWalletProvider + * @returns {Promise<{ agentkit: AgentKit, walletProvider: WalletProvider }>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function prepareAgentkitAndWalletProvider(): Promise<{ + agentkit: AgentKit; + walletProvider: WalletProvider; +}> { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + const config: PrivyWalletConfig = { + appId: process.env.PRIVY_APP_ID as string, + appSecret: process.env.PRIVY_APP_SECRET as string, + walletId: process.env.PRIVY_WALLET_ID as string, + authorizationPrivateKey: process.env.PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY, + authorizationKeyId: process.env.PRIVY_WALLET_AUTHORIZATION_KEY_ID, + chainType: "solana", + networkId: process.env.NETWORK_ID, + }; + // Try to load saved wallet data + if (fs.existsSync(WALLET_DATA_FILE)) { + const savedWallet = JSON.parse(fs.readFileSync(WALLET_DATA_FILE, "utf8")); + config.walletId = savedWallet.walletId; + config.authorizationPrivateKey = savedWallet.authorizationPrivateKey; + config.networkId = savedWallet.networkId; + } + const walletProvider = await PrivyWalletProvider.configureWithWallet(config); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + walletActionProvider(), + splActionProvider(), + jupiterActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + // Save wallet data + const exportedWallet = walletProvider.exportWallet(); + fs.writeFileSync(WALLET_DATA_FILE, JSON.stringify(exportedWallet)); + + return { agentkit, walletProvider }; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/svm/solanaKeypair/prepareAgentkit.ts b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/svm/solanaKeypair/prepareAgentkit.ts new file mode 100644 index 000000000..05f12f62e --- /dev/null +++ b/typescript/create-onchain-agent/templates/prepareAgentkit/agentkit/svm/solanaKeypair/prepareAgentkit.ts @@ -0,0 +1,116 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + jupiterActionProvider, + SOLANA_NETWORK_ID, + SolanaKeypairWalletProvider, + splActionProvider, + walletActionProvider, + WalletProvider, +} from "@coinbase/agentkit"; +import { Keypair } from "@solana/web3.js"; +import bs58 from "bs58"; +import fs from "fs"; + +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the core capabilities of your agent through WalletProvider + * and ActionProvider configuration. + * + * Key Components: + * 1. WalletProvider Setup: + * - Configures the blockchain wallet integration + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 2. ActionProviders Setup: + * - Defines the specific actions your agent can perform + * - Choose from built-in providers or create custom ones: + * - Built-in: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers + * - Custom: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Experiment with different LLM configurations + * - Fine-tune agent parameters for your use case + * + * ## Want to contribute? + * Join us in shaping AgentKit! Check out the contribution guide: + * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md + * - https://discord.gg/CDP + */ + +// Configure a file to persist a user's private key if none provided +const WALLET_DATA_FILE = "wallet_data.txt"; + +/** + * Prepares the AgentKit and WalletProvider. + * + * @function prepareAgentkitAndWalletProvider + * @returns {Promise<{ agentkit: AgentKit, walletProvider: WalletProvider }>} The initialized AI agent. + * + * @description Handles agent setup + * + * @throws {Error} If the agent initialization fails. + */ +export async function prepareAgentkitAndWalletProvider(): Promise<{ + agentkit: AgentKit; + walletProvider: WalletProvider; +}> { + try { + // Setup Private Key + let privateKey = process.env.SOLANA_PRIVATE_KEY as string; + if (!privateKey) { + if (fs.existsSync(WALLET_DATA_FILE)) { + privateKey = JSON.parse(fs.readFileSync(WALLET_DATA_FILE, "utf8")).privateKey; + console.info("Found private key in wallet_data.txt"); + } else { + const keypair = Keypair.generate(); + privateKey = bs58.encode(keypair.secretKey); + fs.writeFileSync(WALLET_DATA_FILE, JSON.stringify({ privateKey })); + console.log("Created new private key and saved to wallet_data.txt"); + console.log( + "We recommend you save this private key to your .env file and delete wallet_data.txt afterwards.", + ); + } + } + + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + // Configure Solana Keypair Wallet Provider + const rpcUrl = process.env.SOLANA_RPC_URL; + let walletProvider: SolanaKeypairWalletProvider; + if (rpcUrl) { + walletProvider = await SolanaKeypairWalletProvider.fromRpcUrl(rpcUrl, privateKey); + } else { + const network = (process.env.NETWORK_ID ?? "solana-devnet") as SOLANA_NETWORK_ID; + walletProvider = await SolanaKeypairWalletProvider.fromNetwork(network, privateKey); + } + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + walletActionProvider(), + splActionProvider(), + jupiterActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + return { agentkit, walletProvider }; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/walletProvider/walletProvider.njk b/typescript/create-onchain-agent/templates/walletProvider/walletProvider.njk new file mode 100644 index 000000000..733b67cb9 --- /dev/null +++ b/typescript/create-onchain-agent/templates/walletProvider/walletProvider.njk @@ -0,0 +1,197 @@ +import { {{ baseClass }}, Network } from "@coinbase/agentkit"; +{% if protocolFamily === "EVM" %} +import { + TransactionRequest, + ReadContractParameters, + ReadContractReturnType, + Abi, + ContractFunctionName, + ContractFunctionArgs, + Hex, +} from "viem"; +{% else %} +import { + Connection, + PublicKey, + RpcResponseAndContext, + SignatureStatus, + SignatureStatusConfig, + VersionedTransaction, + SignatureResult, +} from "@solana/web3.js"; +{% endif %} + +/** + * {{ className }} wallet provider. + */ +export class {{ className }} extends {{ baseClass }} { +{% if protocolFamily === "EVM" %} + /** + * Get the address of the wallet. + */ + getAddress(): string { + throw new Error("Method not implemented."); + } + + /** + * Get the current network. + */ + getNetwork(): Network { + throw new Error("Method not implemented."); + } + + /** + * Get the name of the wallet provider. + */ + getName(): string { + return "{{ name }}"; + } + + /** + * Get the balance of the native asset. + */ + async getBalance(): Promise { + throw new Error("Method not implemented."); + } + + /** + * Transfer the native asset. + */ + async nativeTransfer(to: string, value: string): Promise { + throw new Error("Method not implemented."); + } + + /** + * Sign a message. + */ + async signMessage(message: string | Uint8Array): Promise { + throw new Error("Method not implemented."); + } + + /** + * Sign typed data. + */ + async signTypedData(typedData: any): Promise { + throw new Error("Method not implemented."); + } + + /** + * Sign a transaction. + */ + async signTransaction(transaction: TransactionRequest): Promise { + throw new Error("Method not implemented."); + } + + /** + * Send a transaction. + */ + async sendTransaction(transaction: TransactionRequest): Promise { + throw new Error("Method not implemented."); + } + + /** + * Wait for a transaction receipt. + */ + async waitForTransactionReceipt(txHash: Hex): Promise { + throw new Error("Method not implemented."); + } + + /** + * Read a contract. + */ + async readContract< + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + const args extends ContractFunctionArgs, + >(params: ReadContractParameters): Promise> { + throw new Error("Method not implemented."); + } +{% else %} + /** + * Get the address of the wallet. + */ + getAddress(): string { + throw new Error("Method not implemented."); + } + + /** + * Get the current network. + */ + getNetwork(): Network { + throw new Error("Method not implemented."); + } + + /** + * Get the name of the wallet provider. + */ + getName(): string { + return "{{ name }}"; + } + + /** + * Get the balance of the native asset. + */ + async getBalance(): Promise { + throw new Error("Method not implemented."); + } + + /** + * Transfer the native asset. + */ + async nativeTransfer(to: string, value: string): Promise { + throw new Error("Method not implemented."); + } + + /** + * Get the connection instance. + */ + getConnection(): Connection { + throw new Error("Method not implemented."); + } + + /** + * Get the public key of the wallet. + */ + getPublicKey(): PublicKey { + throw new Error("Method not implemented."); + } + + /** + * Sign a transaction. + */ + async signTransaction(transaction: VersionedTransaction): Promise { + throw new Error("Method not implemented."); + } + + /** + * Send a transaction. + */ + async sendTransaction(transaction: VersionedTransaction): Promise { + throw new Error("Method not implemented."); + } + + /** + * Sign and send a transaction. + */ + async signAndSendTransaction(transaction: VersionedTransaction): Promise { + throw new Error("Method not implemented."); + } + + /** + * Get the status of a transaction. + */ + async getSignatureStatus( + signature: string, + options?: SignatureStatusConfig + ): Promise> { + throw new Error("Method not implemented."); + } + + /** + * Wait for signature receipt. + */ + async waitForSignatureResult(signature: string): Promise> { + throw new Error("Method not implemented."); + } +{% endif %} +} diff --git a/typescript/package-lock.json b/typescript/package-lock.json index 64f29e5df..708654bf5 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -254,14 +254,17 @@ "version": "0.2.0", "license": "Apache-2.0", "dependencies": { + "@types/nunjucks": "^3.2.6", "cac": "^6.7.14", "cross-spawn": "^7.0.3", + "nunjucks": "^3.2.4", "ora": "^8.1.0", "picocolors": "^1.1.0", "prompts": "^2.4.2" }, "bin": { - "create-onchain-agent": "dist/esm/cli.js" + "agentkit": "dist/esm/generate.js", + "create-onchain-agent": "dist/esm/create.js" }, "devDependencies": { "@types/cross-spawn": "^6.0.6", @@ -4854,9 +4857,7 @@ "node_modules/@types/nunjucks": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.2.6.tgz", - "integrity": "sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==", - "dev": true, - "license": "MIT" + "integrity": "sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==" }, "node_modules/@types/ora": { "version": "3.2.0", @@ -5894,7 +5895,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==", - "dev": true, "license": "MIT" }, "node_modules/abitype": { @@ -6340,7 +6340,6 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, "license": "MIT" }, "node_modules/ast-types-flow": { @@ -6682,7 +6681,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -6802,7 +6801,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "devOptional": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -7198,7 +7197,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -7223,7 +7222,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -9899,7 +9898,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, + "devOptional": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -11017,7 +11016,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -11148,7 +11147,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -11206,7 +11205,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -11252,7 +11251,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.12.0" } @@ -14189,8 +14188,6 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", - "dev": true, - "license": "BSD-2-Clause", "dependencies": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", @@ -14215,7 +14212,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -16052,7 +16048,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -17786,7 +17782,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "devOptional": true, "dependencies": { "is-number": "^7.0.0" }, @@ -22653,8 +22649,7 @@ "@types/nunjucks": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.2.6.tgz", - "integrity": "sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==", - "dev": true + "integrity": "sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==" }, "@types/ora": { "version": "3.2.0", @@ -23337,8 +23332,7 @@ "a-sync-waterfall": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", - "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==", - "dev": true + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" }, "abitype": { "version": "1.0.8", @@ -23632,8 +23626,7 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "ast-types-flow": { "version": "0.0.8", @@ -23879,7 +23872,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true + "devOptional": true }, "bindings": { "version": "1.5.0", @@ -23978,7 +23971,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "devOptional": true, "requires": { "fill-range": "^7.1.1" } @@ -24228,7 +24221,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, + "devOptional": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -24244,7 +24237,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "devOptional": true, "requires": { "is-glob": "^4.0.1" } @@ -24452,9 +24445,11 @@ "requires": { "@types/cross-spawn": "^6.0.6", "@types/node": "^20.12.10", + "@types/nunjucks": "^3.2.6", "@types/prompts": "^2.4.9", "cac": "^6.7.14", "cross-spawn": "^7.0.3", + "nunjucks": "^3.2.4", "ora": "^8.1.0", "picocolors": "^1.1.0", "prompts": "^2.4.2" @@ -26201,7 +26196,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, + "devOptional": true, "requires": { "to-regex-range": "^5.0.1" } @@ -26958,7 +26953,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, + "devOptional": true, "requires": { "binary-extensions": "^2.0.0" } @@ -27031,7 +27026,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "devOptional": true }, "is-finalizationregistry": { "version": "1.1.1", @@ -27068,7 +27063,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "requires": { "is-extglob": "^2.1.1" } @@ -27093,7 +27088,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "devOptional": true }, "is-number-object": { "version": "1.1.1", @@ -29098,7 +29093,6 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", - "dev": true, "requires": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", @@ -29108,8 +29102,7 @@ "commander": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" } } }, @@ -30360,7 +30353,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, + "devOptional": true, "requires": { "picomatch": "^2.2.1" } @@ -31586,7 +31579,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "devOptional": true, "requires": { "is-number": "^7.0.0" }