From 00c941c913905918c82e87343817a107649cb9b7 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Tue, 18 Feb 2025 10:04:42 -0800 Subject: [PATCH 01/49] Created base create-onchain-agent package --- .../create-onchain-agent/package-lock.json | 417 ++++++++++++++++++ typescript/create-onchain-agent/package.json | 41 ++ typescript/create-onchain-agent/src/cli.ts | 182 ++++++++ typescript/create-onchain-agent/src/utils.ts | 70 +++ .../create-onchain-agent/tsconfig.build.json | 8 + typescript/create-onchain-agent/tsconfig.json | 18 + 6 files changed, 736 insertions(+) create mode 100644 typescript/create-onchain-agent/package-lock.json create mode 100644 typescript/create-onchain-agent/package.json create mode 100644 typescript/create-onchain-agent/src/cli.ts create mode 100644 typescript/create-onchain-agent/src/utils.ts create mode 100644 typescript/create-onchain-agent/tsconfig.build.json create mode 100644 typescript/create-onchain-agent/tsconfig.json diff --git a/typescript/create-onchain-agent/package-lock.json b/typescript/create-onchain-agent/package-lock.json new file mode 100644 index 000000000..274df4b54 --- /dev/null +++ b/typescript/create-onchain-agent/package-lock.json @@ -0,0 +1,417 @@ +{ + "name": "create-onchain-agent", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "create-onchain-agent", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "cross-spawn": "^7.0.3", + "ora": "^8.1.0", + "picocolors": "^1.1.0", + "prompts": "^2.4.2" + }, + "bin": { + "create-onchain": "dist/esm/cli.js" + }, + "devDependencies": { + "@types/cross-spawn": "^6.0.6", + "@types/node": "^20.12.10", + "@types/prompts": "^2.4.9" + } + }, + "node_modules/@types/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", + "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/prompts": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz", + "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "kleur": "^3.0.3" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "license": "MIT" + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + } + } +} diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json new file mode 100644 index 000000000..8d6805d8a --- /dev/null +++ b/typescript/create-onchain-agent/package.json @@ -0,0 +1,41 @@ +{ + "name": "create-onchain-agent", + "description": "Instantly create onchain-agent applications with AgentKit.", + "version": "0.0.1", + "license": "MIT", + "scripts": { + "build": "bun run clean && bun run build:esm+types", + "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", + "check:types": "tsc --noEmit", + "clean": "rm -rf dist tsconfig.tsbuildinfo", + "dev": "bun src/cli.ts" + }, + "files": [ + "dist/**", + "!dist/**/*.tsbuildinfo", + "src/**/*.ts", + "!src/**/*.test.ts", + "!src/**/*.test-d.ts", + "templates/**" + ], + "bin": { + "create-onchain": "./dist/esm/cli.js" + }, + "sideEffects": false, + "type": "module", + "exports": { + "./package.json": "./package.json" + }, + "dependencies": { + "cac": "^6.7.14", + "cross-spawn": "^7.0.3", + "ora": "^8.1.0", + "picocolors": "^1.1.0", + "prompts": "^2.4.2" + }, + "devDependencies": { + "@types/cross-spawn": "^6.0.6", + "@types/node": "^20.12.10", + "@types/prompts": "^2.4.9" + } +} \ No newline at end of file diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts new file mode 100644 index 000000000..970c587f0 --- /dev/null +++ b/typescript/create-onchain-agent/src/cli.ts @@ -0,0 +1,182 @@ +#!/usr/bin/env node +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import prompts from "prompts"; +import pc from "picocolors"; +import ora from "ora"; +import { + createClickableLink, + detectPackageManager, + isValidPackageName, + toValidPackageName, + optimizedCopy, +} from "./utils.js"; + +const sourceDir = path.resolve( + fileURLToPath(import.meta.url), + "../../../templates/agent" +); + +const renameFiles: Record = { + _gitignore: ".gitignore", + "_env.local": ".env.local", +}; + +const excludeDirs = ["node_modules", ".next"]; +const excludeFiles = [".DS_Store", "Thumbs.db"]; + +async function copyDir(src: string, dest: string) { + await fs.promises.mkdir(dest, { recursive: true }); + const entries = await fs.promises.readdir(src, { withFileTypes: true }); + + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, renameFiles[entry.name] || entry.name); + + if (entry.isDirectory()) { + if (!excludeDirs.includes(entry.name)) { + await copyDir(srcPath, destPath); + } + } else { + if (!excludeFiles.includes(entry.name)) { + await optimizedCopy(srcPath, destPath); + } + } + } +} + +async function init() { + console.log( + `${pc.greenBright(` + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // :::::::: :::: ::: :::::::: ::: ::: ::: ::::::::::: :::: ::: ::::::::::: // + // :+: :+: :+:+: :+: :+: :+: :+: :+: :+: :+: :+: :+:+: :+: :+: // + // +:+ +:+ :+:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ :+:+:+ +:+ +:+ // + // +#+ +:+ +#+ +:+ +#+ +#+ +#++:++#++ +#++:++#++: +#+ +#+ +:+ +#+ +#++:++ // + // +#+ +#+ +#+ +#+#+# +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+# +#+ // + // #+# #+# #+# #+#+# #+# #+# #+# #+# #+# #+# #+# #+# #+#+# #+# // + // ######## ### #### ######## ### ### ### ### ########### ### #### ########### // + // // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////`)}\n\n` + ); + + const defaultProjectName = "my-onchain-agent-app"; + + let result: prompts.Answers<"projectName" | "packageName" | "clientKey" | "smartWallet">; + + try { + result = await prompts( + [ + { + type: "text", + name: "projectName", + message: pc.reset("Project name:"), + initial: defaultProjectName, + onState: (state) => { + state.value = state.value.trim(); + }, + validate: (value) => { + const targetDir = path.join(process.cwd(), value); + if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) { + return "Directory already exists and is not empty. Please choose a different name."; + } + return true; + }, + }, + { + type: (_, { projectName }: { projectName: string }) => + isValidPackageName(projectName) ? null : "text", + name: "packageName", + message: pc.reset("Package name:"), + initial: (_, { projectName }: { projectName: string }) => + toValidPackageName(projectName), + validate: (dir) => + isValidPackageName(dir) || "Invalid package.json name", + }, + { + type: "password", + name: "clientKey", + message: pc.reset( + `Enter your ${createClickableLink( + "Coinbase Developer Platform Client API Key:", + "https://portal.cdp.coinbase.com/products/onchain-agent" + )} (optional)` + ), + }, + { + type: "toggle", + name: "smartWallet", + message: pc.reset("Use Coinbase Smart Wallet? (recommended)"), + initial: true, + active: "yes", + inactive: "no", + }, + ], + { + onCancel: () => { + console.log("\nProject creation cancelled."); + process.exit(0); + }, + } + ); + } catch (cancelled: any) { + console.log(cancelled.message); + process.exit(1); + } + + const { projectName, packageName, clientKey, smartWallet } = result; + const root = path.join(process.cwd(), projectName); + + const spinner = ora(`Creating ${projectName}...`).start(); + + await copyDir(sourceDir, root); + + const pkgPath = path.join(root, "package.json"); + const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8")); + pkg.name = packageName || toValidPackageName(projectName); + await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); + + // Create .env file + const envPath = path.join(root, ".env"); + await fs.promises.writeFile( + envPath, + `NEXT_PUBLIC_AGENTKIT_PROJECT_NAME=${projectName}\nNEXT_PUBLIC_AGENTKIT_API_KEY=${clientKey}\nNEXT_PUBLIC_AGENTKIT_WALLET_CONFIG=${ + smartWallet ? "smartWalletOnly" : "all" + }` + ); + + spinner.succeed(); + console.log(`\n${pc.magenta(`Created new AgentKit project in ${root}`)}`); + + console.log(`\nIntegrations:`); + if (smartWallet) { + console.log(`${pc.greenBright("\u2713")} ${pc.blueBright(`Smart Wallet`)}`); + } + console.log(`${pc.greenBright("\u2713")} ${pc.blueBright(`Base`)}`); + if (clientKey) { + console.log(`${pc.greenBright("\u2713")} ${pc.blueBright(`Coinbase Developer Platform`)}`); + console.log(`${pc.greenBright("\u2713")} ${pc.blueBright(`Paymaster`)}`); + } + + console.log(`\nFrameworks:`); + console.log(`${pc.cyan("- Wagmi")}`); + console.log(`${pc.cyan("- React")}`); + console.log(`${pc.cyan("- Next.js")}`); + console.log(`${pc.cyan("- Tailwind CSS")}`); + console.log(`${pc.cyan("- ESLint")}`); + + console.log( + `\nTo get started with ${pc.green(projectName)}, run the following commands:\n` + ); + if (root !== process.cwd()) { + console.log(` - cd ${path.relative(process.cwd(), root)}`); + } + console.log(" - npm install"); + console.log(" - npm run dev"); +} + +init().catch((e) => { + console.error(e); +}); diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts new file mode 100644 index 000000000..41fe03699 --- /dev/null +++ b/typescript/create-onchain-agent/src/utils.ts @@ -0,0 +1,70 @@ +import fs from 'fs/promises'; +import path from 'path'; + +async function copyFile(src: string, dest: string) { + await fs.copyFile(src, dest); +} + +async function copyDir(src: string, dest: string) { + await fs.mkdir(dest, { recursive: true }); + const entries = await fs.readdir(src, { withFileTypes: true }); + + await Promise.all( + entries.map(async (entry) => { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + if (entry.isDirectory()) { + await copyDir(srcPath, destPath); + } else { + await copyFile(srcPath, destPath); + } + }) + ); +} + +export async function optimizedCopy(src: string, dest: string) { + const stat = await fs.stat(src); + if (stat.isDirectory()) { + await copyDir(src, dest); + } else { + await copyFile(src, dest); + } +} + +export function createClickableLink(text: string, url: string): string { + // OSC 8 ;; URL \a TEXT \a + return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`; +} + +export function isValidPackageName(projectName: string) { + return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test( + projectName + ); +} + +export function toValidPackageName(projectName: string) { + return projectName + .trim() + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/^[._]/, '') + .replace(/[^a-z\d\-~]+/g, '-'); +} + +export function detectPackageManager(): string { + if (process.env.npm_config_user_agent) { + if (process.env.npm_config_user_agent.startsWith('yarn')) { + return 'yarn'; + } + if (process.env.npm_config_user_agent.startsWith('pnpm')) { + return 'pnpm'; + } + if (process.env.npm_config_user_agent.startsWith('npm')) { + return 'npm'; + } + if (process.env.npm_config_user_agent.startsWith('bun')) { + return 'bun'; + } + } + return 'npm'; // default to npm if unable to detect +} diff --git a/typescript/create-onchain-agent/tsconfig.build.json b/typescript/create-onchain-agent/tsconfig.build.json new file mode 100644 index 000000000..2b6ebc81d --- /dev/null +++ b/typescript/create-onchain-agent/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"], + "compilerOptions": { + "sourceMap": true + } + } \ No newline at end of file diff --git a/typescript/create-onchain-agent/tsconfig.json b/typescript/create-onchain-agent/tsconfig.json new file mode 100644 index 000000000..ce15a04ea --- /dev/null +++ b/typescript/create-onchain-agent/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist/esm", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "declarationDir": "./dist/types" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts", "**/*.test-d.ts"] + } \ No newline at end of file From 91ee6d5de1f18a16c84df715039df5f734d6ba94 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Tue, 18 Feb 2025 10:10:32 -0800 Subject: [PATCH 02/49] Added base templates/next template --- .../templates/next/.eslintrc.json | 3 + .../templates/next/.gitignore | 37 +++++++++++ .../templates/next/.yarnrc.yml | 2 + .../templates/next/README.md | 39 ++++++++++++ .../templates/next/app/globals.css | 8 +++ .../templates/next/app/layout.tsx | 50 +++++++++++++++ .../templates/next/app/page.tsx | 61 +++++++++++++++++++ .../templates/next/next.config.js | 6 ++ .../templates/next/package.json | 30 +++++++++ .../templates/next/postcss.config.mjs | 8 +++ .../templates/next/tailwind.config.ts | 19 ++++++ .../templates/next/tsconfig.json | 26 ++++++++ 12 files changed, 289 insertions(+) create mode 100644 typescript/create-onchain-agent/templates/next/.eslintrc.json create mode 100644 typescript/create-onchain-agent/templates/next/.gitignore create mode 100644 typescript/create-onchain-agent/templates/next/.yarnrc.yml create mode 100644 typescript/create-onchain-agent/templates/next/README.md create mode 100644 typescript/create-onchain-agent/templates/next/app/globals.css create mode 100644 typescript/create-onchain-agent/templates/next/app/layout.tsx create mode 100644 typescript/create-onchain-agent/templates/next/app/page.tsx create mode 100644 typescript/create-onchain-agent/templates/next/next.config.js create mode 100644 typescript/create-onchain-agent/templates/next/package.json create mode 100644 typescript/create-onchain-agent/templates/next/postcss.config.mjs create mode 100644 typescript/create-onchain-agent/templates/next/tailwind.config.ts create mode 100644 typescript/create-onchain-agent/templates/next/tsconfig.json diff --git a/typescript/create-onchain-agent/templates/next/.eslintrc.json b/typescript/create-onchain-agent/templates/next/.eslintrc.json new file mode 100644 index 000000000..372241854 --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["next/core-web-vitals", "next/typescript"] +} diff --git a/typescript/create-onchain-agent/templates/next/.gitignore b/typescript/create-onchain-agent/templates/next/.gitignore new file mode 100644 index 000000000..ba6ecc8ce --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/.gitignore @@ -0,0 +1,37 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz +package-lock.json + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/typescript/create-onchain-agent/templates/next/.yarnrc.yml b/typescript/create-onchain-agent/templates/next/.yarnrc.yml new file mode 100644 index 000000000..14fd359c8 --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/.yarnrc.yml @@ -0,0 +1,2 @@ +# You can remove this file if you don't want to use Yarn package manager. +nodeLinker: node-modules \ No newline at end of file diff --git a/typescript/create-onchain-agent/templates/next/README.md b/typescript/create-onchain-agent/templates/next/README.md new file mode 100644 index 000000000..5684afeaf --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/README.md @@ -0,0 +1,39 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-onchain-agent`](). + + +## Getting Started + +First, install dependencies: + +```bash +npm install +# or +yarn install +# or +pnpm install +# or +bun install +``` + +Next, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + + +## Learn More + +To learn more about AgentKit, see our [documentation](https://docs.cdp.coinbase.com/agentkit/docs/welcome). + +To learn more about Next.js, see the [Next.js documentation](https://nextjs.org/docs). diff --git a/typescript/create-onchain-agent/templates/next/app/globals.css b/typescript/create-onchain-agent/templates/next/app/globals.css new file mode 100644 index 000000000..62b934029 --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/globals.css @@ -0,0 +1,8 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + @apply bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100; + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; +} diff --git a/typescript/create-onchain-agent/templates/next/app/layout.tsx b/typescript/create-onchain-agent/templates/next/app/layout.tsx new file mode 100644 index 000000000..bf2ab2bfe --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/layout.tsx @@ -0,0 +1,50 @@ +import type { Metadata } from "next"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "AgentKit Quickstart", + description: "Generated by `create-onchain-agent`, a Next.js template for AgentKit", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {/* Header (Fixed Height) */} +
+ 🤖 AgentKit +
+ + {/* Main Content (Dynamic, Grows but Doesn't Force Scroll) */} +
+ {children} +
+ + {/* Footer (Fixed Height) */} + + + + ); +} diff --git a/typescript/create-onchain-agent/templates/next/app/page.tsx b/typescript/create-onchain-agent/templates/next/app/page.tsx new file mode 100644 index 000000000..e1efd626e --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/page.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { useState } from "react"; + +export default function Home() { + const [messages, setMessages] = useState<{ text: string; sender: "user" | "agent" }[]>([]); + const [input, setInput] = useState(""); + + const sendMessage = () => { + if (!input.trim()) return; + + setMessages([...messages, { text: input, sender: "user" }]); + setInput(""); + + // Mock agent response (replace with actual API call) + setTimeout(() => { + setMessages((prev) => [ + ...prev, + { text: `Agent Response to: "${input}"`, sender: "agent" }, + ]); + }, 1000); + }; + + return ( +
+ {/* Chat Container */} +
+ {/* Chat Messages */} +
+ {messages.length === 0 ? ( +

Start chatting with AgentKit...

+ ) : ( + messages.map((msg, index) => ( +
+ {msg.text} +
+ )) + )} +
+ + {/* Input Box */} +
+ setInput(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && sendMessage()} + /> + +
+
+
+ ); +} diff --git a/typescript/create-onchain-agent/templates/next/next.config.js b/typescript/create-onchain-agent/templates/next/next.config.js new file mode 100644 index 000000000..91ef62f0d --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/next.config.js @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +}; + +module.exports = nextConfig; diff --git a/typescript/create-onchain-agent/templates/next/package.json b/typescript/create-onchain-agent/templates/next/package.json new file mode 100644 index 000000000..54ee7fb16 --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/package.json @@ -0,0 +1,30 @@ +{ + "name": "next", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@coinbase/agentkit": "latest", + "next": "14.2.15", + "react": "^18", + "react-dom": "^18", + "@tanstack/react-query": "^5", + "viem": "^2.17.4", + "wagmi": "^2.11.0" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.15", + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" + } +} diff --git a/typescript/create-onchain-agent/templates/next/postcss.config.mjs b/typescript/create-onchain-agent/templates/next/postcss.config.mjs new file mode 100644 index 000000000..1a69fd2a4 --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/typescript/create-onchain-agent/templates/next/tailwind.config.ts b/typescript/create-onchain-agent/templates/next/tailwind.config.ts new file mode 100644 index 000000000..cb8ee88d2 --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/tailwind.config.ts @@ -0,0 +1,19 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + fontFamily: { + sans: ['DM Sans', 'sans-serif'], + }, + }, + }, + plugins: [] +}; + +export default config; diff --git a/typescript/create-onchain-agent/templates/next/tsconfig.json b/typescript/create-onchain-agent/templates/next/tsconfig.json new file mode 100644 index 000000000..e7ff90fd2 --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} From 4c67161ddb4a2bcdccadf50b9120a46857aa223d Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Tue, 18 Feb 2025 12:34:22 -0800 Subject: [PATCH 03/49] Improved CLI handling of wallet provider --- typescript/create-onchain-agent/src/cli.ts | 58 +++++-------------- .../src/walletProviders.ts | 29 ++++++++++ 2 files changed, 44 insertions(+), 43 deletions(-) create mode 100644 typescript/create-onchain-agent/src/walletProviders.ts diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 970c587f0..1db392804 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -12,6 +12,7 @@ import { toValidPackageName, optimizedCopy, } from "./utils.js"; +import { WalletOption, WalletOptions, WalletOptionsLookup } from "./walletProviders.js"; const sourceDir = path.resolve( fileURLToPath(import.meta.url), @@ -49,22 +50,17 @@ async function copyDir(src: string, dest: string) { async function init() { console.log( `${pc.greenBright(` - ////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // // - // :::::::: :::: ::: :::::::: ::: ::: ::: ::::::::::: :::: ::: ::::::::::: // - // :+: :+: :+:+: :+: :+: :+: :+: :+: :+: :+: :+: :+:+: :+: :+: // - // +:+ +:+ :+:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ :+:+:+ +:+ +:+ // - // +#+ +:+ +#+ +:+ +#+ +#+ +#++:++#++ +#++:++#++: +#+ +#+ +:+ +#+ +#++:++ // - // +#+ +#+ +#+ +#+#+# +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+# +#+ // - // #+# #+# #+# #+#+# #+# #+# #+# #+# #+# #+# #+# #+# #+#+# #+# // - // ######## ### #### ######## ### ### ### ### ########### ### #### ########### // - // // - //////////////////////////////////////////////////////////////////////////////////////////////////////////////`)}\n\n` + █████ ██████ ███████ ███ ██ ████████ ██ ██ ██ ████████ +██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ +███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +██ ██ ██████ ███████ ██ ████ ██ ██ ██ ██ ██ +`)}\n\n` ); const defaultProjectName = "my-onchain-agent-app"; - let result: prompts.Answers<"projectName" | "packageName" | "clientKey" | "smartWallet">; + let result: prompts.Answers<"projectName" | "packageName" | 'walletProvider'>; try { result = await prompts( @@ -96,22 +92,10 @@ async function init() { isValidPackageName(dir) || "Invalid package.json name", }, { - type: "password", - name: "clientKey", - message: pc.reset( - `Enter your ${createClickableLink( - "Coinbase Developer Platform Client API Key:", - "https://portal.cdp.coinbase.com/products/onchain-agent" - )} (optional)` - ), - }, - { - type: "toggle", - name: "smartWallet", - message: pc.reset("Use Coinbase Smart Wallet? (recommended)"), - initial: true, - active: "yes", - inactive: "no", + type: "select", + name: "walletProvider", + message: pc.reset("Choose a wallet provider:"), + choices: WalletOptions.map((option) => ({ title: option, value: option })), }, ], { @@ -126,7 +110,8 @@ async function init() { process.exit(1); } - const { projectName, packageName, clientKey, smartWallet } = result; + const { projectName, packageName, walletProvider } = result; + const walletProviderOptions = WalletOptionsLookup[walletProvider as WalletOption] const root = path.join(process.cwd(), projectName); const spinner = ora(`Creating ${projectName}...`).start(); @@ -142,26 +127,13 @@ async function init() { const envPath = path.join(root, ".env"); await fs.promises.writeFile( envPath, - `NEXT_PUBLIC_AGENTKIT_PROJECT_NAME=${projectName}\nNEXT_PUBLIC_AGENTKIT_API_KEY=${clientKey}\nNEXT_PUBLIC_AGENTKIT_WALLET_CONFIG=${ - smartWallet ? "smartWalletOnly" : "all" - }` + `NETWORK_ID=\nOPENAI_API_KEY=\n${walletProviderOptions.env.map(envVar => `${envVar}=`).join('=\n')}` ); spinner.succeed(); console.log(`\n${pc.magenta(`Created new AgentKit project in ${root}`)}`); - console.log(`\nIntegrations:`); - if (smartWallet) { - console.log(`${pc.greenBright("\u2713")} ${pc.blueBright(`Smart Wallet`)}`); - } - console.log(`${pc.greenBright("\u2713")} ${pc.blueBright(`Base`)}`); - if (clientKey) { - console.log(`${pc.greenBright("\u2713")} ${pc.blueBright(`Coinbase Developer Platform`)}`); - console.log(`${pc.greenBright("\u2713")} ${pc.blueBright(`Paymaster`)}`); - } - console.log(`\nFrameworks:`); - console.log(`${pc.cyan("- Wagmi")}`); console.log(`${pc.cyan("- React")}`); console.log(`${pc.cyan("- Next.js")}`); console.log(`${pc.cyan("- Tailwind CSS")}`); diff --git a/typescript/create-onchain-agent/src/walletProviders.ts b/typescript/create-onchain-agent/src/walletProviders.ts new file mode 100644 index 000000000..8b0da79ee --- /dev/null +++ b/typescript/create-onchain-agent/src/walletProviders.ts @@ -0,0 +1,29 @@ + + +export type WalletOption = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' + +export const WalletOptions: WalletOption[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] + +type WalletProviderConfiguration = { + env: string[]; + apiRoute: string +} + +export const WalletOptionsLookup: Record = { + CDP: { + env: ['CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], + apiRoute: 'agent/cdp/route.ts' + }, + Viem: { + env: ['PRIVATE_KEY'], + apiRoute: 'agent/viem/route.ts' + }, + Privy: { + env: [], + apiRoute: 'agent/privy/route.ts' + }, + SolanaKeypair: { + env: ['SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], + apiRoute: 'agent/solanaKeypair/route.ts' + }, +} \ No newline at end of file From a5239158b9584ad79dffb952f77523b17c0d4c00 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Tue, 18 Feb 2025 12:34:40 -0800 Subject: [PATCH 04/49] Improve responsiveness of UI --- .../templates/next/app/page.tsx | 64 +++++++++++++------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/typescript/create-onchain-agent/templates/next/app/page.tsx b/typescript/create-onchain-agent/templates/next/app/page.tsx index e1efd626e..3bc2eab24 100644 --- a/typescript/create-onchain-agent/templates/next/app/page.tsx +++ b/typescript/create-onchain-agent/templates/next/app/page.tsx @@ -1,29 +1,34 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect, useRef } from "react"; +import { useAgent } from "./hooks/useAgent"; export default function Home() { - const [messages, setMessages] = useState<{ text: string; sender: "user" | "agent" }[]>([]); const [input, setInput] = useState(""); + const { messages, sendMessage, isThinking } = useAgent(); - const sendMessage = () => { - if (!input.trim()) return; + // Ref for the messages container + const messagesEndRef = useRef(null); - setMessages([...messages, { text: input, sender: "user" }]); - setInput(""); + // Function to scroll to the bottom + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }; - // Mock agent response (replace with actual API call) - setTimeout(() => { - setMessages((prev) => [ - ...prev, - { text: `Agent Response to: "${input}"`, sender: "agent" }, - ]); - }, 1000); + // Auto-scroll whenever messages change + useEffect(() => { + scrollToBottom(); + }, [messages]); + + const onSendMessage = async () => { + if (!input.trim() || isThinking) return; + const message = input; + setInput(""); + await sendMessage(message); }; return (
- {/* Chat Container */}
{/* Chat Messages */}
@@ -31,11 +36,26 @@ export default function Home() {

Start chatting with AgentKit...

) : ( messages.map((msg, index) => ( -
+
{msg.text}
)) )} + + {/* Thinking Indicator */} + {isThinking && ( +
🤖 Thinking...
+ )} + + {/* Invisible div to track the bottom */} +
{/* Input Box */} @@ -43,14 +63,20 @@ export default function Home() { setInput(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && sendMessage()} + onKeyDown={(e) => e.key === "Enter" && onSendMessage()} + disabled={isThinking} /> From 76c720d50501fdbf8f4cc5364da043d0d3ce316e Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Tue, 18 Feb 2025 12:34:58 -0800 Subject: [PATCH 05/49] Finished working out what a agent route looks like --- .../templates/next/app/api/agent/route.ts | 81 +++++++++++++++++++ .../templates/next/app/hooks/useAgent.ts | 33 ++++++++ .../templates/next/app/types/api.ts | 4 + 3 files changed, 118 insertions(+) create mode 100644 typescript/create-onchain-agent/templates/next/app/api/agent/route.ts create mode 100644 typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts create mode 100644 typescript/create-onchain-agent/templates/next/app/types/api.ts diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/route.ts new file mode 100644 index 000000000..5c047451f --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/route.ts @@ -0,0 +1,81 @@ +import { NextResponse } from "next/server"; +import { AgentKit, cdpApiActionProvider, cdpWalletActionProvider, CdpWalletProvider, erc20ActionProvider, pythActionProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { getLangChainTools } from "@coinbase/agentkit-langchain"; +import { ChatOpenAI } from "@langchain/openai"; +import { MemorySaver } from "@langchain/langgraph"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { AgentRequest, AgentResponse } from "@/app/types/api"; + +let agent: ReturnType; + +async function initializeAgent(): Promise> { + if (agent) { + return agent; + } + + try { + // Initialize LLM + const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); + + // Initialize WalletProvider + const walletProvider = await CdpWalletProvider.configureWithWallet({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY?.replace(/\\n/g, "\n"), + networkId: process.env.NETWORK_ID || "base-sepolia", + }); + + // Initialize AgentKit + 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?.replace(/\\n/g, "\n"), + }), + cdpWalletActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY?.replace(/\\n/g, "\n"), + }), + ], + }); + const tools = await getLangChainTools(agentkit); + const memory = new MemorySaver(); + + // Initialize Agent + agent = createReactAgent({ + llm, + tools, + checkpointSaver: memory, + }); + + return agent; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} + +export async function POST(req: Request & { json: () => Promise }): Promise> { + try { + const { userMessage } = await req.json(); + const agent = await initializeAgent(); + + const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + + let responseMessage = ""; + for await (const chunk of stream) { + if ("agent" in chunk) { + responseMessage += chunk.agent.messages[0].content; + } + } + + return NextResponse.json({ response: responseMessage }); + } catch (error) { + console.error("Error processing request:", error); + return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + } +} diff --git a/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts b/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts new file mode 100644 index 000000000..c6dab1aaf --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts @@ -0,0 +1,33 @@ +import { useState } from "react"; + +export function useAgent() { + const [messages, setMessages] = useState<{ text: string; sender: "user" | "agent" }[]>([]); + const [isThinking, setIsThinking] = useState(false); + + const sendMessage = async (input: string) => { + if (!input.trim()) return; + + setMessages((prev) => [...prev, { text: input, sender: "user" }]); + setIsThinking(true); + + try { + const response = await fetch("/api/agent", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ userMessage: input }), + }); + + const data = await response.json(); + + if (data.response) { + setMessages((prev) => [...prev, { text: data.response, sender: "agent" }]); + } + } catch (error) { + console.error("Error communicating with agent:", error); + } finally { + setIsThinking(false); + } + }; + + return { messages, sendMessage, isThinking }; +} diff --git a/typescript/create-onchain-agent/templates/next/app/types/api.ts b/typescript/create-onchain-agent/templates/next/app/types/api.ts new file mode 100644 index 000000000..888b65e47 --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/types/api.ts @@ -0,0 +1,4 @@ + +export type AgentRequest = { userMessage: string } + +export type AgentResponse = { response: string } | { error: string} From 433e10651a90eb347e1ab2fff27a51b87d69a8e9 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Tue, 18 Feb 2025 13:08:51 -0800 Subject: [PATCH 06/49] Implemented API routes for CDV, Viem, Privy and Solana routes --- .../src/walletProviders.ts | 8 +- .../next/app/api/agent/{ => cdp}/route.ts | 0 .../next/app/api/agent/privy/route.ts | 76 +++++++++++++++++ .../next/app/api/agent/solanaKeypair/route.ts | 84 +++++++++++++++++++ .../next/app/api/agent/viem/route.ts | 80 ++++++++++++++++++ .../templates/next/package.json | 4 +- 6 files changed, 247 insertions(+), 5 deletions(-) rename typescript/create-onchain-agent/templates/next/app/api/agent/{ => cdp}/route.ts (100%) create mode 100644 typescript/create-onchain-agent/templates/next/app/api/agent/privy/route.ts create mode 100644 typescript/create-onchain-agent/templates/next/app/api/agent/solanaKeypair/route.ts create mode 100644 typescript/create-onchain-agent/templates/next/app/api/agent/viem/route.ts diff --git a/typescript/create-onchain-agent/src/walletProviders.ts b/typescript/create-onchain-agent/src/walletProviders.ts index 8b0da79ee..3994c57bf 100644 --- a/typescript/create-onchain-agent/src/walletProviders.ts +++ b/typescript/create-onchain-agent/src/walletProviders.ts @@ -11,19 +11,19 @@ type WalletProviderConfiguration = { export const WalletOptionsLookup: Record = { CDP: { - env: ['CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], + env: ['NETWORK_ID', 'CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], apiRoute: 'agent/cdp/route.ts' }, Viem: { - env: ['PRIVATE_KEY'], + env: ['NETWORK_ID', 'PRIVATE_KEY'], apiRoute: 'agent/viem/route.ts' }, Privy: { - env: [], + env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], apiRoute: 'agent/privy/route.ts' }, SolanaKeypair: { - env: ['SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], + env: ['NETWORK_ID', 'SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], apiRoute: 'agent/solanaKeypair/route.ts' }, } \ No newline at end of file diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/cdp/route.ts similarity index 100% rename from typescript/create-onchain-agent/templates/next/app/api/agent/route.ts rename to typescript/create-onchain-agent/templates/next/app/api/agent/cdp/route.ts diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/privy/route.ts new file mode 100644 index 000000000..eae67b6fb --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/privy/route.ts @@ -0,0 +1,76 @@ +import { AgentRequest, AgentResponse } from "@/app/types/api"; +import { AgentKit, erc20ActionProvider, PrivyWalletProvider, pythActionProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { getLangChainTools } from "@coinbase/agentkit-langchain"; +import { MemorySaver } from "@langchain/langgraph"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { ChatOpenAI } from "@langchain/openai"; +import { NextResponse } from "next/server"; + +let agent: ReturnType; + +async function initializeAgent(): Promise> { + if (agent) { + return agent; + } + + try { + // Initialize LLM + const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); + + // Initialize WalletProvider + const walletProvider = await PrivyWalletProvider.configureWithWallet({ + 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 + }); + + // Initialize AgentKit + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ], + }); + const tools = await getLangChainTools(agentkit); + const memory = new MemorySaver(); + + // Initialize Agent + agent = createReactAgent({ + llm, + tools, + checkpointSaver: memory, + }); + + return agent; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} + +export async function POST(req: Request & { json: () => Promise }): Promise> { + try { + const { userMessage } = await req.json(); + const agent = await initializeAgent(); + + const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + + let responseMessage = ""; + for await (const chunk of stream) { + if ("agent" in chunk) { + responseMessage += chunk.agent.messages[0].content; + } + } + + return NextResponse.json({ response: responseMessage }); + } catch (error) { + console.error("Error processing request:", error); + return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + } +} diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/solanaKeypair/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/solanaKeypair/route.ts new file mode 100644 index 000000000..cf850194a --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/solanaKeypair/route.ts @@ -0,0 +1,84 @@ +import { AgentRequest, AgentResponse } from "@/app/types/api"; +import { AgentKit, SOLANA_NETWORK_ID, SolanaKeypairWalletProvider, splActionProvider, walletActionProvider } from "@coinbase/agentkit"; +import { getLangChainTools } from "@coinbase/agentkit-langchain"; +import { MemorySaver } from "@langchain/langgraph"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { ChatOpenAI } from "@langchain/openai"; +import { Keypair } from "@solana/web3.js"; +import bs58 from "bs58"; +import { NextResponse } from "next/server"; + +let agent: ReturnType; + +async function initializeAgent(): Promise> { + if (agent) { + return agent; + } + + try { + // Initialize LLM + const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); + + // Setup Private Key + let solanaPrivateKey = process.env.SOLANA_PRIVATE_KEY as string; + if (!solanaPrivateKey) { + console.log(`No Solana account detected. Generating a wallet...`); + const keypair = Keypair.generate(); + solanaPrivateKey = bs58.encode(keypair.secretKey); + console.log(`Created Solana wallet: ${keypair.publicKey.toBase58()}`); + console.log(`Store the private key in your .env for future reuse: ${solanaPrivateKey}`); + } + + // Initialize WalletProvider + // Configure Solana Keypair Wallet Provider + const rpcUrl = process.env.SOLANA_RPC_URL; + let walletProvider: SolanaKeypairWalletProvider; + if (rpcUrl) { + walletProvider = await SolanaKeypairWalletProvider.fromRpcUrl(rpcUrl, solanaPrivateKey); + } else { + const network = (process.env.NETWORK_ID ?? "solana-devnet") as SOLANA_NETWORK_ID; + walletProvider = await SolanaKeypairWalletProvider.fromNetwork(network, solanaPrivateKey); + } + + // Initialize AgentKit + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [splActionProvider(), walletActionProvider()], + }); + const tools = await getLangChainTools(agentkit); + const memory = new MemorySaver(); + + // Initialize Agent + agent = createReactAgent({ + llm, + tools, + checkpointSaver: memory, + }); + + return agent; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} + +export async function POST(req: Request & { json: () => Promise }): Promise> { + try { + const { userMessage } = await req.json(); + const agent = await initializeAgent(); + + const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + + let responseMessage = ""; + for await (const chunk of stream) { + if ("agent" in chunk) { + responseMessage += chunk.agent.messages[0].content; + } + } + + return NextResponse.json({ response: responseMessage }); + } catch (error) { + console.error("Error processing request:", error); + return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + } +} diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/viem/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/viem/route.ts new file mode 100644 index 000000000..6e86bc83b --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/viem/route.ts @@ -0,0 +1,80 @@ +import { NextResponse } from "next/server"; +import { AgentKit, cdpApiActionProvider, cdpWalletActionProvider, CdpWalletProvider, erc20ActionProvider, NETWORK_ID_TO_VIEM_CHAIN, pythActionProvider, ViemWalletProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { getLangChainTools } from "@coinbase/agentkit-langchain"; +import { ChatOpenAI } from "@langchain/openai"; +import { MemorySaver } from "@langchain/langgraph"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { AgentRequest, AgentResponse } from "@/app/types/api"; +import { createWalletClient, http } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +let agent: ReturnType; + +async function initializeAgent(): Promise> { + if (agent) { + return agent; + } + + try { + // Initialize LLM + const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); + + const account = privateKeyToAccount( + process.env.PRIVATE_KEY as `0x${string}` + ); + const networkId = process.env.NETWORK_ID as string; + + // Initialize WalletProvider + const client = createWalletClient({ + account, + chain: NETWORK_ID_TO_VIEM_CHAIN[networkId], + transport: http(), + }); + const walletProvider = await new ViemWalletProvider(client); + + // Initialize AgentKit + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ], + }); + const tools = await getLangChainTools(agentkit); + const memory = new MemorySaver(); + + // Initialize Agent + agent = createReactAgent({ + llm, + tools, + checkpointSaver: memory, + }); + + return agent; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} + +export async function POST(req: Request & { json: () => Promise }): Promise> { + try { + const { userMessage } = await req.json(); + const agent = await initializeAgent(); + + const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + + let responseMessage = ""; + for await (const chunk of stream) { + if ("agent" in chunk) { + responseMessage += chunk.agent.messages[0].content; + } + } + + return NextResponse.json({ response: responseMessage }); + } catch (error) { + console.error("Error processing request:", error); + return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + } +} diff --git a/typescript/create-onchain-agent/templates/next/package.json b/typescript/create-onchain-agent/templates/next/package.json index 54ee7fb16..378d24697 100644 --- a/typescript/create-onchain-agent/templates/next/package.json +++ b/typescript/create-onchain-agent/templates/next/package.json @@ -10,10 +10,12 @@ }, "dependencies": { "@coinbase/agentkit": "latest", + "@solana/web3.js": "^1.98.0", + "@tanstack/react-query": "^5", + "bs58": "^6.0.0", "next": "14.2.15", "react": "^18", "react-dom": "^18", - "@tanstack/react-query": "^5", "viem": "^2.17.4", "wagmi": "^2.11.0" }, From 1c62e6dc5720dcc6d9dcbaf775766961eee8c998 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Tue, 18 Feb 2025 13:10:30 -0800 Subject: [PATCH 07/49] Refactored naming of route configuration and options --- typescript/create-onchain-agent/src/cli.ts | 6 +++--- typescript/create-onchain-agent/src/walletProviders.ts | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 1db392804..46800e91b 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -12,7 +12,7 @@ import { toValidPackageName, optimizedCopy, } from "./utils.js"; -import { WalletOption, WalletOptions, WalletOptionsLookup } from "./walletProviders.js"; +import { WalletProviderChoice, WalletProviderChoices, WalletProviderRouteConfigurations } from "./walletProviders.js"; const sourceDir = path.resolve( fileURLToPath(import.meta.url), @@ -95,7 +95,7 @@ async function init() { type: "select", name: "walletProvider", message: pc.reset("Choose a wallet provider:"), - choices: WalletOptions.map((option) => ({ title: option, value: option })), + choices: WalletProviderChoices.map((option) => ({ title: option, value: option })), }, ], { @@ -111,7 +111,7 @@ async function init() { } const { projectName, packageName, walletProvider } = result; - const walletProviderOptions = WalletOptionsLookup[walletProvider as WalletOption] + const walletProviderOptions = WalletProviderRouteConfigurations[walletProvider as WalletProviderChoice] const root = path.join(process.cwd(), projectName); const spinner = ora(`Creating ${projectName}...`).start(); diff --git a/typescript/create-onchain-agent/src/walletProviders.ts b/typescript/create-onchain-agent/src/walletProviders.ts index 3994c57bf..98695f345 100644 --- a/typescript/create-onchain-agent/src/walletProviders.ts +++ b/typescript/create-onchain-agent/src/walletProviders.ts @@ -1,15 +1,14 @@ -export type WalletOption = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' +export type WalletProviderChoice = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' -export const WalletOptions: WalletOption[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] +export const WalletProviderChoices: WalletProviderChoice[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] -type WalletProviderConfiguration = { +type WalletProviderRouteConfiguration = { env: string[]; apiRoute: string } - -export const WalletOptionsLookup: Record = { +export const WalletProviderRouteConfigurations: Record = { CDP: { env: ['NETWORK_ID', 'CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], apiRoute: 'agent/cdp/route.ts' From 72e59ae3e78c74f46285b47483e1439e33d4ba65 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Tue, 18 Feb 2025 14:20:58 -0800 Subject: [PATCH 08/49] Setup CLI for testing --- typescript/create-onchain-agent/package.json | 6 +- typescript/create-onchain-agent/src/cli.ts | 33 ++++------ typescript/create-onchain-agent/src/utils.ts | 62 +++++++++++++++++++ .../src/walletProviders.ts | 28 --------- 4 files changed, 78 insertions(+), 51 deletions(-) delete mode 100644 typescript/create-onchain-agent/src/walletProviders.ts diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index 8d6805d8a..bc7c6d1f6 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -4,11 +4,11 @@ "version": "0.0.1", "license": "MIT", "scripts": { - "build": "bun run clean && bun run build:esm+types", + "build": "npm run clean && npm run build:esm+types", "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", "check:types": "tsc --noEmit", "clean": "rm -rf dist tsconfig.tsbuildinfo", - "dev": "bun src/cli.ts" + "dev": "node --loader ts-node/esm src/cli.ts" }, "files": [ "dist/**", @@ -19,7 +19,7 @@ "templates/**" ], "bin": { - "create-onchain": "./dist/esm/cli.js" + "create-onchain-agent": "./dist/esm/cli.js" }, "sideEffects": false, "type": "module", diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 46800e91b..d36c313b1 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -1,22 +1,21 @@ #!/usr/bin/env node import fs from "fs"; +import ora from "ora"; import path from "path"; -import { fileURLToPath } from "url"; -import prompts from "prompts"; import pc from "picocolors"; -import ora from "ora"; +import prompts from "prompts"; +import { fileURLToPath } from "url"; import { - createClickableLink, - detectPackageManager, + handleWalletProviderSelection, isValidPackageName, - toValidPackageName, optimizedCopy, + toValidPackageName, + WalletProviderChoices } from "./utils.js"; -import { WalletProviderChoice, WalletProviderChoices, WalletProviderRouteConfigurations } from "./walletProviders.js"; const sourceDir = path.resolve( fileURLToPath(import.meta.url), - "../../../templates/agent" + "../../../templates/next" ); const renameFiles: Record = { @@ -50,11 +49,11 @@ async function copyDir(src: string, dest: string) { async function init() { console.log( `${pc.greenBright(` - █████ ██████ ███████ ███ ██ ████████ ██ ██ ██ ████████ -██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ -███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -██ ██ ██████ ███████ ██ ████ ██ ██ ██ ██ ██ + █████ ██████ ███████ ███ ██ ████████ ██ ██ ██ ████████ +██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ +███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +██ ██ ██████ ███████ ██ ████ ██ ██ ██ ██ ██ `)}\n\n` ); @@ -111,7 +110,6 @@ async function init() { } const { projectName, packageName, walletProvider } = result; - const walletProviderOptions = WalletProviderRouteConfigurations[walletProvider as WalletProviderChoice] const root = path.join(process.cwd(), projectName); const spinner = ora(`Creating ${projectName}...`).start(); @@ -123,12 +121,7 @@ async function init() { pkg.name = packageName || toValidPackageName(projectName); await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); - // Create .env file - const envPath = path.join(root, ".env"); - await fs.promises.writeFile( - envPath, - `NETWORK_ID=\nOPENAI_API_KEY=\n${walletProviderOptions.env.map(envVar => `${envVar}=`).join('=\n')}` - ); + await handleWalletProviderSelection(root, walletProvider) spinner.succeed(); console.log(`\n${pc.magenta(`Created new AgentKit project in ${root}`)}`); diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 41fe03699..186f60779 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -1,6 +1,13 @@ import fs from 'fs/promises'; import path from 'path'; +export type WalletProviderChoice = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' +export const WalletProviderChoices: WalletProviderChoice[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] +type WalletProviderRouteConfiguration = { + env: string[]; + apiRoute: string +} + async function copyFile(src: string, dest: string) { await fs.copyFile(src, dest); } @@ -68,3 +75,58 @@ export function detectPackageManager(): string { } return 'npm'; // default to npm if unable to detect } + +export const WalletProviderRouteConfigurations: Record = { + CDP: { + env: ['NETWORK_ID', 'CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], + apiRoute: 'cdp/route.ts' + }, + Viem: { + env: ['NETWORK_ID', 'PRIVATE_KEY'], + apiRoute: 'viem/route.ts' + }, + Privy: { + env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], + apiRoute: 'privy/route.ts' + }, + SolanaKeypair: { + env: ['NETWORK_ID', 'SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], + apiRoute: 'solanaKeypair/route.ts' + }, +} + +export async function handleWalletProviderSelection(root: string, walletProvider: WalletProviderChoice) { + const agentDir = path.join(root, "app", "api", "agent"); + const selectedRouteConfig = WalletProviderRouteConfigurations[walletProvider]; + + // Create .env file + const envPath = path.join(root, ".env"); + await fs.writeFile( + envPath, + `OPENAI_API_KEY=\n${selectedRouteConfig.env.map(envVar => `${envVar}=`).join('\n')}` + ); + + // Promote selected route (move `apiRoute` to `api/agent/route.ts`) + const selectedRoutePath = path.join(agentDir, selectedRouteConfig.apiRoute); + const newRoutePath = path.join(agentDir, "route.ts"); + + await fs.rename(selectedRoutePath, newRoutePath); + + // Delete all unselected routes + const providerRoutes = Object.values(WalletProviderRouteConfigurations).map((config) => path.join(agentDir, config.apiRoute)); + for (const routePath of providerRoutes) { + // Remove file + await fs.rm(routePath, { recursive: true, force: true }); + + // If directory is empty, remove directory + try { + const parentFolder = path.dirname(routePath); + const files = await fs.readdir(parentFolder); + if (files.length === 0) { + await fs.rm(parentFolder, { recursive: true, force: true }); + } + } catch (error) { + // Skip removing directory + } + } +} diff --git a/typescript/create-onchain-agent/src/walletProviders.ts b/typescript/create-onchain-agent/src/walletProviders.ts deleted file mode 100644 index 98695f345..000000000 --- a/typescript/create-onchain-agent/src/walletProviders.ts +++ /dev/null @@ -1,28 +0,0 @@ - - -export type WalletProviderChoice = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' - -export const WalletProviderChoices: WalletProviderChoice[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] - -type WalletProviderRouteConfiguration = { - env: string[]; - apiRoute: string -} -export const WalletProviderRouteConfigurations: Record = { - CDP: { - env: ['NETWORK_ID', 'CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], - apiRoute: 'agent/cdp/route.ts' - }, - Viem: { - env: ['NETWORK_ID', 'PRIVATE_KEY'], - apiRoute: 'agent/viem/route.ts' - }, - Privy: { - env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], - apiRoute: 'agent/privy/route.ts' - }, - SolanaKeypair: { - env: ['NETWORK_ID', 'SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], - apiRoute: 'agent/solanaKeypair/route.ts' - }, -} \ No newline at end of file From dd17fd1455dc8bcf0d8ff1869c120f8317efe580 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 14:59:00 -0800 Subject: [PATCH 09/49] Added branching to CLI for network -> wallet provider selection --- .../create-onchain-agent/package-lock.json | 2 +- typescript/create-onchain-agent/src/cli.ts | 17 +++++++++--- typescript/create-onchain-agent/src/utils.ts | 26 +++++++++++++++++-- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/typescript/create-onchain-agent/package-lock.json b/typescript/create-onchain-agent/package-lock.json index 274df4b54..77bff5836 100644 --- a/typescript/create-onchain-agent/package-lock.json +++ b/typescript/create-onchain-agent/package-lock.json @@ -16,7 +16,7 @@ "prompts": "^2.4.2" }, "bin": { - "create-onchain": "dist/esm/cli.js" + "create-onchain-agent": "dist/esm/cli.js" }, "devDependencies": { "@types/cross-spawn": "^6.0.6", diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index d36c313b1..342fb4c98 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -8,9 +8,11 @@ import { fileURLToPath } from "url"; import { handleWalletProviderSelection, isValidPackageName, + Network, + Networks, + NetworkToWalletProviders, optimizedCopy, toValidPackageName, - WalletProviderChoices } from "./utils.js"; const sourceDir = path.resolve( @@ -92,9 +94,16 @@ async function init() { }, { type: "select", + name: "network", + message: pc.reset("Choose a network:"), + choices: [...Networks].map((network) => ({ title: network, value: network })), + }, + { + type: (prev, { network }) => NetworkToWalletProviders[network as Network].length > 1 ? "select" : null, name: "walletProvider", message: pc.reset("Choose a wallet provider:"), - choices: WalletProviderChoices.map((option) => ({ title: option, value: option })), + choices: (prev, { network }) => + NetworkToWalletProviders[network as Network].map((provider) => ({ title: provider, value: provider })), }, ], { @@ -109,7 +118,7 @@ async function init() { process.exit(1); } - const { projectName, packageName, walletProvider } = result; + const { projectName, packageName, network, walletProvider } = result; const root = path.join(process.cwd(), projectName); const spinner = ora(`Creating ${projectName}...`).start(); @@ -121,7 +130,7 @@ async function init() { pkg.name = packageName || toValidPackageName(projectName); await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); - await handleWalletProviderSelection(root, walletProvider) + await handleWalletProviderSelection(root, walletProvider, network) spinner.succeed(); console.log(`\n${pc.magenta(`Created new AgentKit project in ${root}`)}`); diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 186f60779..4cf73386e 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -1,6 +1,28 @@ import fs from 'fs/promises'; import path from 'path'; +export type EVMNetwork = "ethereum-mainnet" | "ethereum-sepolia" | "polygon-mainnet" | "polygon-mumbai" | "base-mainnet" | "base-sepolia" | "arbitrum-mainnet" | "arbitrum-sepolia" | "optimism-mainnet" | "optimism-sepolia"; +export type SVMNetwork = "solana-mainnet" | "solana-devnet" | "solana-testnet"; +export type Network = EVMNetwork | SVMNetwork; + +export const NetworkToWalletProviders: Record = { + "arbitrum-mainnet": ["CDP", "Viem", "Privy"], + "arbitrum-sepolia": ["Viem", "Privy"], + "base-mainnet": ["CDP","Viem", "Privy" ], + "base-sepolia": ["CDP", "Viem", "Privy"], + "ethereum-mainnet": ["CDP", "Viem", "Privy"], + "ethereum-sepolia": ["Viem", "Privy"], + "optimism-mainnet": ["Viem", "Privy"], + "optimism-sepolia": ["Viem", "Privy"], + "polygon-mainnet": ["CDP", "Viem", "Privy"], + "polygon-mumbai": ["Viem", "Privy"], + "solana-mainnet": ["SolanaKeypair", "Privy"], + "solana-devnet": ["SolanaKeypair", "Privy"], + "solana-testnet": ["SolanaKeypair", "Privy"] +} + +export const Networks: Network[] = ["ethereum-mainnet", "ethereum-sepolia", "base-mainnet", "base-sepolia", "arbitrum-mainnet", "arbitrum-sepolia", "optimism-mainnet", "optimism-sepolia", "polygon-mainnet", "polygon-mumbai", "solana-mainnet", "solana-devnet", "solana-testnet" ] + export type WalletProviderChoice = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' export const WalletProviderChoices: WalletProviderChoice[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] type WalletProviderRouteConfiguration = { @@ -95,7 +117,7 @@ export const WalletProviderRouteConfigurations: Record `${envVar}=`).join('\n')}` + `NETWORK_ID=${network}\nOPENAI_API_KEY=\n${selectedRouteConfig.env.map(envVar => `${envVar}=`).join('\n')}` ); // Promote selected route (move `apiRoute` to `api/agent/route.ts`) From ab897c59f368d548d62f524185d7a57ffd694184 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 15:13:08 -0800 Subject: [PATCH 10/49] chore: handling defaults in CLI --- typescript/create-onchain-agent/src/cli.ts | 12 ++++++++---- typescript/create-onchain-agent/src/utils.ts | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 342fb4c98..920f67955 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -61,7 +61,7 @@ async function init() { const defaultProjectName = "my-onchain-agent-app"; - let result: prompts.Answers<"projectName" | "packageName" | 'walletProvider'>; + let result: prompts.Answers<"projectName" | "packageName" | 'walletProvider' | 'network'>; try { result = await prompts( @@ -96,14 +96,18 @@ async function init() { type: "select", name: "network", message: pc.reset("Choose a network:"), - choices: [...Networks].map((network) => ({ title: network, value: network })), + choices: Networks.map((network) => ({ title: network == 'base-sepolia' ? `${network} (default)` : network, value: network })), + initial: Networks.indexOf("base-sepolia"), }, { type: (prev, { network }) => NetworkToWalletProviders[network as Network].length > 1 ? "select" : null, name: "walletProvider", message: pc.reset("Choose a wallet provider:"), - choices: (prev, { network }) => - NetworkToWalletProviders[network as Network].map((provider) => ({ title: provider, value: provider })), + choices: (prev, { network }) => NetworkToWalletProviders[network as Network].map((provider) => ({ + title: provider === NetworkToWalletProviders[network as Network][0] ? `${provider} (default)` : provider, + value: provider, + })), + initial: 0, }, ], { diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 4cf73386e..21320046b 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -122,7 +122,7 @@ export async function handleWalletProviderSelection(root: string, walletProvider const selectedRouteConfig = WalletProviderRouteConfigurations[walletProvider]; // Create .env file - const envPath = path.join(root, ".env"); + const envPath = path.join(root, ".env-local"); await fs.writeFile( envPath, `NETWORK_ID=${network}\nOPENAI_API_KEY=\n${selectedRouteConfig.env.map(envVar => `${envVar}=`).join('\n')}` From 9743183f93e59432f93540a2f84be574aed0eb97 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 16:20:17 -0800 Subject: [PATCH 11/49] chore: refactored api routes to better reflect network/walletProvider combinations --- typescript/create-onchain-agent/src/utils.ts | 91 +++++++++++++++---- .../next/app/api/agent/{ => evm}/cdp/route.ts | 0 .../app/api/agent/{ => evm}/privy/route.ts | 0 .../app/api/agent/{ => evm}/viem/route.ts | 0 .../next/app/api/agent/svm/privy/route.ts | 76 ++++++++++++++++ .../agent/{ => svm}/solanaKeypair/route.ts | 0 6 files changed, 148 insertions(+), 19 deletions(-) rename typescript/create-onchain-agent/templates/next/app/api/agent/{ => evm}/cdp/route.ts (100%) rename typescript/create-onchain-agent/templates/next/app/api/agent/{ => evm}/privy/route.ts (100%) rename typescript/create-onchain-agent/templates/next/app/api/agent/{ => evm}/viem/route.ts (100%) create mode 100644 typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts rename typescript/create-onchain-agent/templates/next/app/api/agent/{ => svm}/solanaKeypair/route.ts (100%) diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 21320046b..71932454e 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -1,10 +1,45 @@ import fs from 'fs/promises'; import path from 'path'; -export type EVMNetwork = "ethereum-mainnet" | "ethereum-sepolia" | "polygon-mainnet" | "polygon-mumbai" | "base-mainnet" | "base-sepolia" | "arbitrum-mainnet" | "arbitrum-sepolia" | "optimism-mainnet" | "optimism-sepolia"; +export type EVMNetwork = + | "ethereum-mainnet" + | "ethereum-sepolia" + | "polygon-mainnet" + | "polygon-mumbai" + | "base-mainnet" + | "base-sepolia" + | "arbitrum-mainnet" + | "arbitrum-sepolia" + | "optimism-mainnet" + | "optimism-sepolia"; + export type SVMNetwork = "solana-mainnet" | "solana-devnet" | "solana-testnet"; + export type Network = EVMNetwork | SVMNetwork; +const EVM_NETWORKS: Set = new Set([ + "ethereum-mainnet", + "ethereum-sepolia", + "polygon-mainnet", + "polygon-mumbai", + "base-mainnet", + "base-sepolia", + "arbitrum-mainnet", + "arbitrum-sepolia", + "optimism-mainnet", + "optimism-sepolia", +]); + +const SVM_NETWORKS: Set = new Set([ + "solana-mainnet", + "solana-devnet", + "solana-testnet", +]); + +export function getNetworkFamily(network: EVMNetwork | SVMNetwork) { + return EVM_NETWORKS.has(network) ? 'EVM' : SVM_NETWORKS.has(network) ? 'SVM' : undefined; +} + export const NetworkToWalletProviders: Record = { "arbitrum-mainnet": ["CDP", "Viem", "Privy"], "arbitrum-sepolia": ["Viem", "Privy"], @@ -98,28 +133,46 @@ export function detectPackageManager(): string { return 'npm'; // default to npm if unable to detect } -export const WalletProviderRouteConfigurations: Record = { - CDP: { - env: ['NETWORK_ID', 'CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], - apiRoute: 'cdp/route.ts' - }, - Viem: { - env: ['NETWORK_ID', 'PRIVATE_KEY'], - apiRoute: 'viem/route.ts' - }, - Privy: { - env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], - apiRoute: 'privy/route.ts' - }, - SolanaKeypair: { - env: ['NETWORK_ID', 'SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], - apiRoute: 'solanaKeypair/route.ts' +export const WalletProviderRouteConfigurations: Record<('EVM' | 'SVM'), Partial>> = { + EVM: { + CDP: { + env: ['NETWORK_ID', 'CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], + apiRoute: 'evm/cdp/route.ts' + }, + Viem: { + env: ['NETWORK_ID', 'PRIVATE_KEY'], + apiRoute: 'evm/viem/route.ts' + }, + Privy: { + env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], + apiRoute: 'evm/privy/route.ts' + }, }, + SVM: { + SolanaKeypair: { + env: ['NETWORK_ID', 'SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], + apiRoute: 'svm/solanaKeypair/route.ts' + }, + Privy: { + env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], + apiRoute: 'svm/privy/route.ts' + }, + } } export async function handleWalletProviderSelection(root: string, walletProvider: WalletProviderChoice, network: Network) { const agentDir = path.join(root, "app", "api", "agent"); - const selectedRouteConfig = WalletProviderRouteConfigurations[walletProvider]; + const networkFamily = getNetworkFamily(network) + + if (!networkFamily) { + return 'ERROR: Unsupported network selected'; + } + + const selectedRouteConfig = WalletProviderRouteConfigurations[networkFamily][walletProvider]; + + if (!selectedRouteConfig) { + return 'ERROR: Selected invalid network & wallet provider combination' + } // Create .env file const envPath = path.join(root, ".env-local"); @@ -135,7 +188,7 @@ export async function handleWalletProviderSelection(root: string, walletProvider await fs.rename(selectedRoutePath, newRoutePath); // Delete all unselected routes - const providerRoutes = Object.values(WalletProviderRouteConfigurations).map((config) => path.join(agentDir, config.apiRoute)); + const providerRoutes = Object.values(WalletProviderRouteConfigurations[networkFamily]).map((config) => path.join(agentDir, config.apiRoute)); for (const routePath of providerRoutes) { // Remove file await fs.rm(routePath, { recursive: true, force: true }); diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/cdp/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts similarity index 100% rename from typescript/create-onchain-agent/templates/next/app/api/agent/cdp/route.ts rename to typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts similarity index 100% rename from typescript/create-onchain-agent/templates/next/app/api/agent/privy/route.ts rename to typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/viem/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts similarity index 100% rename from typescript/create-onchain-agent/templates/next/app/api/agent/viem/route.ts rename to typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts new file mode 100644 index 000000000..eae67b6fb --- /dev/null +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts @@ -0,0 +1,76 @@ +import { AgentRequest, AgentResponse } from "@/app/types/api"; +import { AgentKit, erc20ActionProvider, PrivyWalletProvider, pythActionProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { getLangChainTools } from "@coinbase/agentkit-langchain"; +import { MemorySaver } from "@langchain/langgraph"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { ChatOpenAI } from "@langchain/openai"; +import { NextResponse } from "next/server"; + +let agent: ReturnType; + +async function initializeAgent(): Promise> { + if (agent) { + return agent; + } + + try { + // Initialize LLM + const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); + + // Initialize WalletProvider + const walletProvider = await PrivyWalletProvider.configureWithWallet({ + 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 + }); + + // Initialize AgentKit + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ], + }); + const tools = await getLangChainTools(agentkit); + const memory = new MemorySaver(); + + // Initialize Agent + agent = createReactAgent({ + llm, + tools, + checkpointSaver: memory, + }); + + return agent; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} + +export async function POST(req: Request & { json: () => Promise }): Promise> { + try { + const { userMessage } = await req.json(); + const agent = await initializeAgent(); + + const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + + let responseMessage = ""; + for await (const chunk of stream) { + if ("agent" in chunk) { + responseMessage += chunk.agent.messages[0].content; + } + } + + return NextResponse.json({ response: responseMessage }); + } catch (error) { + console.error("Error processing request:", error); + return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + } +} diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/solanaKeypair/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts similarity index 100% rename from typescript/create-onchain-agent/templates/next/app/api/agent/solanaKeypair/route.ts rename to typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts From 260897da50000297803f423ee307478e16b271ed Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 16:41:59 -0800 Subject: [PATCH 12/49] fix: changes after rebase --- .../next/app/api/agent/svm/privy/route.ts | 14 +++++++------- .../next/app/api/agent/svm/solanaKeypair/route.ts | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts index eae67b6fb..98d09c586 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts @@ -1,5 +1,5 @@ import { AgentRequest, AgentResponse } from "@/app/types/api"; -import { AgentKit, erc20ActionProvider, PrivyWalletProvider, pythActionProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { AgentKit, erc20ActionProvider, jupiterActionProvider, PrivyWalletProvider, pythActionProvider, splActionProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; import { getLangChainTools } from "@coinbase/agentkit-langchain"; import { MemorySaver } from "@langchain/langgraph"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; @@ -22,19 +22,19 @@ async function initializeAgent(): Promise> { 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 + authorizationKeyId: process.env.PRIVY_WALLET_AUTHORIZATION_KEY_ID, + chainType: "solana", + networkId: process.env.NETWORK_ID, }); // Initialize AgentKit const agentkit = await AgentKit.from({ walletProvider, actionProviders: [ - wethActionProvider(), - pythActionProvider(), - walletActionProvider(), - erc20ActionProvider(), + walletActionProvider(), + splActionProvider(), + jupiterActionProvider() ], }); const tools = await getLangChainTools(agentkit); diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts index cf850194a..44ec9ac73 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts @@ -1,5 +1,5 @@ import { AgentRequest, AgentResponse } from "@/app/types/api"; -import { AgentKit, SOLANA_NETWORK_ID, SolanaKeypairWalletProvider, splActionProvider, walletActionProvider } from "@coinbase/agentkit"; +import { AgentKit, SOLANA_NETWORK_ID, SolanaKeypairWalletProvider, splActionProvider, walletActionProvider, jupiterActionProvider } from "@coinbase/agentkit"; import { getLangChainTools } from "@coinbase/agentkit-langchain"; import { MemorySaver } from "@langchain/langgraph"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; @@ -43,7 +43,7 @@ async function initializeAgent(): Promise> { // Initialize AgentKit const agentkit = await AgentKit.from({ walletProvider, - actionProviders: [splActionProvider(), walletActionProvider()], + actionProviders: [splActionProvider(), walletActionProvider(), jupiterActionProvider()], }); const tools = await getLangChainTools(agentkit); const memory = new MemorySaver(); From 2143171708ef1bbc01b80e1e4c864a9cae401bc1 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 16:57:35 -0800 Subject: [PATCH 13/49] chore: cleanup files better after refactor --- typescript/create-onchain-agent/src/utils.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 71932454e..30e8799bb 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -136,11 +136,11 @@ export function detectPackageManager(): string { export const WalletProviderRouteConfigurations: Record<('EVM' | 'SVM'), Partial>> = { EVM: { CDP: { - env: ['NETWORK_ID', 'CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], + env: ['CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], apiRoute: 'evm/cdp/route.ts' }, Viem: { - env: ['NETWORK_ID', 'PRIVATE_KEY'], + env: ['PRIVATE_KEY'], apiRoute: 'evm/viem/route.ts' }, Privy: { @@ -150,7 +150,7 @@ export const WalletProviderRouteConfigurations: Record<('EVM' | 'SVM'), Partial< }, SVM: { SolanaKeypair: { - env: ['NETWORK_ID', 'SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], + env: ['SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], apiRoute: 'svm/solanaKeypair/route.ts' }, Privy: { @@ -188,7 +188,8 @@ export async function handleWalletProviderSelection(root: string, walletProvider await fs.rename(selectedRoutePath, newRoutePath); // Delete all unselected routes - const providerRoutes = Object.values(WalletProviderRouteConfigurations[networkFamily]).map((config) => path.join(agentDir, config.apiRoute)); + const allRouteConfigurations = Object.values(WalletProviderRouteConfigurations).flatMap(routeConfigurations => Object.values(routeConfigurations)).filter(x => x); + const providerRoutes = allRouteConfigurations.map((config) => path.join(agentDir, config.apiRoute)); for (const routePath of providerRoutes) { // Remove file await fs.rm(routePath, { recursive: true, force: true }); @@ -204,4 +205,6 @@ export async function handleWalletProviderSelection(root: string, walletProvider // Skip removing directory } } + await fs.rm(path.join(agentDir, 'evm'), { recursive: true, force: true }); + await fs.rm(path.join(agentDir, 'svm'), { recursive: true, force: true }); } From c089a4667c4497c04930299b147c2ad38b9f1d8b Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 17:05:24 -0800 Subject: [PATCH 14/49] feat: improve CLI display --- typescript/create-onchain-agent/src/cli.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 920f67955..28fdd5685 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -50,13 +50,15 @@ async function copyDir(src: string, dest: string) { async function init() { console.log( - `${pc.greenBright(` + `${pc.blue(` █████ ██████ ███████ ███ ██ ████████ ██ ██ ██ ████████ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ ███████ ██ ████ ██ ██ ██ ██ ██ -`)}\n\n` + + Giving every AI agent a crypto wallet +`)}` ); const defaultProjectName = "my-onchain-agent-app"; @@ -137,16 +139,17 @@ async function init() { await handleWalletProviderSelection(root, walletProvider, network) spinner.succeed(); - console.log(`\n${pc.magenta(`Created new AgentKit project in ${root}`)}`); + console.log(`\n${pc.blueBright(`Created new AgentKit project in ${root}`)}`); console.log(`\nFrameworks:`); - console.log(`${pc.cyan("- React")}`); - console.log(`${pc.cyan("- Next.js")}`); - console.log(`${pc.cyan("- Tailwind CSS")}`); - console.log(`${pc.cyan("- ESLint")}`); + console.log(`${pc.blueBright("- AgentKit")}`); + console.log(`${pc.blueBright("- React")}`); + console.log(`${pc.blueBright("- Next.js")}`); + console.log(`${pc.blueBright("- Tailwind CSS")}`); + console.log(`${pc.blueBright("- ESLint")}`); console.log( - `\nTo get started with ${pc.green(projectName)}, run the following commands:\n` + `\nTo get started with ${pc.blueBright(projectName)}, run the following commands:\n` ); if (root !== process.cwd()) { console.log(` - cd ${path.relative(process.cwd(), root)}`); From ccda1824391814b03da0d66e9e58e660f5e7e5c1 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 22:59:13 -0800 Subject: [PATCH 15/49] fix: adressed feedback --- typescript/create-onchain-agent/src/cli.ts | 43 ++++++++++++++---- typescript/create-onchain-agent/src/utils.ts | 48 ++++++++++++-------- 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 28fdd5685..9449ae327 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -11,8 +11,10 @@ import { Network, Networks, NetworkToWalletProviders, + NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, optimizedCopy, toValidPackageName, + WalletProviderChoice, } from "./utils.js"; const sourceDir = path.resolve( @@ -61,9 +63,9 @@ async function init() { `)}` ); - const defaultProjectName = "my-onchain-agent-app"; + const defaultProjectName = "onchain-agent"; - let result: prompts.Answers<"projectName" | "packageName" | 'walletProvider' | 'network'>; + let result: prompts.Answers<"projectName" | "packageName" | 'walletProvider' | 'network' | 'chainId'>; try { result = await prompts( @@ -98,17 +100,37 @@ async function init() { type: "select", name: "network", message: pc.reset("Choose a network:"), - choices: Networks.map((network) => ({ title: network == 'base-sepolia' ? `${network} (default)` : network, value: network })), + choices: Networks.map((network) => ({ + title: network === "base-sepolia" ? `${network} (default)` : network, + value: network as Network | null, + })).concat([{ title: 'other', value: null}]), initial: Networks.indexOf("base-sepolia"), }, { - type: (prev, { network }) => NetworkToWalletProviders[network as Network].length > 1 ? "select" : null, + type: (prev, { network }) => network === null ? "text" : null, + name: "chainId", + message: pc.reset("Enter your Ethereum chain ID:"), + validate: (value) => value.trim() ? Number.parseInt(value) ? true : "Chain ID must be a number." : "Chain ID cannot be empty.", + }, + { + type: (prev, { network }) => !network || NetworkToWalletProviders[network as Network].length > 1 ? "select" : null, name: "walletProvider", message: pc.reset("Choose a wallet provider:"), - choices: (prev, { network }) => NetworkToWalletProviders[network as Network].map((provider) => ({ - title: provider === NetworkToWalletProviders[network as Network][0] ? `${provider} (default)` : provider, - value: provider, - })), + choices: (prev, { network, chainId }) => { + let walletProviderChoises: WalletProviderChoice[]; + + if (network) { + walletProviderChoises = NetworkToWalletProviders[network as Network]; + } + else { + walletProviderChoises = NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS; + } + + return walletProviderChoises.map((provider) => ({ + title: provider === walletProviderChoises[0] ? `${provider} (default)` : provider, + value: provider, + })) + }, initial: 0, }, ], @@ -123,8 +145,8 @@ async function init() { console.log(cancelled.message); process.exit(1); } + const { projectName, packageName, network, chainId, walletProvider } = result; - const { projectName, packageName, network, walletProvider } = result; const root = path.join(process.cwd(), projectName); const spinner = ora(`Creating ${projectName}...`).start(); @@ -136,7 +158,7 @@ async function init() { pkg.name = packageName || toValidPackageName(projectName); await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); - await handleWalletProviderSelection(root, walletProvider, network) + await handleWalletProviderSelection(root, walletProvider, network, chainId) spinner.succeed(); console.log(`\n${pc.blueBright(`Created new AgentKit project in ${root}`)}`); @@ -155,6 +177,7 @@ async function init() { console.log(` - cd ${path.relative(process.cwd(), root)}`); } console.log(" - npm install"); + console.log(" - mv .env.local .env"); console.log(" - npm run dev"); } diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 30e8799bb..78dfeccff 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -40,19 +40,23 @@ export function getNetworkFamily(network: EVMNetwork | SVMNetwork) { return EVM_NETWORKS.has(network) ? 'EVM' : SVM_NETWORKS.has(network) ? 'SVM' : undefined; } +export const CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["CDP", "Viem", "Privy"]; +export const NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["Viem", "Privy"]; +export const SVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["SolanaKeypair", "Privy"] + export const NetworkToWalletProviders: Record = { - "arbitrum-mainnet": ["CDP", "Viem", "Privy"], - "arbitrum-sepolia": ["Viem", "Privy"], - "base-mainnet": ["CDP","Viem", "Privy" ], - "base-sepolia": ["CDP", "Viem", "Privy"], - "ethereum-mainnet": ["CDP", "Viem", "Privy"], - "ethereum-sepolia": ["Viem", "Privy"], - "optimism-mainnet": ["Viem", "Privy"], - "optimism-sepolia": ["Viem", "Privy"], - "polygon-mainnet": ["CDP", "Viem", "Privy"], - "polygon-mumbai": ["Viem", "Privy"], - "solana-mainnet": ["SolanaKeypair", "Privy"], - "solana-devnet": ["SolanaKeypair", "Privy"], + "arbitrum-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "arbitrum-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "base-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "base-sepolia": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "ethereum-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "ethereum-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "optimism-mainnet": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "optimism-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "polygon-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "polygon-mumbai": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "solana-mainnet": SVM_WALLET_PROVIDERS, + "solana-devnet": SVM_WALLET_PROVIDERS, "solana-testnet": ["SolanaKeypair", "Privy"] } @@ -160,22 +164,28 @@ export const WalletProviderRouteConfigurations: Record<('EVM' | 'SVM'), Partial< } } -export async function handleWalletProviderSelection(root: string, walletProvider: WalletProviderChoice, network: Network) { +export async function handleWalletProviderSelection(root: string, walletProvider: WalletProviderChoice, network?: Network, chainId?: string) { const agentDir = path.join(root, "app", "api", "agent"); - const networkFamily = getNetworkFamily(network) - if (!networkFamily) { - return 'ERROR: Unsupported network selected'; + let networkFamily: ReturnType; + if (network) { + networkFamily = getNetworkFamily(network); + } + else if (chainId) { + networkFamily = 'EVM'; + } + else { + throw new Error('Unsupported network and chainId selected'); } - const selectedRouteConfig = WalletProviderRouteConfigurations[networkFamily][walletProvider]; + const selectedRouteConfig = WalletProviderRouteConfigurations[networkFamily!][walletProvider]; if (!selectedRouteConfig) { - return 'ERROR: Selected invalid network & wallet provider combination' + throw new Error('Selected invalid network & wallet provider combination') } // Create .env file - const envPath = path.join(root, ".env-local"); + const envPath = path.join(root, ".env.local"); await fs.writeFile( envPath, `NETWORK_ID=${network}\nOPENAI_API_KEY=\n${selectedRouteConfig.env.map(envVar => `${envVar}=`).join('\n')}` From 968a2cdae3c62f25d4a289200650a4d1e90e4228 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 23:32:15 -0800 Subject: [PATCH 16/49] feat: improved the cli logged messages --- typescript/create-onchain-agent/src/cli.ts | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 9449ae327..b93d53d46 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -161,24 +161,33 @@ async function init() { await handleWalletProviderSelection(root, walletProvider, network, chainId) spinner.succeed(); - console.log(`\n${pc.blueBright(`Created new AgentKit project in ${root}`)}`); + console.log(pc.blueBright(`\nSuccessfully created your AgentKit project in ${root}`)); console.log(`\nFrameworks:`); - console.log(`${pc.blueBright("- AgentKit")}`); - console.log(`${pc.blueBright("- React")}`); - console.log(`${pc.blueBright("- Next.js")}`); - console.log(`${pc.blueBright("- Tailwind CSS")}`); - console.log(`${pc.blueBright("- ESLint")}`); + console.log(pc.gray("- AgentKit")); + console.log(pc.gray("- React")); + console.log(pc.gray("- Next.js")); + console.log(pc.gray("- Tailwind CSS")); + console.log(pc.gray("- ESLint")); - console.log( - `\nTo get started with ${pc.blueBright(projectName)}, run the following commands:\n` - ); + console.log(pc.bold("\nWhat's Next?")); + + console.log(`\nTo get started, run the following commands:\n`); if (root !== process.cwd()) { console.log(` - cd ${path.relative(process.cwd(), root)}`); } console.log(" - npm install"); + console.log(pc.gray(" - # Open .env.local and configure your API keys")); console.log(" - mv .env.local .env"); console.log(" - npm run dev"); + + console.log(pc.bold("\nLearn more")); + console.log(" - Checkout the docs"); + console.log(pc.blueBright(" - https://docs.cdp.coinbase.com/agentkit/docs/welcome")); + console.log(" - Visit the repo"); + console.log(pc.blueBright(" - http://github.com/coinbase/agentkit")); + console.log(" - Join the community"); + console.log(pc.blueBright(" - https://discord.gg/CDP")); } init().catch((e) => { From 96f75fd925f2380feb045d3bfa4335c07f04341a Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Wed, 19 Feb 2025 23:39:03 -0800 Subject: [PATCH 17/49] refactor: types and constants out of utils --- typescript/create-onchain-agent/src/cli.ts | 7 +- .../create-onchain-agent/src/constants.ts | 74 +++++++++++++++ typescript/create-onchain-agent/src/types.ts | 23 +++++ typescript/create-onchain-agent/src/utils.ts | 93 +------------------ 4 files changed, 101 insertions(+), 96 deletions(-) create mode 100644 typescript/create-onchain-agent/src/constants.ts create mode 100644 typescript/create-onchain-agent/src/types.ts diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index b93d53d46..65fc83023 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -8,14 +8,11 @@ import { fileURLToPath } from "url"; import { handleWalletProviderSelection, isValidPackageName, - Network, - Networks, - NetworkToWalletProviders, - NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, optimizedCopy, toValidPackageName, - WalletProviderChoice, } from "./utils.js"; +import { Networks, NetworkToWalletProviders, NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS } from "./constants.js"; +import { Network, WalletProviderChoice } from "./types.js"; const sourceDir = path.resolve( fileURLToPath(import.meta.url), diff --git a/typescript/create-onchain-agent/src/constants.ts b/typescript/create-onchain-agent/src/constants.ts new file mode 100644 index 000000000..4f3365b19 --- /dev/null +++ b/typescript/create-onchain-agent/src/constants.ts @@ -0,0 +1,74 @@ +import { EVMNetwork, Network, SVMNetwork, WalletProviderChoice, WalletProviderRouteConfiguration } from "./types"; + +export const EVM_NETWORKS: Set = new Set([ + "ethereum-mainnet", + "ethereum-sepolia", + "polygon-mainnet", + "polygon-mumbai", + "base-mainnet", + "base-sepolia", + "arbitrum-mainnet", + "arbitrum-sepolia", + "optimism-mainnet", + "optimism-sepolia", +]); + +export const SVM_NETWORKS: Set = new Set([ + "solana-mainnet", + "solana-devnet", + "solana-testnet", +]); + +export const CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["CDP", "Viem", "Privy"]; +export const NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["Viem", "Privy"]; +export const SVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["SolanaKeypair", "Privy"] + +export const NetworkToWalletProviders: Record = { + "arbitrum-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "arbitrum-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "base-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "base-sepolia": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "ethereum-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "ethereum-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "optimism-mainnet": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "optimism-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "polygon-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "polygon-mumbai": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + "solana-mainnet": SVM_WALLET_PROVIDERS, + "solana-devnet": SVM_WALLET_PROVIDERS, + "solana-testnet": ["SolanaKeypair", "Privy"] +} + + +export const Networks: Network[] = ["ethereum-mainnet", "ethereum-sepolia", "base-mainnet", "base-sepolia", "arbitrum-mainnet", "arbitrum-sepolia", "optimism-mainnet", "optimism-sepolia", "polygon-mainnet", "polygon-mumbai", "solana-mainnet", "solana-devnet", "solana-testnet" ] + +export const WalletProviderChoices: WalletProviderChoice[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] + + +export const WalletProviderRouteConfigurations: Record<('EVM' | 'SVM'), Partial>> = { + EVM: { + CDP: { + env: ['CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], + apiRoute: 'evm/cdp/route.ts' + }, + Viem: { + env: ['PRIVATE_KEY'], + apiRoute: 'evm/viem/route.ts' + }, + Privy: { + env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], + apiRoute: 'evm/privy/route.ts' + }, + }, + SVM: { + SolanaKeypair: { + env: ['SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], + apiRoute: 'svm/solanaKeypair/route.ts' + }, + Privy: { + env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], + apiRoute: 'svm/privy/route.ts' + }, + } +} + diff --git a/typescript/create-onchain-agent/src/types.ts b/typescript/create-onchain-agent/src/types.ts new file mode 100644 index 000000000..0fee698c5 --- /dev/null +++ b/typescript/create-onchain-agent/src/types.ts @@ -0,0 +1,23 @@ + +export type EVMNetwork = + | "ethereum-mainnet" + | "ethereum-sepolia" + | "polygon-mainnet" + | "polygon-mumbai" + | "base-mainnet" + | "base-sepolia" + | "arbitrum-mainnet" + | "arbitrum-sepolia" + | "optimism-mainnet" + | "optimism-sepolia"; + +export type SVMNetwork = "solana-mainnet" | "solana-devnet" | "solana-testnet"; + +export type Network = EVMNetwork | SVMNetwork; + +export type WalletProviderChoice = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' + +export type WalletProviderRouteConfiguration = { + env: string[]; + apiRoute: string +} diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 78dfeccff..ca9a2b086 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -1,74 +1,12 @@ import fs from 'fs/promises'; import path from 'path'; - -export type EVMNetwork = - | "ethereum-mainnet" - | "ethereum-sepolia" - | "polygon-mainnet" - | "polygon-mumbai" - | "base-mainnet" - | "base-sepolia" - | "arbitrum-mainnet" - | "arbitrum-sepolia" - | "optimism-mainnet" - | "optimism-sepolia"; - -export type SVMNetwork = "solana-mainnet" | "solana-devnet" | "solana-testnet"; - -export type Network = EVMNetwork | SVMNetwork; - -const EVM_NETWORKS: Set = new Set([ - "ethereum-mainnet", - "ethereum-sepolia", - "polygon-mainnet", - "polygon-mumbai", - "base-mainnet", - "base-sepolia", - "arbitrum-mainnet", - "arbitrum-sepolia", - "optimism-mainnet", - "optimism-sepolia", -]); - -const SVM_NETWORKS: Set = new Set([ - "solana-mainnet", - "solana-devnet", - "solana-testnet", -]); +import { EVMNetwork, Network, SVMNetwork, WalletProviderChoice } from './types'; +import { EVM_NETWORKS, SVM_NETWORKS, WalletProviderRouteConfigurations } from './constants'; export function getNetworkFamily(network: EVMNetwork | SVMNetwork) { return EVM_NETWORKS.has(network) ? 'EVM' : SVM_NETWORKS.has(network) ? 'SVM' : undefined; } -export const CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["CDP", "Viem", "Privy"]; -export const NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["Viem", "Privy"]; -export const SVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["SolanaKeypair", "Privy"] - -export const NetworkToWalletProviders: Record = { - "arbitrum-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "arbitrum-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "base-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "base-sepolia": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "ethereum-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "ethereum-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "optimism-mainnet": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "optimism-sepolia": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "polygon-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "polygon-mumbai": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - "solana-mainnet": SVM_WALLET_PROVIDERS, - "solana-devnet": SVM_WALLET_PROVIDERS, - "solana-testnet": ["SolanaKeypair", "Privy"] -} - -export const Networks: Network[] = ["ethereum-mainnet", "ethereum-sepolia", "base-mainnet", "base-sepolia", "arbitrum-mainnet", "arbitrum-sepolia", "optimism-mainnet", "optimism-sepolia", "polygon-mainnet", "polygon-mumbai", "solana-mainnet", "solana-devnet", "solana-testnet" ] - -export type WalletProviderChoice = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' -export const WalletProviderChoices: WalletProviderChoice[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] -type WalletProviderRouteConfiguration = { - env: string[]; - apiRoute: string -} - async function copyFile(src: string, dest: string) { await fs.copyFile(src, dest); } @@ -137,33 +75,6 @@ export function detectPackageManager(): string { return 'npm'; // default to npm if unable to detect } -export const WalletProviderRouteConfigurations: Record<('EVM' | 'SVM'), Partial>> = { - EVM: { - CDP: { - env: ['CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], - apiRoute: 'evm/cdp/route.ts' - }, - Viem: { - env: ['PRIVATE_KEY'], - apiRoute: 'evm/viem/route.ts' - }, - Privy: { - env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], - apiRoute: 'evm/privy/route.ts' - }, - }, - SVM: { - SolanaKeypair: { - env: ['SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], - apiRoute: 'svm/solanaKeypair/route.ts' - }, - Privy: { - env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], - apiRoute: 'svm/privy/route.ts' - }, - } -} - export async function handleWalletProviderSelection(root: string, walletProvider: WalletProviderChoice, network?: Network, chainId?: string) { const agentDir = path.join(root, "app", "api", "agent"); From fbc3b8ef0a4bc1a3b757631136562288788c02a6 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 00:00:01 -0800 Subject: [PATCH 18/49] refactor: refactored constants and types out of utils --- .../create-onchain-agent/src/constants.ts | 88 +++++++++---------- typescript/create-onchain-agent/src/types.ts | 9 +- typescript/create-onchain-agent/src/utils.ts | 4 +- 3 files changed, 47 insertions(+), 54 deletions(-) diff --git a/typescript/create-onchain-agent/src/constants.ts b/typescript/create-onchain-agent/src/constants.ts index 4f3365b19..2e8c3d927 100644 --- a/typescript/create-onchain-agent/src/constants.ts +++ b/typescript/create-onchain-agent/src/constants.ts @@ -1,27 +1,20 @@ import { EVMNetwork, Network, SVMNetwork, WalletProviderChoice, WalletProviderRouteConfiguration } from "./types"; -export const EVM_NETWORKS: Set = new Set([ - "ethereum-mainnet", - "ethereum-sepolia", - "polygon-mainnet", - "polygon-mumbai", - "base-mainnet", - "base-sepolia", - "arbitrum-mainnet", - "arbitrum-sepolia", - "optimism-mainnet", - "optimism-sepolia", +export const EVM_NETWORKS: Set = new Set([ + "ethereum-mainnet", "ethereum-sepolia", + "polygon-mainnet", "polygon-mumbai", + "base-mainnet", "base-sepolia", + "arbitrum-mainnet", "arbitrum-sepolia", + "optimism-mainnet", "optimism-sepolia" ]); -export const SVM_NETWORKS: Set = new Set([ - "solana-mainnet", - "solana-devnet", - "solana-testnet", +export const SVM_NETWORKS: Set = new Set([ + "solana-mainnet", "solana-devnet", "solana-testnet" ]); export const CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["CDP", "Viem", "Privy"]; export const NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["Viem", "Privy"]; -export const SVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["SolanaKeypair", "Privy"] +export const SVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["SolanaKeypair", "Privy"]; export const NetworkToWalletProviders: Record = { "arbitrum-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, @@ -36,39 +29,40 @@ export const NetworkToWalletProviders: Record = "polygon-mumbai": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, "solana-mainnet": SVM_WALLET_PROVIDERS, "solana-devnet": SVM_WALLET_PROVIDERS, - "solana-testnet": ["SolanaKeypair", "Privy"] -} + "solana-testnet": SVM_WALLET_PROVIDERS +}; +export const Networks: Network[] = [...EVM_NETWORKS, ...SVM_NETWORKS]; -export const Networks: Network[] = ["ethereum-mainnet", "ethereum-sepolia", "base-mainnet", "base-sepolia", "arbitrum-mainnet", "arbitrum-sepolia", "optimism-mainnet", "optimism-sepolia", "polygon-mainnet", "polygon-mumbai", "solana-mainnet", "solana-devnet", "solana-testnet" ] +export const WalletProviderChoices: WalletProviderChoice[] = [...new Set([ + ...CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + ...NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + ...SVM_WALLET_PROVIDERS +])]; -export const WalletProviderChoices: WalletProviderChoice[] = ['CDP', 'Viem', 'Privy', 'SolanaKeypair'] - - -export const WalletProviderRouteConfigurations: Record<('EVM' | 'SVM'), Partial>> = { - EVM: { - CDP: { - env: ['CDP_API_KEY_NAME', 'CDP_API_KEY_PRIVATE_KEY'], - apiRoute: 'evm/cdp/route.ts' - }, - Viem: { - env: ['PRIVATE_KEY'], - apiRoute: 'evm/viem/route.ts' - }, - Privy: { - env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], - apiRoute: 'evm/privy/route.ts' - }, +export const WalletProviderRouteConfigurations: Record<"EVM" | "SVM", Partial>> = { + EVM: { + CDP: { + env: ["CDP_API_KEY_NAME", "CDP_API_KEY_PRIVATE_KEY"], + apiRoute: "evm/cdp/route.ts" + }, + Viem: { + env: ["PRIVATE_KEY"], + apiRoute: "evm/viem/route.ts" }, - SVM: { - SolanaKeypair: { - env: ['SOLANA_RPC_URL', 'SOLANA_PRIVATE_KEY'], - apiRoute: 'svm/solanaKeypair/route.ts' - }, - Privy: { - env: ['PRIVY_APP_ID', 'PRIVY_APP_SECRET', 'PRIVY_WALLET_ID', 'CHAIN_ID', 'PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY', 'PRIVY_WALLET_AUTHORIZATION_KEY_ID'], - apiRoute: 'svm/privy/route.ts' - }, + Privy: { + env: ["PRIVY_APP_ID", "PRIVY_APP_SECRET", "PRIVY_WALLET_ID", "CHAIN_ID", "PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY", "PRIVY_WALLET_AUTHORIZATION_KEY_ID"], + apiRoute: "evm/privy/route.ts" } -} - + }, + SVM: { + SolanaKeypair: { + env: ["SOLANA_RPC_URL", "SOLANA_PRIVATE_KEY"], + apiRoute: "svm/solanaKeypair/route.ts" + }, + Privy: { + env: ["PRIVY_APP_ID", "PRIVY_APP_SECRET", "PRIVY_WALLET_ID", "CHAIN_ID", "PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY", "PRIVY_WALLET_AUTHORIZATION_KEY_ID"], + apiRoute: "svm/privy/route.ts" + } + } +}; diff --git a/typescript/create-onchain-agent/src/types.ts b/typescript/create-onchain-agent/src/types.ts index 0fee698c5..d0704693a 100644 --- a/typescript/create-onchain-agent/src/types.ts +++ b/typescript/create-onchain-agent/src/types.ts @@ -1,4 +1,3 @@ - export type EVMNetwork = | "ethereum-mainnet" | "ethereum-sepolia" @@ -15,9 +14,9 @@ export type SVMNetwork = "solana-mainnet" | "solana-devnet" | "solana-testnet"; export type Network = EVMNetwork | SVMNetwork; -export type WalletProviderChoice = 'CDP' | 'Viem' | 'Privy' | 'SolanaKeypair' +export type WalletProviderChoice = "CDP" | "Viem" | "Privy" | "SolanaKeypair"; export type WalletProviderRouteConfiguration = { - env: string[]; - apiRoute: string -} + env: string[]; + apiRoute: string; +}; diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index ca9a2b086..b6fe22a2c 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -1,10 +1,10 @@ import fs from 'fs/promises'; import path from 'path'; import { EVMNetwork, Network, SVMNetwork, WalletProviderChoice } from './types'; -import { EVM_NETWORKS, SVM_NETWORKS, WalletProviderRouteConfigurations } from './constants'; +import { EVM_NETWORKS, SVM_NETWORKS, WalletProviderRouteConfigurations } from './constants.js'; export function getNetworkFamily(network: EVMNetwork | SVMNetwork) { - return EVM_NETWORKS.has(network) ? 'EVM' : SVM_NETWORKS.has(network) ? 'SVM' : undefined; + return EVM_NETWORKS.has(network as EVMNetwork) ? 'EVM' : SVM_NETWORKS.has(network as SVMNetwork) ? 'SVM' : undefined; } async function copyFile(src: string, dest: string) { From 97376d4e4031c6f6a4a3397b8e755305fd68cf7c Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 08:29:50 -0800 Subject: [PATCH 19/49] refactor: fileSystem out of cli --- typescript/create-onchain-agent/src/cli.ts | 57 +++---------------- .../create-onchain-agent/src/fileSystem.ts | 57 +++++++++++++++++++ typescript/create-onchain-agent/src/utils.ts | 2 +- 3 files changed, 67 insertions(+), 49 deletions(-) create mode 100644 typescript/create-onchain-agent/src/fileSystem.ts diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 65fc83023..a5339c7f7 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -4,48 +4,14 @@ import ora from "ora"; import path from "path"; import pc from "picocolors"; import prompts from "prompts"; -import { fileURLToPath } from "url"; +import { Networks, NetworkToWalletProviders, NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS } from "./constants.js"; +import { Network, WalletProviderChoice } from "./types.js"; +import { copyTemplate } from "./fileSystem.js"; import { - handleWalletProviderSelection, + handleSelection, isValidPackageName, - optimizedCopy, - toValidPackageName, + toValidPackageName } from "./utils.js"; -import { Networks, NetworkToWalletProviders, NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS } from "./constants.js"; -import { Network, WalletProviderChoice } from "./types.js"; - -const sourceDir = path.resolve( - fileURLToPath(import.meta.url), - "../../../templates/next" -); - -const renameFiles: Record = { - _gitignore: ".gitignore", - "_env.local": ".env.local", -}; - -const excludeDirs = ["node_modules", ".next"]; -const excludeFiles = [".DS_Store", "Thumbs.db"]; - -async function copyDir(src: string, dest: string) { - await fs.promises.mkdir(dest, { recursive: true }); - const entries = await fs.promises.readdir(src, { withFileTypes: true }); - - for (const entry of entries) { - const srcPath = path.join(src, entry.name); - const destPath = path.join(dest, renameFiles[entry.name] || entry.name); - - if (entry.isDirectory()) { - if (!excludeDirs.includes(entry.name)) { - await copyDir(srcPath, destPath); - } - } else { - if (!excludeFiles.includes(entry.name)) { - await optimizedCopy(srcPath, destPath); - } - } - } -} async function init() { console.log( @@ -144,18 +110,13 @@ async function init() { } const { projectName, packageName, network, chainId, walletProvider } = result; - const root = path.join(process.cwd(), projectName); - const spinner = ora(`Creating ${projectName}...`).start(); - await copyDir(sourceDir, root); - - const pkgPath = path.join(root, "package.json"); - const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8")); - pkg.name = packageName || toValidPackageName(projectName); - await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); + // Copy template over to new project + const root = await copyTemplate(projectName, packageName); - await handleWalletProviderSelection(root, walletProvider, network, chainId) + // Handle selection-specific logic over copied-template + await handleSelection(root, walletProvider, network, chainId) spinner.succeed(); console.log(pc.blueBright(`\nSuccessfully created your AgentKit project in ${root}`)); diff --git a/typescript/create-onchain-agent/src/fileSystem.ts b/typescript/create-onchain-agent/src/fileSystem.ts new file mode 100644 index 000000000..94870c993 --- /dev/null +++ b/typescript/create-onchain-agent/src/fileSystem.ts @@ -0,0 +1,57 @@ + +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import { + optimizedCopy, + toValidPackageName +} from "./utils.js"; + +const sourceDir = path.resolve( + fileURLToPath(import.meta.url), + "../../../templates/next" +); + + +const renameFiles: Record = { + _gitignore: ".gitignore", + "_env.local": ".env.local", +}; + +const excludeDirs = ["node_modules", ".next"]; +const excludeFiles = [".DS_Store", "Thumbs.db"]; + +function getSourceDir() { + return sourceDir; +} + +async function copyDir(src: string, dest: string) { + await fs.promises.mkdir(dest, { recursive: true }); + const entries = await fs.promises.readdir(src, { withFileTypes: true }); + + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, renameFiles[entry.name] || entry.name); + + if (entry.isDirectory()) { + if (!excludeDirs.includes(entry.name)) { + await copyDir(srcPath, destPath); + } + } else { + if (!excludeFiles.includes(entry.name)) { + await optimizedCopy(srcPath, destPath); + } + } + } +} + +export async function copyTemplate(projectName: string, packageName: string) { + const root = path.join(process.cwd(), projectName); + await copyDir(getSourceDir(), root); + const pkgPath = path.join(root, "package.json"); + const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8")); + pkg.name = packageName || toValidPackageName(projectName); + await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); + + return root; +} \ No newline at end of file diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index b6fe22a2c..5db5ece98 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -75,7 +75,7 @@ export function detectPackageManager(): string { return 'npm'; // default to npm if unable to detect } -export async function handleWalletProviderSelection(root: string, walletProvider: WalletProviderChoice, network?: Network, chainId?: string) { +export async function handleSelection(root: string, walletProvider: WalletProviderChoice, network?: Network, chainId?: string) { const agentDir = path.join(root, "app", "api", "agent"); let networkFamily: ReturnType; From 7fb5350a3adaf306f67e8e058c05bedb54c32fba Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 08:57:53 -0800 Subject: [PATCH 20/49] feat: coinbase branding improvements --- typescript/create-onchain-agent/src/cli.ts | 2 +- .../templates/next/app/layout.tsx | 76 ++++++++++++++----- .../templates/next/app/page.tsx | 12 +-- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index a5339c7f7..af9afc248 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -145,7 +145,7 @@ async function init() { console.log(" - Visit the repo"); console.log(pc.blueBright(" - http://github.com/coinbase/agentkit")); console.log(" - Join the community"); - console.log(pc.blueBright(" - https://discord.gg/CDP")); + console.log(pc.blueBright(" - https://discord.gg/CDP\n")); } init().catch((e) => { diff --git a/typescript/create-onchain-agent/templates/next/app/layout.tsx b/typescript/create-onchain-agent/templates/next/app/layout.tsx index bf2ab2bfe..fcadbe5f8 100644 --- a/typescript/create-onchain-agent/templates/next/app/layout.tsx +++ b/typescript/create-onchain-agent/templates/next/app/layout.tsx @@ -15,8 +15,16 @@ export default function RootLayout({ {/* Header (Fixed Height) */} -
- 🤖 AgentKit +
+ Coinbase + + + AgentKit +
{/* Main Content (Dynamic, Grows but Doesn't Force Scroll) */} @@ -26,23 +34,53 @@ export default function RootLayout({ {/* Footer (Fixed Height) */} diff --git a/typescript/create-onchain-agent/templates/next/app/page.tsx b/typescript/create-onchain-agent/templates/next/app/page.tsx index 3bc2eab24..f5b51e104 100644 --- a/typescript/create-onchain-agent/templates/next/app/page.tsx +++ b/typescript/create-onchain-agent/templates/next/app/page.tsx @@ -38,10 +38,10 @@ export default function Home() { messages.map((msg, index) => (
{msg.text} @@ -71,10 +71,10 @@ export default function Home() { />
)) )} diff --git a/typescript/create-onchain-agent/templates/next/package.json b/typescript/create-onchain-agent/templates/next/package.json index 378d24697..adff341d6 100644 --- a/typescript/create-onchain-agent/templates/next/package.json +++ b/typescript/create-onchain-agent/templates/next/package.json @@ -16,6 +16,7 @@ "next": "14.2.15", "react": "^18", "react-dom": "^18", + "react-markdown": "^10.0.0", "viem": "^2.17.4", "wagmi": "^2.11.0" }, From dc4d1be083f007d1b186073115873ffb9acd995c Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 12:25:23 -0800 Subject: [PATCH 24/49] chore: added comments to files where agents get created --- .../next/app/api/agent/evm/cdp/route.ts | 104 +++++++++++++++--- .../next/app/api/agent/evm/privy/route.ts | 97 ++++++++++++++-- .../next/app/api/agent/evm/viem/route.ts | 98 +++++++++++++++-- .../next/app/api/agent/svm/privy/route.ts | 96 ++++++++++++++-- .../app/api/agent/svm/solanaKeypair/route.ts | 96 ++++++++++++++-- 5 files changed, 436 insertions(+), 55 deletions(-) diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts index 5c047451f..058131668 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts @@ -6,25 +6,74 @@ import { MemorySaver } from "@langchain/langgraph"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; import { AgentRequest, AgentResponse } from "@/app/types/api"; +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, + * allowing you to configure the agent to suit your needs. + * + * # Key Steps to Customize Your Agent:** + * + * 1. Select your LLM: + * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. + * + * 2. Set up your WalletProvider: + * - Configure the `CdpWalletProvider` to manage blockchain interactions. + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 3️. Set up your ActionProviders: + * - ActionProviders define what your agent can do. + * - Choose from built-in providers or create your own: + * - 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 + * + * 4. Instantiate your Agent: + * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Learn more about available WalletProviders & ActionProviders. + * - Experiment with custom ActionProviders for your unique 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 + */ + +// The agent let agent: ReturnType; -async function initializeAgent(): Promise> { +/** + * 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. + */ +async function getOrInitializeAgent(): Promise> { + // If agent has already been initialized, return it if (agent) { return agent; } try { - // Initialize LLM + // Initialize LLM: https://platform.openai.com/docs/models#gpt-4o const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); - // Initialize WalletProvider + // 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?.replace(/\\n/g, "\n"), + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, networkId: process.env.NETWORK_ID || "base-sepolia", }); - - // Initialize AgentKit + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions const agentkit = await AgentKit.from({ walletProvider, actionProviders: [ @@ -34,11 +83,11 @@ async function initializeAgent(): Promise> { erc20ActionProvider(), cdpApiActionProvider({ apiKeyName: process.env.CDP_API_KEY_NAME, - apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY?.replace(/\\n/g, "\n"), + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, }), cdpWalletActionProvider({ apiKeyName: process.env.CDP_API_KEY_NAME, - apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY?.replace(/\\n/g, "\n"), + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, }), ], }); @@ -59,23 +108,50 @@ async function initializeAgent(): Promise> { } } +/** + * Handles incoming POST requests to interact with the AgentKit-powered AI agent. + * This function processes user messages and streams responses from the agent. + * + * @function POST + * @param {Request & { json: () => Promise }} req - The incoming request object containing the user message. + * @returns {Promise>} JSON response containing the AI-generated reply or an error message. + * + * @description Sends a single message to the agent and returns the agents' final response. + * + * @example + * const response = await fetch("/api/agent", { + * method: "POST", + * headers: { "Content-Type": "application/json" }, + * body: JSON.stringify({ userMessage: input }), + * }); + */ export async function POST(req: Request & { json: () => Promise }): Promise> { try { + // 1️. Extract user message from the request body const { userMessage } = await req.json(); - const agent = await initializeAgent(); - const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + // 2. Get the agent + const agent = await getOrInitializeAgent(); - let responseMessage = ""; + // 3.Start streaming the agent's response + const stream = await agent.stream( + { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent + { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + ); + + // 4️. Process the streamed response chunks into a single message + let agentResponse = ""; for await (const chunk of stream) { if ("agent" in chunk) { - responseMessage += chunk.agent.messages[0].content; + agentResponse += chunk.agent.messages[0].content; } } - return NextResponse.json({ response: responseMessage }); + // 5️. Return the final response + return NextResponse.json({ response: agentResponse }); } catch (error) { console.error("Error processing request:", error); - return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + return NextResponse.json({ error: "Failed to process message" }); } } + diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts index eae67b6fb..476004604 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts @@ -6,18 +6,68 @@ import { createReactAgent } from "@langchain/langgraph/prebuilt"; import { ChatOpenAI } from "@langchain/openai"; import { NextResponse } from "next/server"; +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, + * allowing you to configure the agent to suit your needs. + * + * # Key Steps to Customize Your Agent:** + * + * 1. Select your LLM: + * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. + * + * 2. Set up your WalletProvider: + * - Configure the `CdpWalletProvider` to manage blockchain interactions. + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 3️. Set up your ActionProviders: + * - ActionProviders define what your agent can do. + * - Choose from built-in providers or create your own: + * - 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 + * + * 4. Instantiate your Agent: + * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Learn more about available WalletProviders & ActionProviders. + * - Experiment with custom ActionProviders for your unique 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 + */ + +// The agent let agent: ReturnType; -async function initializeAgent(): Promise> { +/** + * 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. + */ +async function getOrInitializeAgent(): Promise> { + // If agent has already been initialized, return it if (agent) { return agent; } try { - // Initialize LLM + // Initialize LLM: https://platform.openai.com/docs/models#gpt-4o const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); - // Initialize WalletProvider + + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management const walletProvider = await PrivyWalletProvider.configureWithWallet({ appId: process.env.PRIVY_APP_ID as string, appSecret: process.env.PRIVY_APP_SECRET as string, @@ -27,7 +77,7 @@ async function initializeAgent(): Promise> { authorizationKeyId: process.env.PRIVY_WALLET_AUTHORIZATION_KEY_ID }); - // Initialize AgentKit + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions const agentkit = await AgentKit.from({ walletProvider, actionProviders: [ @@ -54,23 +104,50 @@ async function initializeAgent(): Promise> { } } +/** + * Handles incoming POST requests to interact with the AgentKit-powered AI agent. + * This function processes user messages and streams responses from the agent. + * + * @function POST + * @param {Request & { json: () => Promise }} req - The incoming request object containing the user message. + * @returns {Promise>} JSON response containing the AI-generated reply or an error message. + * + * @description Sends a single message to the agent and returns the agents' final response. + * + * @example + * const response = await fetch("/api/agent", { + * method: "POST", + * headers: { "Content-Type": "application/json" }, + * body: JSON.stringify({ userMessage: input }), + * }); + */ export async function POST(req: Request & { json: () => Promise }): Promise> { try { + // 1️. Extract user message from the request body const { userMessage } = await req.json(); - const agent = await initializeAgent(); - const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + // 2. Get the agent + const agent = await getOrInitializeAgent(); - let responseMessage = ""; + // 3.Start streaming the agent's response + const stream = await agent.stream( + { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent + { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + ); + + // 4️. Process the streamed response chunks into a single message + let agentResponse = ""; for await (const chunk of stream) { if ("agent" in chunk) { - responseMessage += chunk.agent.messages[0].content; + agentResponse += chunk.agent.messages[0].content; } } - return NextResponse.json({ response: responseMessage }); + // 5️. Return the final response + return NextResponse.json({ response: agentResponse }); } catch (error) { console.error("Error processing request:", error); - return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + return NextResponse.json({ error: "Failed to process message" }); } } + diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts index 6e86bc83b..07a393cf8 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts @@ -7,23 +7,72 @@ import { createReactAgent } from "@langchain/langgraph/prebuilt"; import { AgentRequest, AgentResponse } from "@/app/types/api"; import { createWalletClient, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; + +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, + * allowing you to configure the agent to suit your needs. + * + * # Key Steps to Customize Your Agent:** + * + * 1. Select your LLM: + * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. + * + * 2. Set up your WalletProvider: + * - Configure the `CdpWalletProvider` to manage blockchain interactions. + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 3️. Set up your ActionProviders: + * - ActionProviders define what your agent can do. + * - Choose from built-in providers or create your own: + * - 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 + * + * 4. Instantiate your Agent: + * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Learn more about available WalletProviders & ActionProviders. + * - Experiment with custom ActionProviders for your unique 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 + */ + +// The agent let agent: ReturnType; -async function initializeAgent(): Promise> { +/** + * 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. + */ +async function getOrInitializeAgent(): Promise> { + // If agent has already been initialized, return it if (agent) { return agent; } try { - // Initialize LLM + // Initialize LLM: https://platform.openai.com/docs/models#gpt-4o const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management const account = privateKeyToAccount( process.env.PRIVATE_KEY as `0x${string}` ); const networkId = process.env.NETWORK_ID as string; - - // Initialize WalletProvider const client = createWalletClient({ account, chain: NETWORK_ID_TO_VIEM_CHAIN[networkId], @@ -31,7 +80,7 @@ async function initializeAgent(): Promise> { }); const walletProvider = await new ViemWalletProvider(client); - // Initialize AgentKit + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions const agentkit = await AgentKit.from({ walletProvider, actionProviders: [ @@ -58,23 +107,50 @@ async function initializeAgent(): Promise> { } } +/** + * Handles incoming POST requests to interact with the AgentKit-powered AI agent. + * This function processes user messages and streams responses from the agent. + * + * @function POST + * @param {Request & { json: () => Promise }} req - The incoming request object containing the user message. + * @returns {Promise>} JSON response containing the AI-generated reply or an error message. + * + * @description Sends a single message to the agent and returns the agents' final response. + * + * @example + * const response = await fetch("/api/agent", { + * method: "POST", + * headers: { "Content-Type": "application/json" }, + * body: JSON.stringify({ userMessage: input }), + * }); + */ export async function POST(req: Request & { json: () => Promise }): Promise> { try { + // 1️. Extract user message from the request body const { userMessage } = await req.json(); - const agent = await initializeAgent(); - const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + // 2. Get the agent + const agent = await getOrInitializeAgent(); - let responseMessage = ""; + // 3.Start streaming the agent's response + const stream = await agent.stream( + { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent + { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + ); + + // 4️. Process the streamed response chunks into a single message + let agentResponse = ""; for await (const chunk of stream) { if ("agent" in chunk) { - responseMessage += chunk.agent.messages[0].content; + agentResponse += chunk.agent.messages[0].content; } } - return NextResponse.json({ response: responseMessage }); + // 5️. Return the final response + return NextResponse.json({ response: agentResponse }); } catch (error) { console.error("Error processing request:", error); - return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + return NextResponse.json({ error: "Failed to process message" }); } } + diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts index 98d09c586..51f1cf5d3 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts @@ -6,18 +6,67 @@ import { createReactAgent } from "@langchain/langgraph/prebuilt"; import { ChatOpenAI } from "@langchain/openai"; import { NextResponse } from "next/server"; +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, + * allowing you to configure the agent to suit your needs. + * + * # Key Steps to Customize Your Agent:** + * + * 1. Select your LLM: + * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. + * + * 2. Set up your WalletProvider: + * - Configure the `CdpWalletProvider` to manage blockchain interactions. + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 3️. Set up your ActionProviders: + * - ActionProviders define what your agent can do. + * - Choose from built-in providers or create your own: + * - 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 + * + * 4. Instantiate your Agent: + * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Learn more about available WalletProviders & ActionProviders. + * - Experiment with custom ActionProviders for your unique 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 + */ + +// The agent let agent: ReturnType; -async function initializeAgent(): Promise> { +/** + * 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. + */ +async function getOrInitializeAgent(): Promise> { + // If agent has already been initialized, return it if (agent) { return agent; } try { - // Initialize LLM + // Initialize LLM: https://platform.openai.com/docs/models#gpt-4o const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); - // Initialize WalletProvider + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management const walletProvider = await PrivyWalletProvider.configureWithWallet({ appId: process.env.PRIVY_APP_ID as string, appSecret: process.env.PRIVY_APP_SECRET as string, @@ -28,7 +77,7 @@ async function initializeAgent(): Promise> { networkId: process.env.NETWORK_ID, }); - // Initialize AgentKit + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions const agentkit = await AgentKit.from({ walletProvider, actionProviders: [ @@ -54,23 +103,50 @@ async function initializeAgent(): Promise> { } } +/** + * Handles incoming POST requests to interact with the AgentKit-powered AI agent. + * This function processes user messages and streams responses from the agent. + * + * @function POST + * @param {Request & { json: () => Promise }} req - The incoming request object containing the user message. + * @returns {Promise>} JSON response containing the AI-generated reply or an error message. + * + * @description Sends a single message to the agent and returns the agents' final response. + * + * @example + * const response = await fetch("/api/agent", { + * method: "POST", + * headers: { "Content-Type": "application/json" }, + * body: JSON.stringify({ userMessage: input }), + * }); + */ export async function POST(req: Request & { json: () => Promise }): Promise> { try { + // 1️. Extract user message from the request body const { userMessage } = await req.json(); - const agent = await initializeAgent(); - const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + // 2. Get the agent + const agent = await getOrInitializeAgent(); + + // 3.Start streaming the agent's response + const stream = await agent.stream( + { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent + { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + ); - let responseMessage = ""; + // 4️. Process the streamed response chunks into a single message + let agentResponse = ""; for await (const chunk of stream) { if ("agent" in chunk) { - responseMessage += chunk.agent.messages[0].content; + agentResponse += chunk.agent.messages[0].content; } } - return NextResponse.json({ response: responseMessage }); + // 5️. Return the final response + return NextResponse.json({ response: agentResponse }); } catch (error) { console.error("Error processing request:", error); - return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + return NextResponse.json({ error: "Failed to process message" }); } } + diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts index 44ec9ac73..e01c0669e 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts @@ -8,15 +8,64 @@ import { Keypair } from "@solana/web3.js"; import bs58 from "bs58"; import { NextResponse } from "next/server"; +/** + * AgentKit Integration Route + * + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, + * allowing you to configure the agent to suit your needs. + * + * # Key Steps to Customize Your Agent:** + * + * 1. Select your LLM: + * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. + * + * 2. Set up your WalletProvider: + * - Configure the `CdpWalletProvider` to manage blockchain interactions. + * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers + * + * 3️. Set up your ActionProviders: + * - ActionProviders define what your agent can do. + * - Choose from built-in providers or create your own: + * - 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 + * + * 4. Instantiate your Agent: + * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. + * + * # Next Steps: + * - Explore the AgentKit README: https://github.com/coinbase/agentkit + * - Learn more about available WalletProviders & ActionProviders. + * - Experiment with custom ActionProviders for your unique 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 + */ + +// The agent let agent: ReturnType; -async function initializeAgent(): Promise> { +/** + * 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. + */ +async function getOrInitializeAgent(): Promise> { + // If agent has already been initialized, return it if (agent) { return agent; } try { - // Initialize LLM + // Initialize LLM: https://platform.openai.com/docs/models#gpt-4o const llm = new ChatOpenAI({ model: "gpt-4o-mini" }); // Setup Private Key @@ -29,7 +78,7 @@ async function initializeAgent(): Promise> { console.log(`Store the private key in your .env for future reuse: ${solanaPrivateKey}`); } - // Initialize WalletProvider + // 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; @@ -40,7 +89,7 @@ async function initializeAgent(): Promise> { walletProvider = await SolanaKeypairWalletProvider.fromNetwork(network, solanaPrivateKey); } - // Initialize AgentKit + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions const agentkit = await AgentKit.from({ walletProvider, actionProviders: [splActionProvider(), walletActionProvider(), jupiterActionProvider()], @@ -62,23 +111,50 @@ async function initializeAgent(): Promise> { } } +/** + * Handles incoming POST requests to interact with the AgentKit-powered AI agent. + * This function processes user messages and streams responses from the agent. + * + * @function POST + * @param {Request & { json: () => Promise }} req - The incoming request object containing the user message. + * @returns {Promise>} JSON response containing the AI-generated reply or an error message. + * + * @description Sends a single message to the agent and returns the agents' final response. + * + * @example + * const response = await fetch("/api/agent", { + * method: "POST", + * headers: { "Content-Type": "application/json" }, + * body: JSON.stringify({ userMessage: input }), + * }); + */ export async function POST(req: Request & { json: () => Promise }): Promise> { try { + // 1️. Extract user message from the request body const { userMessage } = await req.json(); - const agent = await initializeAgent(); - const stream = await agent.stream({ messages: [{ content: userMessage, role: "user" }] }, {configurable: {thread_id: 'AgentKit Discussion'}}); + // 2. Get the agent + const agent = await getOrInitializeAgent(); + + // 3.Start streaming the agent's response + const stream = await agent.stream( + { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent + { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + ); - let responseMessage = ""; + // 4️. Process the streamed response chunks into a single message + let agentResponse = ""; for await (const chunk of stream) { if ("agent" in chunk) { - responseMessage += chunk.agent.messages[0].content; + agentResponse += chunk.agent.messages[0].content; } } - return NextResponse.json({ response: responseMessage }); + // 5️. Return the final response + return NextResponse.json({ response: agentResponse }); } catch (error) { console.error("Error processing request:", error); - return NextResponse.json({ error: "Failed to process message" }, { status: 500 }); + return NextResponse.json({ error: "Failed to process message" }); } } + From 8a4871cb57a8898be020a4e6d46ec0030c5d8acd Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 12:33:24 -0800 Subject: [PATCH 25/49] chore: refactored useAgent and commented --- .../templates/next/app/hooks/useAgent.ts | 73 +++++++++++++++---- .../templates/next/app/types/api.ts | 2 +- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts b/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts index c6dab1aaf..e85921bd9 100644 --- a/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts +++ b/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts @@ -1,32 +1,73 @@ import { useState } from "react"; +import { AgentRequest, AgentResponse } from "../types/api"; +/** + * Sends a user message to the AgentKit backend API and retrieves the agent's response. + * + * @async + * @function callAgentAPI + * @param {string} userMessage - The message sent by the user. + * @returns {Promise} The agent's response message or `null` if an error occurs. + * + * @throws {Error} Logs an error if the request fails. + */ +async function messageAgent(userMessage: string): Promise { + try { + const response = await fetch("/api/agent", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ userMessage } as AgentRequest), + }); + + const data = (await response.json()) as AgentResponse; + return data.response ?? data.error ?? null; + } catch (error) { + console.error("Error communicating with agent:", error); + return null; + } +} + +/** + * + * This hook manages interactions with the AI agent by making REST calls to the backend. + * It also stores the local conversation state, tracking messages sent by the user and + * responses from the agent. + * + * #### How It Works + * - `sendMessage(input)` sends a message to `/api/agent` and updates state. + * - `messages` stores the chat history. + * - `isThinking` tracks whether the agent is processing a response. + * + * #### See Also + * - The API logic in `/api/agent.ts` + * + * @returns {Object} An object containing: + * - `messages`: The conversation history. + * - `sendMessage`: A function to send a new message. + * - `isThinking`: Boolean indicating if the agent is processing a response. + */ export function useAgent() { const [messages, setMessages] = useState<{ text: string; sender: "user" | "agent" }[]>([]); const [isThinking, setIsThinking] = useState(false); + /** + * Sends a user message, updates local state, and retrieves the agent's response. + * + * @param {string} input - The message from the user. + */ const sendMessage = async (input: string) => { if (!input.trim()) return; setMessages((prev) => [...prev, { text: input, sender: "user" }]); setIsThinking(true); - try { - const response = await fetch("/api/agent", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ userMessage: input }), - }); - - const data = await response.json(); - - if (data.response) { - setMessages((prev) => [...prev, { text: data.response, sender: "agent" }]); - } - } catch (error) { - console.error("Error communicating with agent:", error); - } finally { - setIsThinking(false); + const responseMessage = await messageAgent(input); + + if (responseMessage) { + setMessages((prev) => [...prev, { text: responseMessage, sender: "agent" }]); } + + setIsThinking(false); }; return { messages, sendMessage, isThinking }; diff --git a/typescript/create-onchain-agent/templates/next/app/types/api.ts b/typescript/create-onchain-agent/templates/next/app/types/api.ts index 888b65e47..62d1b39b6 100644 --- a/typescript/create-onchain-agent/templates/next/app/types/api.ts +++ b/typescript/create-onchain-agent/templates/next/app/types/api.ts @@ -1,4 +1,4 @@ export type AgentRequest = { userMessage: string } -export type AgentResponse = { response: string } | { error: string} +export type AgentResponse = { response?: string, error?: string } From 5c80f9f25c9f2adb81e15ce9700f0e47ebd2d7aa Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 12:44:07 -0800 Subject: [PATCH 26/49] chore: added base project README --- .../templates/next/README.md | 70 +++++++++++++------ .../next/app/api/agent/evm/cdp/route.ts | 1 - .../next/app/api/agent/evm/privy/route.ts | 1 - .../next/app/api/agent/evm/viem/route.ts | 1 - .../next/app/api/agent/svm/privy/route.ts | 1 - .../app/api/agent/svm/solanaKeypair/route.ts | 1 - 6 files changed, 47 insertions(+), 28 deletions(-) diff --git a/typescript/create-onchain-agent/templates/next/README.md b/typescript/create-onchain-agent/templates/next/README.md index 5684afeaf..e7bc54f18 100644 --- a/typescript/create-onchain-agent/templates/next/README.md +++ b/typescript/create-onchain-agent/templates/next/README.md @@ -1,39 +1,63 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-onchain-agent`](). +# AgentKit Project + +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-onchain-agent`](). +It integrates [AgentKit](https://github.com/coinbase/agentkit) to provide AI-driven interactions with on-chain capabilities. ## Getting Started First, install dependencies: -```bash -npm install -# or -yarn install -# or -pnpm install -# or -bun install -``` +`npm install` Next, run the development server: -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` +`npm run dev` + +Open [http://localhost:3000](http://localhost:3000) in your browser to see the project. + + +## Configuring Your Agent + +You can modify your configuration of the agent. By default, your agent configuration occurs in the `/api/agent/route.ts` file. + +### 1. Select Your LLM +Modify the `ChatOpenAI` instantiation to use the model of your choice. +For OpenAI models, refer to: [OpenAI Model Documentation](https://platform.openai.com/docs/models) + +### 2. Select Your WalletProvider +AgentKit requires a **WalletProvider** to interact with blockchain networks. -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +- Documentation: [Wallet Providers](https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers) -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +### 3. Select Your ActionProviders +ActionProviders define what your agent can do. You can use built-in providers or create your own. +- Built-in ActionProviders: [Available Providers](https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers) +- Custom ActionProviders: [Creating a Provider](https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider) + +--- + +## Next Steps + +- Explore the AgentKit README: [AgentKit Documentation](https://github.com/coinbase/agentkit) +- Learn more about available WalletProviders & ActionProviders. +- Experiment with custom ActionProviders for your specific use case. + +--- ## Learn More -To learn more about AgentKit, see our [documentation](https://docs.cdp.coinbase.com/agentkit/docs/welcome). +- [Learn more about CDP](https://docs.cdp.coinbase.com/) +- [Learn more about AgentKit](https://docs.cdp.coinbase.com/agentkit/docs/welcome) +- [Learn more about Next.js](https://nextjs.org/docs) +- [Learn more about Tailwind CSS](https://tailwindcss.com/docs) + +--- + +## Contributing + +Interested in contributing to AgentKit? Follow the contribution guide: -To learn more about Next.js, see the [Next.js documentation](https://nextjs.org/docs). +- [Contribution Guide](https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md) +- Join the discussion on [Discord](https://discord.gg/CDP) diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts index 058131668..1d05dd6f0 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts @@ -19,7 +19,6 @@ import { AgentRequest, AgentResponse } from "@/app/types/api"; * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. * * 2. Set up your WalletProvider: - * - Configure the `CdpWalletProvider` to manage blockchain interactions. * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers * * 3️. Set up your ActionProviders: diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts index 476004604..d22925c5b 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts @@ -19,7 +19,6 @@ import { NextResponse } from "next/server"; * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. * * 2. Set up your WalletProvider: - * - Configure the `CdpWalletProvider` to manage blockchain interactions. * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers * * 3️. Set up your ActionProviders: diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts index 07a393cf8..e5cd03540 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts @@ -21,7 +21,6 @@ import { privateKeyToAccount } from "viem/accounts"; * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. * * 2. Set up your WalletProvider: - * - Configure the `CdpWalletProvider` to manage blockchain interactions. * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers * * 3️. Set up your ActionProviders: diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts index 51f1cf5d3..fb3b3395f 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts @@ -19,7 +19,6 @@ import { NextResponse } from "next/server"; * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. * * 2. Set up your WalletProvider: - * - Configure the `CdpWalletProvider` to manage blockchain interactions. * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers * * 3️. Set up your ActionProviders: diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts index e01c0669e..ac27bd2e6 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts @@ -21,7 +21,6 @@ import { NextResponse } from "next/server"; * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. * * 2. Set up your WalletProvider: - * - Configure the `CdpWalletProvider` to manage blockchain interactions. * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers * * 3️. Set up your ActionProviders: From 67f3f2d2523e53020a367ee7f481c64ee812bed0 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 13:08:43 -0800 Subject: [PATCH 27/49] feat: added wallet provider descriptions to cli --- typescript/create-onchain-agent/src/cli.ts | 34 +++++++++++-------- .../create-onchain-agent/src/constants.ts | 4 +-- typescript/create-onchain-agent/src/utils.ts | 9 ++++- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index af9afc248..58b75edec 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -4,13 +4,14 @@ import ora from "ora"; import path from "path"; import pc from "picocolors"; import prompts from "prompts"; -import { Networks, NetworkToWalletProviders, NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS } from "./constants.js"; +import { Networks, NetworkToWalletProviders } from "./constants.js"; import { Network, WalletProviderChoice } from "./types.js"; import { copyTemplate } from "./fileSystem.js"; import { handleSelection, isValidPackageName, - toValidPackageName + toValidPackageName, + getWalletProviders } from "./utils.js"; async function init() { @@ -78,19 +79,24 @@ async function init() { { type: (prev, { network }) => !network || NetworkToWalletProviders[network as Network].length > 1 ? "select" : null, name: "walletProvider", - message: pc.reset("Choose a wallet provider:"), + message: (prev, { network }) => { + const walletDescriptions: Record = { + CDP: "Uses Coinbase Developer Platform (CDP)'s managed wallet.", + Viem: "Client-side Ethereum wallet.", + Privy: "Authentication and wallet infrastructure.", + SolanaKeypair: "Client-side Solana wallet.", + }; + + const providerDescriptions = getWalletProviders(network) + .map((provider) => ` - ${provider}: ${walletDescriptions[provider]}`) + .join("\n"); + + return pc.reset(`Choose a wallet provider:\n${providerDescriptions}\n`); + }, choices: (prev, { network, chainId }) => { - let walletProviderChoises: WalletProviderChoice[]; - - if (network) { - walletProviderChoises = NetworkToWalletProviders[network as Network]; - } - else { - walletProviderChoises = NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS; - } - - return walletProviderChoises.map((provider) => ({ - title: provider === walletProviderChoises[0] ? `${provider} (default)` : provider, + const walletProviders = getWalletProviders(network); + return getWalletProviders(network).map((provider) => ({ + title: provider === walletProviders[0] ? `${provider} (default)` : provider, value: provider, })) }, diff --git a/typescript/create-onchain-agent/src/constants.ts b/typescript/create-onchain-agent/src/constants.ts index 2e457e28f..be177b654 100644 --- a/typescript/create-onchain-agent/src/constants.ts +++ b/typescript/create-onchain-agent/src/constants.ts @@ -12,9 +12,9 @@ export const SVM_NETWORKS: Set = new Set([ "solana-mainnet", "solana-devnet", "solana-testnet" ]); -export const CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["CDP", "Viem", "Privy"]; +const CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["CDP", "Viem", "Privy"]; +const SVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["SolanaKeypair", "Privy"]; export const NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["Viem", "Privy"]; -export const SVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["SolanaKeypair", "Privy"]; export const NetworkToWalletProviders: Record = { "arbitrum-mainnet": CDP_SUPPORTED_EVM_WALLET_PROVIDERS, diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 711fbba64..c84b0f520 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -1,7 +1,7 @@ import fs from 'fs/promises'; import path from 'path'; import { EVMNetwork, Network, SVMNetwork, WalletProviderChoice } from './types'; -import { EVM_NETWORKS, SVM_NETWORKS, WalletProviderRouteConfigurations } from './constants.js'; +import { EVM_NETWORKS, NetworkToWalletProviders, NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, SVM_NETWORKS, WalletProviderRouteConfigurations } from './constants.js'; export function getNetworkFamily(network: EVMNetwork | SVMNetwork) { return EVM_NETWORKS.has(network as EVMNetwork) ? 'EVM' : SVM_NETWORKS.has(network as SVMNetwork) ? 'SVM' : undefined; @@ -75,6 +75,13 @@ export function detectPackageManager(): string { return 'npm'; // default to npm if unable to detect } +export const getWalletProviders = (network?: Network) => { + if (network) { + return NetworkToWalletProviders[network]; + } + return NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS; +} + export async function handleSelection(root: string, walletProvider: WalletProviderChoice, network?: Network, chainId?: string) { const agentDir = path.join(root, "app", "api", "agent"); From 8ab6f7f287b398db23eb31d665f188318c43637d Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 13:33:04 -0800 Subject: [PATCH 28/49] bump: 0.1.0 --- typescript/create-onchain-agent/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index bc7c6d1f6..f0ce57cb3 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -1,7 +1,7 @@ { "name": "create-onchain-agent", "description": "Instantly create onchain-agent applications with AgentKit.", - "version": "0.0.1", + "version": "0.1.0", "license": "MIT", "scripts": { "build": "npm run clean && npm run build:esm+types", From 7c5094fd1fd4522cca610327058273a60e520d8c Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 14:04:09 -0800 Subject: [PATCH 29/49] fix: added files for npm --- typescript/create-onchain-agent/.npmignore | 4 + typescript/create-onchain-agent/README.md | 84 ++++++++++++++++++++ typescript/create-onchain-agent/package.json | 4 +- 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 typescript/create-onchain-agent/.npmignore create mode 100644 typescript/create-onchain-agent/README.md diff --git a/typescript/create-onchain-agent/.npmignore b/typescript/create-onchain-agent/.npmignore new file mode 100644 index 000000000..1eed77ab4 --- /dev/null +++ b/typescript/create-onchain-agent/.npmignore @@ -0,0 +1,4 @@ +# Source +src/** +./node_modules/ +__tests__/** \ No newline at end of file diff --git a/typescript/create-onchain-agent/README.md b/typescript/create-onchain-agent/README.md new file mode 100644 index 000000000..c1ffebbf9 --- /dev/null +++ b/typescript/create-onchain-agent/README.md @@ -0,0 +1,84 @@ +# @coinbase/create-onchain-agent + +## Overview + +`@coinbase/create-onchain-agent` is a CLI tool 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. + +## Installation & Usage + +To use `create-onchain-agent`, simply run: + +```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 blockchain networks**. +- **Select your preferred wallet provider**. +- **Preconfigured with Next.js, React, Tailwind CSS, and ESLint**. +- **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. **Network**: Choose from available blockchain networks. +4. **Chain ID**: Specify if using a custom network. +5. **Wallet Provider**: Select a preferred method for wallet management. + +After answering the prompts, the CLI will: + +- Generate the project structure. +- Copy necessary template files. +- Configure the selected settings. +- Display next steps to get started. + +## What's Included + +The generated project includes: + +- **AgentKit** - The core framework for building onchain agents. +- **React & Next.js** - Web UI for interaction. +- **Tailwind CSS** - Preconfigured styling. +- **ESLint** - Linting and code quality tools. + +## Getting Started + +Once your project is created, navigate into the directory and install dependencies: + +```sh +cd your-project-name +npm install +``` + +Then, configure your environment variables: + +```sh +mv .env.local .env +``` + +Run the development server: + +```sh +npm run dev +``` + +## Documentation & Support + +- **Docs:** [https://docs.cdp.coinbase.com/agentkit/docs/welcome](https://docs.cdp.coinbase.com/agentkit/docs/welcome) +- **GitHub Repo:** [http://github.com/coinbase/agentkit](http://github.com/coinbase/agentkit) +- **Community & Support:** [https://discord.gg/CDP](https://discord.gg/CDP) + +## License + +This project is licensed under the **MIT License**. + +--- + +For any issues, please open a GitHub issue or join the Coinbase Developer Platform Discord for support. + diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index f0ce57cb3..a0cf3d042 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -1,7 +1,7 @@ { - "name": "create-onchain-agent", + "name": "@coinbase/create-onchain-agent", "description": "Instantly create onchain-agent applications with AgentKit.", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "scripts": { "build": "npm run clean && npm run build:esm+types", From 406de3e96c85984363fb391397276105c9fb41b1 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 14:16:50 -0800 Subject: [PATCH 30/49] chore: improved readme --- typescript/create-onchain-agent/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript/create-onchain-agent/README.md b/typescript/create-onchain-agent/README.md index c1ffebbf9..9e818fa26 100644 --- a/typescript/create-onchain-agent/README.md +++ b/typescript/create-onchain-agent/README.md @@ -1,10 +1,10 @@ -# @coinbase/create-onchain-agent +# create-onchain-agent ## Overview -`@coinbase/create-onchain-agent` is a CLI tool 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 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. -## Installation & Usage +## Usage To use `create-onchain-agent`, simply run: From f440741c1d04ddcf6d7d490ff3e5ed4b9614fb17 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Thu, 20 Feb 2025 14:18:09 -0800 Subject: [PATCH 31/49] chore: removed @coinbase from package.json --- typescript/create-onchain-agent/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index a0cf3d042..26bcca397 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -1,5 +1,5 @@ { - "name": "@coinbase/create-onchain-agent", + "name": "create-onchain-agent", "description": "Instantly create onchain-agent applications with AgentKit.", "version": "0.1.1", "license": "MIT", From 949ac3e326b8d96e31414fc998e600591f9ed81c Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 15:23:23 -0800 Subject: [PATCH 32/49] chore: added basis for prettier and lint --- package-lock.json | 1 + package.json | 1 + typescript/create-onchain-agent/.eslintrc.json | 4 ++++ typescript/create-onchain-agent/.prettierignore | 7 +++++++ typescript/create-onchain-agent/.prettierrc | 11 +++++++++++ typescript/create-onchain-agent/package.json | 6 +++++- 6 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 typescript/create-onchain-agent/.eslintrc.json create mode 100644 typescript/create-onchain-agent/.prettierignore create mode 100644 typescript/create-onchain-agent/.prettierrc diff --git a/package-lock.json b/package-lock.json index 5422befeb..bffe1619e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "license": "Apache-2.0", "workspaces": [ "typescript/agentkit", + "typescript/agentkit/create-onchain-agent", "typescript/framework-extensions/langchain", "typescript/examples/langchain-cdp-chatbot", "typescript/examples/langchain-twitter-chatbot", diff --git a/package.json b/package.json index be9434790..042ffa0d9 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "private": true, "workspaces": [ "typescript/agentkit", + "typescript/agentkit/create-onchain-agent", "typescript/framework-extensions/langchain", "typescript/examples/langchain-cdp-chatbot", "typescript/examples/langchain-twitter-chatbot", diff --git a/typescript/create-onchain-agent/.eslintrc.json b/typescript/create-onchain-agent/.eslintrc.json new file mode 100644 index 000000000..91571ba7a --- /dev/null +++ b/typescript/create-onchain-agent/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": ["../../.eslintrc.base.json"] +} diff --git a/typescript/create-onchain-agent/.prettierignore b/typescript/create-onchain-agent/.prettierignore new file mode 100644 index 000000000..cc592ae32 --- /dev/null +++ b/typescript/create-onchain-agent/.prettierignore @@ -0,0 +1,7 @@ +docs/ +dist/ +coverage/ +.github/ +src/client +**/**/*.json +*.md \ No newline at end of file diff --git a/typescript/create-onchain-agent/.prettierrc b/typescript/create-onchain-agent/.prettierrc new file mode 100644 index 000000000..ffb416b74 --- /dev/null +++ b/typescript/create-onchain-agent/.prettierrc @@ -0,0 +1,11 @@ +{ + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "avoid", + "printWidth": 100, + "proseWrap": "never" +} diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index 26bcca397..1c98c98df 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -1,11 +1,15 @@ { "name": "create-onchain-agent", "description": "Instantly create onchain-agent applications with AgentKit.", - "version": "0.1.1", + "version": "0.1.0", "license": "MIT", "scripts": { "build": "npm run clean && npm run build:esm+types", "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", + "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"", + "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"", + "lint": "eslint -c .eslintrc.json *.ts", + "lint:fix": "eslint -c .eslintrc.json *.ts --fix", "check:types": "tsc --noEmit", "clean": "rm -rf dist tsconfig.tsbuildinfo", "dev": "node --loader ts-node/esm src/cli.ts" From 04399611a5ee698f1adc589f9990f1102c15ed3d Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 15:26:29 -0800 Subject: [PATCH 33/49] chore: cli readme feedback --- typescript/create-onchain-agent/README.md | 27 +++++++---------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/typescript/create-onchain-agent/README.md b/typescript/create-onchain-agent/README.md index 9e818fa26..4c38e5ee3 100644 --- a/typescript/create-onchain-agent/README.md +++ b/typescript/create-onchain-agent/README.md @@ -4,6 +4,13 @@ `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. +## Prerequisites + +Before using `create-onchain-agent`, ensure you have the following installed: + +- **Node.js** (v18 or later) – [Download here](https://nodejs.org/) +- **npm** (v9 or later) – Comes bundled with Node.js + ## Usage To use `create-onchain-agent`, simply run: @@ -38,21 +45,12 @@ After answering the prompts, the CLI will: - Configure the selected settings. - Display next steps to get started. -## What's Included - -The generated project includes: - -- **AgentKit** - The core framework for building onchain agents. -- **React & Next.js** - Web UI for interaction. -- **Tailwind CSS** - Preconfigured styling. -- **ESLint** - Linting and code quality tools. - ## Getting Started Once your project is created, navigate into the directory and install dependencies: ```sh -cd your-project-name +cd my-project npm install ``` @@ -73,12 +71,3 @@ npm run dev - **Docs:** [https://docs.cdp.coinbase.com/agentkit/docs/welcome](https://docs.cdp.coinbase.com/agentkit/docs/welcome) - **GitHub Repo:** [http://github.com/coinbase/agentkit](http://github.com/coinbase/agentkit) - **Community & Support:** [https://discord.gg/CDP](https://discord.gg/CDP) - -## License - -This project is licensed under the **MIT License**. - ---- - -For any issues, please open a GitHub issue or join the Coinbase Developer Platform Discord for support. - From 8ce238611ad8a0de7af118b19caf0fdfa77b1e7d Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 15:30:37 -0800 Subject: [PATCH 34/49] chore: template readme feedback --- .../templates/next/README.md | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/typescript/create-onchain-agent/templates/next/README.md b/typescript/create-onchain-agent/templates/next/README.md index e7bc54f18..33be04f06 100644 --- a/typescript/create-onchain-agent/templates/next/README.md +++ b/typescript/create-onchain-agent/templates/next/README.md @@ -1,48 +1,52 @@ -# AgentKit Project +# Onchain Agent Powered by AgentKit -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-onchain-agent`](). -It integrates [AgentKit](https://github.com/coinbase/agentkit) to provide AI-driven interactions with on-chain capabilities. +This is a [Next.js](https://nextjs.org) project bootstrapped with `create-onchain-agent`. +It integrates [AgentKit](https://github.com/coinbase/agentkit) to provide AI-driven interactions with on-chain capabilities. ## Getting Started First, install dependencies: -`npm install` +```sh +npm install +``` + +Then, configure your environment variables: -Next, run the development server: +```sh +mv .env.local .env +``` -`npm run dev` +Run the development server: + +```sh +npm run dev +``` Open [http://localhost:3000](http://localhost:3000) in your browser to see the project. ## Configuring Your Agent -You can modify your configuration of the agent. By default, your agent configuration occurs in the `/api/agent/route.ts` file. +You can [modify your configuration](https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#usage) of the agent. By default, your agent configuration occurs in the `/api/agent/route.ts` file. ### 1. Select Your LLM -Modify the `ChatOpenAI` instantiation to use the model of your choice. -For OpenAI models, refer to: [OpenAI Model Documentation](https://platform.openai.com/docs/models) - -### 2. Select Your WalletProvider -AgentKit requires a **WalletProvider** to interact with blockchain networks. - -- Documentation: [Wallet Providers](https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers) +Modify the `ChatOpenAI` instantiation to use the model of your choice. -### 3. Select Your ActionProviders -ActionProviders define what your agent can do. You can use built-in providers or create your own. +### 2. Select Your Wallet Provider +AgentKit requires a **Wallet Provider** to interact with blockchain networks. -- Built-in ActionProviders: [Available Providers](https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#action-providers) -- Custom ActionProviders: [Creating a Provider](https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#creating-an-action-provider) +### 3. Select Your Action Providers +Action Providers define what your agent can do. You can use built-in providers or create your own. --- ## Next Steps - Explore the AgentKit README: [AgentKit Documentation](https://github.com/coinbase/agentkit) -- Learn more about available WalletProviders & ActionProviders. -- Experiment with custom ActionProviders for your specific use case. +- Learn more about available Wallet Providers & Action Providers. +- Experiment with custom Action Providers for your specific use case. --- From 400615ba9c6eeb93298a2a3da5f44059179d7264 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 15:31:07 -0800 Subject: [PATCH 35/49] chore: add coinbase to package description --- typescript/create-onchain-agent/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index 1c98c98df..29c92ec5b 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -1,6 +1,6 @@ { "name": "create-onchain-agent", - "description": "Instantly create onchain-agent applications with AgentKit.", + "description": "Instantly create onchain-agent applications with Coinbase AgentKit.", "version": "0.1.0", "license": "MIT", "scripts": { From bec4a5f535972f77a4571319d9a9d76b658bea56 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 15:39:53 -0800 Subject: [PATCH 36/49] chore: fixed prettier and ran --- package.json | 2 +- typescript/create-onchain-agent/package.json | 2 +- typescript/create-onchain-agent/src/cli.ts | 52 +++++---- .../create-onchain-agent/src/constants.ts | 79 +++++++++----- .../create-onchain-agent/src/fileSystem.ts | 16 +-- typescript/create-onchain-agent/src/types.ts | 2 +- typescript/create-onchain-agent/src/utils.ts | 101 ++++++++++-------- .../next/app/api/agent/evm/cdp/route.ts | 76 +++++++------ .../next/app/api/agent/evm/privy/route.ts | 63 ++++++----- .../next/app/api/agent/evm/viem/route.ts | 68 +++++++----- .../next/app/api/agent/svm/privy/route.ts | 56 +++++----- .../app/api/agent/svm/solanaKeypair/route.ts | 46 ++++---- .../templates/next/app/hooks/useAgent.ts | 8 +- .../templates/next/app/types/api.ts | 5 +- .../templates/next/tailwind.config.ts | 4 +- .../create-onchain-agent/tsconfig.build.json | 14 +-- typescript/create-onchain-agent/tsconfig.json | 34 +++--- 17 files changed, 354 insertions(+), 274 deletions(-) diff --git a/package.json b/package.json index 042ffa0d9..5b29e315e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "workspaces": [ "typescript/agentkit", - "typescript/agentkit/create-onchain-agent", + "typescript/create-onchain-agent", "typescript/framework-extensions/langchain", "typescript/examples/langchain-cdp-chatbot", "typescript/examples/langchain-twitter-chatbot", diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index 29c92ec5b..23af68ca0 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -42,4 +42,4 @@ "@types/node": "^20.12.10", "@types/prompts": "^2.4.9" } -} \ No newline at end of file +} diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 58b75edec..66dd100dd 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -11,7 +11,7 @@ import { handleSelection, isValidPackageName, toValidPackageName, - getWalletProviders + getWalletProviders, } from "./utils.js"; async function init() { @@ -24,12 +24,14 @@ async function init() { ██ ██ ██████ ███████ ██ ████ ██ ██ ██ ██ ██ Giving every AI agent a crypto wallet -`)}` +`)}`, ); const defaultProjectName = "onchain-agent"; - let result: prompts.Answers<"projectName" | "packageName" | 'walletProvider' | 'network' | 'chainId'>; + let result: prompts.Answers< + "projectName" | "packageName" | "walletProvider" | "network" | "chainId" + >; try { result = await prompts( @@ -39,10 +41,10 @@ async function init() { name: "projectName", message: pc.reset("Project name:"), initial: defaultProjectName, - onState: (state) => { + onState: state => { state.value = state.value.trim(); }, - validate: (value) => { + validate: value => { const targetDir = path.join(process.cwd(), value); if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) { return "Directory already exists and is not empty. Please choose a different name."; @@ -55,29 +57,33 @@ async function init() { isValidPackageName(projectName) ? null : "text", name: "packageName", message: pc.reset("Package name:"), - initial: (_, { projectName }: { projectName: string }) => - toValidPackageName(projectName), - validate: (dir) => - isValidPackageName(dir) || "Invalid package.json name", + initial: (_, { projectName }: { projectName: string }) => toValidPackageName(projectName), + validate: dir => isValidPackageName(dir) || "Invalid package.json name", }, { type: "select", name: "network", message: pc.reset("Choose a network:"), - choices: Networks.map((network) => ({ + choices: Networks.map(network => ({ title: network === "base-sepolia" ? `${network} (default)` : network, value: network as Network | null, - })).concat([{ title: 'other', value: null}]), + })).concat([{ title: "other", value: null }]), initial: Networks.indexOf("base-sepolia"), }, { - type: (prev, { network }) => network === null ? "text" : null, + type: (prev, { network }) => (network === null ? "text" : null), name: "chainId", message: pc.reset("Enter your Ethereum chain ID:"), - validate: (value) => value.trim() ? Number.parseInt(value) ? true : "Chain ID must be a number." : "Chain ID cannot be empty.", + validate: value => + value.trim() + ? Number.parseInt(value) + ? true + : "Chain ID must be a number." + : "Chain ID cannot be empty.", }, { - type: (prev, { network }) => !network || NetworkToWalletProviders[network as Network].length > 1 ? "select" : null, + type: (prev, { network }) => + !network || NetworkToWalletProviders[network as Network].length > 1 ? "select" : null, name: "walletProvider", message: (prev, { network }) => { const walletDescriptions: Record = { @@ -86,19 +92,19 @@ async function init() { Privy: "Authentication and wallet infrastructure.", SolanaKeypair: "Client-side Solana wallet.", }; - + const providerDescriptions = getWalletProviders(network) - .map((provider) => ` - ${provider}: ${walletDescriptions[provider]}`) + .map(provider => ` - ${provider}: ${walletDescriptions[provider]}`) .join("\n"); - + return pc.reset(`Choose a wallet provider:\n${providerDescriptions}\n`); }, choices: (prev, { network, chainId }) => { const walletProviders = getWalletProviders(network); - return getWalletProviders(network).map((provider) => ({ + return getWalletProviders(network).map(provider => ({ title: provider === walletProviders[0] ? `${provider} (default)` : provider, value: provider, - })) + })); }, initial: 0, }, @@ -108,7 +114,7 @@ async function init() { console.log("\nProject creation cancelled."); process.exit(0); }, - } + }, ); } catch (cancelled: any) { console.log(cancelled.message); @@ -118,11 +124,11 @@ async function init() { const spinner = ora(`Creating ${projectName}...`).start(); - // Copy template over to new project + // Copy template over to new project const root = await copyTemplate(projectName, packageName); // Handle selection-specific logic over copied-template - await handleSelection(root, walletProvider, network, chainId) + await handleSelection(root, walletProvider, network, chainId); spinner.succeed(); console.log(pc.blueBright(`\nSuccessfully created your AgentKit project in ${root}`)); @@ -154,6 +160,6 @@ async function init() { console.log(pc.blueBright(" - https://discord.gg/CDP\n")); } -init().catch((e) => { +init().catch(e => { console.error(e); }); diff --git a/typescript/create-onchain-agent/src/constants.ts b/typescript/create-onchain-agent/src/constants.ts index be177b654..2cd0a764e 100644 --- a/typescript/create-onchain-agent/src/constants.ts +++ b/typescript/create-onchain-agent/src/constants.ts @@ -1,15 +1,28 @@ -import { EVMNetwork, Network, SVMNetwork, WalletProviderChoice, WalletProviderRouteConfiguration } from "./types"; +import { + EVMNetwork, + Network, + SVMNetwork, + WalletProviderChoice, + WalletProviderRouteConfiguration, +} from "./types"; export const EVM_NETWORKS: Set = new Set([ - "ethereum-mainnet", "ethereum-sepolia", - "polygon-mainnet", "polygon-mumbai", - "base-mainnet", "base-sepolia", - "arbitrum-mainnet", "arbitrum-sepolia", - "optimism-mainnet", "optimism-sepolia" + "ethereum-mainnet", + "ethereum-sepolia", + "polygon-mainnet", + "polygon-mumbai", + "base-mainnet", + "base-sepolia", + "arbitrum-mainnet", + "arbitrum-sepolia", + "optimism-mainnet", + "optimism-sepolia", ]); export const SVM_NETWORKS: Set = new Set([ - "solana-mainnet", "solana-devnet", "solana-testnet" + "solana-mainnet", + "solana-devnet", + "solana-testnet", ]); const CDP_SUPPORTED_EVM_WALLET_PROVIDERS: WalletProviderChoice[] = ["CDP", "Viem", "Privy"]; @@ -29,26 +42,31 @@ export const NetworkToWalletProviders: Record = "polygon-mumbai": NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, "solana-mainnet": SVM_WALLET_PROVIDERS, "solana-devnet": SVM_WALLET_PROVIDERS, - "solana-testnet": SVM_WALLET_PROVIDERS + "solana-testnet": SVM_WALLET_PROVIDERS, }; export const Networks: Network[] = [...EVM_NETWORKS, ...SVM_NETWORKS]; -export const WalletProviderChoices: WalletProviderChoice[] = [...new Set([ - ...CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - ...NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, - ...SVM_WALLET_PROVIDERS -])]; +export const WalletProviderChoices: WalletProviderChoice[] = [ + ...new Set([ + ...CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + ...NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + ...SVM_WALLET_PROVIDERS, + ]), +]; -export const WalletProviderRouteConfigurations: Record<"EVM" | "SVM", Partial>> = { +export const WalletProviderRouteConfigurations: Record< + "EVM" | "SVM", + Partial> +> = { EVM: { CDP: { env: { topComments: ["Get keys from CDP Portal: https://portal.cdp.coinbase.com/"], required: ["CDP_API_KEY_NAME=", "CDP_API_KEY_PRIVATE_KEY="], - optional: [] + optional: [], }, - apiRoute: "evm/cdp/route.ts" + apiRoute: "evm/cdp/route.ts", }, Viem: { env: { @@ -56,33 +74,42 @@ export const WalletProviderRouteConfigurations: Record<"EVM" | "SVM", Partial = { _gitignore: ".gitignore", @@ -52,6 +44,6 @@ export async function copyTemplate(projectName: string, packageName: string) { const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8")); pkg.name = packageName || toValidPackageName(projectName); await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); - + return root; -} \ No newline at end of file +} diff --git a/typescript/create-onchain-agent/src/types.ts b/typescript/create-onchain-agent/src/types.ts index c3b44bbf1..8fa13ae3f 100644 --- a/typescript/create-onchain-agent/src/types.ts +++ b/typescript/create-onchain-agent/src/types.ts @@ -21,6 +21,6 @@ export type WalletProviderRouteConfiguration = { topComments: string[]; required: string[]; optional: string[]; - }, + }; apiRoute: string; }; diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index c84b0f520..05d6b1e5e 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -1,10 +1,20 @@ -import fs from 'fs/promises'; -import path from 'path'; -import { EVMNetwork, Network, SVMNetwork, WalletProviderChoice } from './types'; -import { EVM_NETWORKS, NetworkToWalletProviders, NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, SVM_NETWORKS, WalletProviderRouteConfigurations } from './constants.js'; +import fs from "fs/promises"; +import path from "path"; +import { EVMNetwork, Network, SVMNetwork, WalletProviderChoice } from "./types"; +import { + EVM_NETWORKS, + NetworkToWalletProviders, + NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS, + SVM_NETWORKS, + WalletProviderRouteConfigurations, +} from "./constants.js"; export function getNetworkFamily(network: EVMNetwork | SVMNetwork) { - return EVM_NETWORKS.has(network as EVMNetwork) ? 'EVM' : SVM_NETWORKS.has(network as SVMNetwork) ? 'SVM' : undefined; + return EVM_NETWORKS.has(network as EVMNetwork) + ? "EVM" + : SVM_NETWORKS.has(network as SVMNetwork) + ? "SVM" + : undefined; } async function copyFile(src: string, dest: string) { @@ -16,7 +26,7 @@ async function copyDir(src: string, dest: string) { const entries = await fs.readdir(src, { withFileTypes: true }); await Promise.all( - entries.map(async (entry) => { + entries.map(async entry => { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { @@ -24,7 +34,7 @@ async function copyDir(src: string, dest: string) { } else { await copyFile(srcPath, destPath); } - }) + }), ); } @@ -43,36 +53,34 @@ export function createClickableLink(text: string, url: string): string { } export function isValidPackageName(projectName: string) { - return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test( - projectName - ); + return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(projectName); } export function toValidPackageName(projectName: string) { return projectName .trim() .toLowerCase() - .replace(/\s+/g, '-') - .replace(/^[._]/, '') - .replace(/[^a-z\d\-~]+/g, '-'); + .replace(/\s+/g, "-") + .replace(/^[._]/, "") + .replace(/[^a-z\d\-~]+/g, "-"); } export function detectPackageManager(): string { if (process.env.npm_config_user_agent) { - if (process.env.npm_config_user_agent.startsWith('yarn')) { - return 'yarn'; + if (process.env.npm_config_user_agent.startsWith("yarn")) { + return "yarn"; } - if (process.env.npm_config_user_agent.startsWith('pnpm')) { - return 'pnpm'; + if (process.env.npm_config_user_agent.startsWith("pnpm")) { + return "pnpm"; } - if (process.env.npm_config_user_agent.startsWith('npm')) { - return 'npm'; + if (process.env.npm_config_user_agent.startsWith("npm")) { + return "npm"; } - if (process.env.npm_config_user_agent.startsWith('bun')) { - return 'bun'; + if (process.env.npm_config_user_agent.startsWith("bun")) { + return "bun"; } } - return 'npm'; // default to npm if unable to detect + return "npm"; // default to npm if unable to detect } export const getWalletProviders = (network?: Network) => { @@ -80,44 +88,49 @@ export const getWalletProviders = (network?: Network) => { return NetworkToWalletProviders[network]; } return NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS; -} - -export async function handleSelection(root: string, walletProvider: WalletProviderChoice, network?: Network, chainId?: string) { +}; + +export async function handleSelection( + root: string, + walletProvider: WalletProviderChoice, + network?: Network, + chainId?: string, +) { const agentDir = path.join(root, "app", "api", "agent"); let networkFamily: ReturnType; if (network) { networkFamily = getNetworkFamily(network); - } - else if (chainId) { - networkFamily = 'EVM'; - } - else { - throw new Error('Unsupported network and chainId selected'); + } else if (chainId) { + networkFamily = "EVM"; + } else { + throw new Error("Unsupported network and chainId selected"); } const selectedRouteConfig = WalletProviderRouteConfigurations[networkFamily!][walletProvider]; if (!selectedRouteConfig) { - throw new Error('Selected invalid network & wallet provider combination') + throw new Error("Selected invalid network & wallet provider combination"); } // Create .env file const envPath = path.join(root, ".env.local"); const envLines = [ // Start file with notes regarding .env var setup - ...["Get keys from OpenAI Platform: https://platform.openai.com/api-keys", ...selectedRouteConfig.env.topComments].map(comment => `# ${comment}`).join("\n"), + ...[ + "Get keys from OpenAI Platform: https://platform.openai.com/api-keys", + ...selectedRouteConfig.env.topComments, + ] + .map(comment => `# ${comment}`) + .join("\n"), // Continue with # Required section "\n\n# Required\n", ...["OPENAI_API_KEY=", ...selectedRouteConfig.env.required].join("\n"), // Finish with # Optional section "\n\n# Optional\n", - ...[`NETWORK_ID=${network}`, ...selectedRouteConfig.env.optional].join("\n") - ] - await fs.writeFile( - envPath, - envLines - ); + ...[`NETWORK_ID=${network}`, ...selectedRouteConfig.env.optional].join("\n"), + ]; + await fs.writeFile(envPath, envLines); // Promote selected route (move `apiRoute` to `api/agent/route.ts`) const selectedRoutePath = path.join(agentDir, selectedRouteConfig.apiRoute); @@ -126,8 +139,10 @@ export async function handleSelection(root: string, walletProvider: WalletProvid await fs.rename(selectedRoutePath, newRoutePath); // Delete all unselected routes - const allRouteConfigurations = Object.values(WalletProviderRouteConfigurations).flatMap(routeConfigurations => Object.values(routeConfigurations)).filter(x => x); - const providerRoutes = allRouteConfigurations.map((config) => path.join(agentDir, config.apiRoute)); + const allRouteConfigurations = Object.values(WalletProviderRouteConfigurations) + .flatMap(routeConfigurations => Object.values(routeConfigurations)) + .filter(x => x); + const providerRoutes = allRouteConfigurations.map(config => path.join(agentDir, config.apiRoute)); for (const routePath of providerRoutes) { // Remove file await fs.rm(routePath, { recursive: true, force: true }); @@ -143,6 +158,6 @@ export async function handleSelection(root: string, walletProvider: WalletProvid // Skip removing directory } } - await fs.rm(path.join(agentDir, 'evm'), { recursive: true, force: true }); - await fs.rm(path.join(agentDir, 'svm'), { recursive: true, force: true }); + await fs.rm(path.join(agentDir, "evm"), { recursive: true, force: true }); + await fs.rm(path.join(agentDir, "svm"), { recursive: true, force: true }); } diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts index 1d05dd6f0..31554cae9 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts @@ -1,5 +1,14 @@ import { NextResponse } from "next/server"; -import { AgentKit, cdpApiActionProvider, cdpWalletActionProvider, CdpWalletProvider, erc20ActionProvider, pythActionProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { + AgentKit, + cdpApiActionProvider, + cdpWalletActionProvider, + CdpWalletProvider, + erc20ActionProvider, + pythActionProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; import { getLangChainTools } from "@coinbase/agentkit-langchain"; import { ChatOpenAI } from "@langchain/openai"; import { MemorySaver } from "@langchain/langgraph"; @@ -9,34 +18,34 @@ import { AgentRequest, AgentResponse } from "@/app/types/api"; /** * AgentKit Integration Route * - * This file is your gateway to integrating AgentKit with your product. - * It defines the interaction between your system and the AI agent, + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, * allowing you to configure the agent to suit your needs. * * # Key Steps to Customize Your Agent:** - * + * * 1. Select your LLM: * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. - * + * * 2. Set up your WalletProvider: * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers - * + * * 3️. Set up your ActionProviders: - * - ActionProviders define what your agent can do. + * - ActionProviders define what your agent can do. * - Choose from built-in providers or create your own: * - 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 - * + * * 4. Instantiate your Agent: * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. - * + * * # Next Steps: * - Explore the AgentKit README: https://github.com/coinbase/agentkit * - Learn more about available WalletProviders & ActionProviders. * - Experiment with custom ActionProviders for your unique use case. - * + * * ## Want to contribute? - * Join us in shaping AgentKit! Check out the contribution guide: + * Join us in shaping AgentKit! Check out the contribution guide: * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md * - https://discord.gg/CDP */ @@ -50,7 +59,7 @@ let agent: ReturnType; * * @function getOrInitializeAgent * @returns {Promise>} The initialized AI agent. - * + * * @description Handles agent setup * * @throws {Error} If the agent initialization fails. @@ -74,30 +83,30 @@ async function getOrInitializeAgent(): Promise Promise }): Promise> { +export async function POST( + req: Request & { json: () => Promise }, +): Promise> { try { // 1️. Extract user message from the request body const { userMessage } = await req.json(); @@ -135,7 +146,7 @@ export async function POST(req: Request & { json: () => Promise }) // 3.Start streaming the agent's response const stream = await agent.stream( { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent - { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + { configurable: { thread_id: "AgentKit Discussion" } }, // Customizable thread ID for tracking conversations ); // 4️. Process the streamed response chunks into a single message @@ -153,4 +164,3 @@ export async function POST(req: Request & { json: () => Promise }) return NextResponse.json({ error: "Failed to process message" }); } } - diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts index d22925c5b..32686a576 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts @@ -1,5 +1,12 @@ import { AgentRequest, AgentResponse } from "@/app/types/api"; -import { AgentKit, erc20ActionProvider, PrivyWalletProvider, pythActionProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { + AgentKit, + erc20ActionProvider, + PrivyWalletProvider, + pythActionProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; import { getLangChainTools } from "@coinbase/agentkit-langchain"; import { MemorySaver } from "@langchain/langgraph"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; @@ -9,34 +16,34 @@ import { NextResponse } from "next/server"; /** * AgentKit Integration Route * - * This file is your gateway to integrating AgentKit with your product. - * It defines the interaction between your system and the AI agent, + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, * allowing you to configure the agent to suit your needs. * * # Key Steps to Customize Your Agent:** - * + * * 1. Select your LLM: * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. - * + * * 2. Set up your WalletProvider: * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers - * + * * 3️. Set up your ActionProviders: - * - ActionProviders define what your agent can do. + * - ActionProviders define what your agent can do. * - Choose from built-in providers or create your own: * - 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 - * + * * 4. Instantiate your Agent: * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. - * + * * # Next Steps: * - Explore the AgentKit README: https://github.com/coinbase/agentkit * - Learn more about available WalletProviders & ActionProviders. * - Experiment with custom ActionProviders for your unique use case. - * + * * ## Want to contribute? - * Join us in shaping AgentKit! Check out the contribution guide: + * Join us in shaping AgentKit! Check out the contribution guide: * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md * - https://discord.gg/CDP */ @@ -50,7 +57,7 @@ let agent: ReturnType; * * @function getOrInitializeAgent * @returns {Promise>} The initialized AI agent. - * + * * @description Handles agent setup * * @throws {Error} If the agent initialization fails. @@ -65,7 +72,6 @@ async function getOrInitializeAgent(): Promise Promise }): Promise> { +export async function POST( + req: Request & { json: () => Promise }, +): Promise> { try { // 1️. Extract user message from the request body const { userMessage } = await req.json(); @@ -131,7 +139,7 @@ export async function POST(req: Request & { json: () => Promise }) // 3.Start streaming the agent's response const stream = await agent.stream( { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent - { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + { configurable: { thread_id: "AgentKit Discussion" } }, // Customizable thread ID for tracking conversations ); // 4️. Process the streamed response chunks into a single message @@ -149,4 +157,3 @@ export async function POST(req: Request & { json: () => Promise }) return NextResponse.json({ error: "Failed to process message" }); } } - diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts index e5cd03540..8d0b80fe5 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/viem/route.ts @@ -1,5 +1,16 @@ import { NextResponse } from "next/server"; -import { AgentKit, cdpApiActionProvider, cdpWalletActionProvider, CdpWalletProvider, erc20ActionProvider, NETWORK_ID_TO_VIEM_CHAIN, pythActionProvider, ViemWalletProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { + AgentKit, + cdpApiActionProvider, + cdpWalletActionProvider, + CdpWalletProvider, + erc20ActionProvider, + NETWORK_ID_TO_VIEM_CHAIN, + pythActionProvider, + ViemWalletProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; import { getLangChainTools } from "@coinbase/agentkit-langchain"; import { ChatOpenAI } from "@langchain/openai"; import { MemorySaver } from "@langchain/langgraph"; @@ -11,34 +22,34 @@ import { privateKeyToAccount } from "viem/accounts"; /** * AgentKit Integration Route * - * This file is your gateway to integrating AgentKit with your product. - * It defines the interaction between your system and the AI agent, + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, * allowing you to configure the agent to suit your needs. * * # Key Steps to Customize Your Agent:** - * + * * 1. Select your LLM: * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. - * + * * 2. Set up your WalletProvider: * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers - * + * * 3️. Set up your ActionProviders: - * - ActionProviders define what your agent can do. + * - ActionProviders define what your agent can do. * - Choose from built-in providers or create your own: * - 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 - * + * * 4. Instantiate your Agent: * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. - * + * * # Next Steps: * - Explore the AgentKit README: https://github.com/coinbase/agentkit * - Learn more about available WalletProviders & ActionProviders. * - Experiment with custom ActionProviders for your unique use case. - * + * * ## Want to contribute? - * Join us in shaping AgentKit! Check out the contribution guide: + * Join us in shaping AgentKit! Check out the contribution guide: * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md * - https://discord.gg/CDP */ @@ -52,7 +63,7 @@ let agent: ReturnType; * * @function getOrInitializeAgent * @returns {Promise>} The initialized AI agent. - * + * * @description Handles agent setup * * @throws {Error} If the agent initialization fails. @@ -68,9 +79,7 @@ async function getOrInitializeAgent(): Promise Promise }): Promise> { +export async function POST( + req: Request & { json: () => Promise }, +): Promise> { try { // 1️. Extract user message from the request body const { userMessage } = await req.json(); @@ -134,7 +145,7 @@ export async function POST(req: Request & { json: () => Promise }) // 3.Start streaming the agent's response const stream = await agent.stream( { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent - { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + { configurable: { thread_id: "AgentKit Discussion" } }, // Customizable thread ID for tracking conversations ); // 4️. Process the streamed response chunks into a single message @@ -152,4 +163,3 @@ export async function POST(req: Request & { json: () => Promise }) return NextResponse.json({ error: "Failed to process message" }); } } - diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts index fb3b3395f..a5ca1304e 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/privy/route.ts @@ -1,5 +1,14 @@ import { AgentRequest, AgentResponse } from "@/app/types/api"; -import { AgentKit, erc20ActionProvider, jupiterActionProvider, PrivyWalletProvider, pythActionProvider, splActionProvider, walletActionProvider, wethActionProvider } from "@coinbase/agentkit"; +import { + AgentKit, + erc20ActionProvider, + jupiterActionProvider, + PrivyWalletProvider, + pythActionProvider, + splActionProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; import { getLangChainTools } from "@coinbase/agentkit-langchain"; import { MemorySaver } from "@langchain/langgraph"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; @@ -9,34 +18,34 @@ import { NextResponse } from "next/server"; /** * AgentKit Integration Route * - * This file is your gateway to integrating AgentKit with your product. - * It defines the interaction between your system and the AI agent, + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, * allowing you to configure the agent to suit your needs. * * # Key Steps to Customize Your Agent:** - * + * * 1. Select your LLM: * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. - * + * * 2. Set up your WalletProvider: * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers - * + * * 3️. Set up your ActionProviders: - * - ActionProviders define what your agent can do. + * - ActionProviders define what your agent can do. * - Choose from built-in providers or create your own: * - 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 - * + * * 4. Instantiate your Agent: * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. - * + * * # Next Steps: * - Explore the AgentKit README: https://github.com/coinbase/agentkit * - Learn more about available WalletProviders & ActionProviders. * - Experiment with custom ActionProviders for your unique use case. - * + * * ## Want to contribute? - * Join us in shaping AgentKit! Check out the contribution guide: + * Join us in shaping AgentKit! Check out the contribution guide: * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md * - https://discord.gg/CDP */ @@ -50,7 +59,7 @@ let agent: ReturnType; * * @function getOrInitializeAgent * @returns {Promise>} The initialized AI agent. - * + * * @description Handles agent setup * * @throws {Error} If the agent initialization fails. @@ -75,24 +84,20 @@ async function getOrInitializeAgent(): Promise Promise }): Promise> { +export async function POST( + req: Request & { json: () => Promise }, +): Promise> { try { // 1️. Extract user message from the request body const { userMessage } = await req.json(); @@ -130,7 +137,7 @@ export async function POST(req: Request & { json: () => Promise }) // 3.Start streaming the agent's response const stream = await agent.stream( { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent - { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + { configurable: { thread_id: "AgentKit Discussion" } }, // Customizable thread ID for tracking conversations ); // 4️. Process the streamed response chunks into a single message @@ -148,4 +155,3 @@ export async function POST(req: Request & { json: () => Promise }) return NextResponse.json({ error: "Failed to process message" }); } } - diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts index ac27bd2e6..2ee715679 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/svm/solanaKeypair/route.ts @@ -1,5 +1,12 @@ import { AgentRequest, AgentResponse } from "@/app/types/api"; -import { AgentKit, SOLANA_NETWORK_ID, SolanaKeypairWalletProvider, splActionProvider, walletActionProvider, jupiterActionProvider } from "@coinbase/agentkit"; +import { + AgentKit, + SOLANA_NETWORK_ID, + SolanaKeypairWalletProvider, + splActionProvider, + walletActionProvider, + jupiterActionProvider, +} from "@coinbase/agentkit"; import { getLangChainTools } from "@coinbase/agentkit-langchain"; import { MemorySaver } from "@langchain/langgraph"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; @@ -11,34 +18,34 @@ import { NextResponse } from "next/server"; /** * AgentKit Integration Route * - * This file is your gateway to integrating AgentKit with your product. - * It defines the interaction between your system and the AI agent, + * This file is your gateway to integrating AgentKit with your product. + * It defines the interaction between your system and the AI agent, * allowing you to configure the agent to suit your needs. * * # Key Steps to Customize Your Agent:** - * + * * 1. Select your LLM: * - Modify the `ChatOpenAI` instantiation to choose your preferred LLM. - * + * * 2. Set up your WalletProvider: * - Learn more: https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#evm-wallet-providers - * + * * 3️. Set up your ActionProviders: - * - ActionProviders define what your agent can do. + * - ActionProviders define what your agent can do. * - Choose from built-in providers or create your own: * - 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 - * + * * 4. Instantiate your Agent: * - Pass the LLM, tools, and memory into `createReactAgent()` to bring your agent to life. - * + * * # Next Steps: * - Explore the AgentKit README: https://github.com/coinbase/agentkit * - Learn more about available WalletProviders & ActionProviders. * - Experiment with custom ActionProviders for your unique use case. - * + * * ## Want to contribute? - * Join us in shaping AgentKit! Check out the contribution guide: + * Join us in shaping AgentKit! Check out the contribution guide: * - https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md * - https://discord.gg/CDP */ @@ -52,7 +59,7 @@ let agent: ReturnType; * * @function getOrInitializeAgent * @returns {Promise>} The initialized AI agent. - * + * * @description Handles agent setup * * @throws {Error} If the agent initialization fails. @@ -87,7 +94,7 @@ async function getOrInitializeAgent(): Promise Promise }): Promise> { +export async function POST( + req: Request & { json: () => Promise }, +): Promise> { try { // 1️. Extract user message from the request body const { userMessage } = await req.json(); @@ -138,7 +147,7 @@ export async function POST(req: Request & { json: () => Promise }) // 3.Start streaming the agent's response const stream = await agent.stream( { messages: [{ content: userMessage, role: "user" }] }, // The new message to send to the agent - { configurable: { thread_id: 'AgentKit Discussion' } } // Customizable thread ID for tracking conversations + { configurable: { thread_id: "AgentKit Discussion" } }, // Customizable thread ID for tracking conversations ); // 4️. Process the streamed response chunks into a single message @@ -156,4 +165,3 @@ export async function POST(req: Request & { json: () => Promise }) return NextResponse.json({ error: "Failed to process message" }); } } - diff --git a/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts b/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts index e85921bd9..7d3c2a22f 100644 --- a/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts +++ b/typescript/create-onchain-agent/templates/next/app/hooks/useAgent.ts @@ -28,9 +28,9 @@ async function messageAgent(userMessage: string): Promise { } /** - * + * * This hook manages interactions with the AI agent by making REST calls to the backend. - * It also stores the local conversation state, tracking messages sent by the user and + * It also stores the local conversation state, tracking messages sent by the user and * responses from the agent. * * #### How It Works @@ -58,13 +58,13 @@ export function useAgent() { const sendMessage = async (input: string) => { if (!input.trim()) return; - setMessages((prev) => [...prev, { text: input, sender: "user" }]); + setMessages(prev => [...prev, { text: input, sender: "user" }]); setIsThinking(true); const responseMessage = await messageAgent(input); if (responseMessage) { - setMessages((prev) => [...prev, { text: responseMessage, sender: "agent" }]); + setMessages(prev => [...prev, { text: responseMessage, sender: "agent" }]); } setIsThinking(false); diff --git a/typescript/create-onchain-agent/templates/next/app/types/api.ts b/typescript/create-onchain-agent/templates/next/app/types/api.ts index 62d1b39b6..707406085 100644 --- a/typescript/create-onchain-agent/templates/next/app/types/api.ts +++ b/typescript/create-onchain-agent/templates/next/app/types/api.ts @@ -1,4 +1,3 @@ +export type AgentRequest = { userMessage: string }; -export type AgentRequest = { userMessage: string } - -export type AgentResponse = { response?: string, error?: string } +export type AgentResponse = { response?: string; error?: string }; diff --git a/typescript/create-onchain-agent/templates/next/tailwind.config.ts b/typescript/create-onchain-agent/templates/next/tailwind.config.ts index cb8ee88d2..2e394cd42 100644 --- a/typescript/create-onchain-agent/templates/next/tailwind.config.ts +++ b/typescript/create-onchain-agent/templates/next/tailwind.config.ts @@ -9,11 +9,11 @@ const config: Config = { theme: { extend: { fontFamily: { - sans: ['DM Sans', 'sans-serif'], + sans: ["DM Sans", "sans-serif"], }, }, }, - plugins: [] + plugins: [], }; export default config; diff --git a/typescript/create-onchain-agent/tsconfig.build.json b/typescript/create-onchain-agent/tsconfig.build.json index 2b6ebc81d..f34c137f8 100644 --- a/typescript/create-onchain-agent/tsconfig.build.json +++ b/typescript/create-onchain-agent/tsconfig.build.json @@ -1,8 +1,8 @@ { - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.test.ts"], - "compilerOptions": { - "sourceMap": true - } - } \ No newline at end of file + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"], + "compilerOptions": { + "sourceMap": true + } +} diff --git a/typescript/create-onchain-agent/tsconfig.json b/typescript/create-onchain-agent/tsconfig.json index ce15a04ea..cf5f07f19 100644 --- a/typescript/create-onchain-agent/tsconfig.json +++ b/typescript/create-onchain-agent/tsconfig.json @@ -1,18 +1,18 @@ { - "compilerOptions": { - "target": "ES2020", - "module": "ES2020", - "moduleResolution": "node", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "outDir": "./dist/esm", - "rootDir": "./src", - "declaration": true, - "declarationMap": true, - "declarationDir": "./dist/types" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "**/*.test.ts", "**/*.test-d.ts"] - } \ No newline at end of file + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist/esm", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "declarationDir": "./dist/types" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts", "**/*.test-d.ts"] +} From 8d9985f7815003f0e2831935375fe4f4ddc9d3ff Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 15:56:05 -0800 Subject: [PATCH 37/49] chore: fixed lint and ran --- typescript/create-onchain-agent/package.json | 4 +- typescript/create-onchain-agent/src/cli.ts | 21 +++- .../create-onchain-agent/src/fileSystem.ts | 35 +++++- typescript/create-onchain-agent/src/utils.ts | 109 +++++++++++++++--- 4 files changed, 143 insertions(+), 26 deletions(-) diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index 23af68ca0..827fabfa7 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -8,8 +8,8 @@ "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"", "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"", - "lint": "eslint -c .eslintrc.json *.ts", - "lint:fix": "eslint -c .eslintrc.json *.ts --fix", + "lint": "eslint -c .eslintrc.json \"src/**/*.ts\"", + "lint:fix": "eslint -c .eslintrc.json \"src/**/*.ts\" --fix", "check:types": "tsc --noEmit", "clean": "rm -rf dist tsconfig.tsbuildinfo", "dev": "node --loader ts-node/esm src/cli.ts" diff --git a/typescript/create-onchain-agent/src/cli.ts b/typescript/create-onchain-agent/src/cli.ts index 66dd100dd..81e8cc1c3 100644 --- a/typescript/create-onchain-agent/src/cli.ts +++ b/typescript/create-onchain-agent/src/cli.ts @@ -14,6 +14,15 @@ import { getWalletProviders, } from "./utils.js"; +/** + * Initializes the project creation process. + * + * - Prompts the user for project details including project name, package name, network, chain ID, and wallet provider. + * - Validates user input, ensuring directories do not already exist and package names are valid. + * - Copies the selected template to the new project directory. + * - Handles network and wallet provider selection logic. + * - Displays a summary of the created project along with next steps. + */ async function init() { console.log( `${pc.blue(` @@ -22,7 +31,7 @@ async function init() { ███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ ███████ ██ ████ ██ ██ ██ ██ ██ - + Giving every AI agent a crypto wallet `)}`, ); @@ -99,7 +108,7 @@ async function init() { return pc.reset(`Choose a wallet provider:\n${providerDescriptions}\n`); }, - choices: (prev, { network, chainId }) => { + choices: (prev, { network }) => { const walletProviders = getWalletProviders(network); return getWalletProviders(network).map(provider => ({ title: provider === walletProviders[0] ? `${provider} (default)` : provider, @@ -116,8 +125,12 @@ async function init() { }, }, ); - } catch (cancelled: any) { - console.log(cancelled.message); + } catch (cancelled: unknown) { + if (cancelled instanceof Error) { + console.info(cancelled.message); + } else { + console.info("An unknown error occurred"); + } process.exit(1); } const { projectName, packageName, network, chainId, walletProvider } = result; diff --git a/typescript/create-onchain-agent/src/fileSystem.ts b/typescript/create-onchain-agent/src/fileSystem.ts index ac93dd8cf..94f242676 100644 --- a/typescript/create-onchain-agent/src/fileSystem.ts +++ b/typescript/create-onchain-agent/src/fileSystem.ts @@ -13,11 +13,28 @@ const renameFiles: Record = { const excludeDirs = ["node_modules", ".next"]; const excludeFiles = [".DS_Store", "Thumbs.db"]; -function getSourceDir() { +/** + * Retrieves the source directory for copying template files. + * + * @returns {string} The source directory path. + */ +function getSourceDir(): string { return sourceDir; } -async function copyDir(src: string, dest: string) { +/** + * Recursively copies a directory and its contents from the source to the destination. + * + * - Creates the destination directory if it does not exist. + * - Copies files, renaming them if specified in `renameFiles`. + * - Skips directories listed in `excludeDirs`. + * - Skips files listed in `excludeFiles`. + * + * @param {string} src - The source directory path. + * @param {string} dest - The destination directory path. + * @returns {Promise} A promise that resolves when the copy operation is complete. + */ +async function copyDir(src: string, dest: string): Promise { await fs.promises.mkdir(dest, { recursive: true }); const entries = await fs.promises.readdir(src, { withFileTypes: true }); @@ -37,12 +54,24 @@ async function copyDir(src: string, dest: string) { } } -export async function copyTemplate(projectName: string, packageName: string) { +/** + * Copies a project template to a new directory and updates the package name. + * + * - Copies the template from the source directory. + * - 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). + * @returns {Promise} The path of the newly created project directory. + */ +export async function copyTemplate(projectName: string, packageName: string): Promise { const root = path.join(process.cwd(), projectName); await copyDir(getSourceDir(), root); + const pkgPath = path.join(root, "package.json"); const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8")); pkg.name = packageName || toValidPackageName(projectName); + await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); return root; diff --git a/typescript/create-onchain-agent/src/utils.ts b/typescript/create-onchain-agent/src/utils.ts index 05d6b1e5e..dd38ae6d2 100644 --- a/typescript/create-onchain-agent/src/utils.ts +++ b/typescript/create-onchain-agent/src/utils.ts @@ -9,7 +9,13 @@ import { WalletProviderRouteConfigurations, } from "./constants.js"; -export function getNetworkFamily(network: EVMNetwork | SVMNetwork) { +/** + * Determines the network family based on the provided network. + * + * @param {EVMNetwork | SVMNetwork} network - The network to check. + * @returns {"EVM" | "SVM" | undefined} The network family, or `undefined` if not recognized. + */ +export function getNetworkFamily(network: EVMNetwork | SVMNetwork): "EVM" | "SVM" | undefined { return EVM_NETWORKS.has(network as EVMNetwork) ? "EVM" : SVM_NETWORKS.has(network as SVMNetwork) @@ -17,11 +23,25 @@ export function getNetworkFamily(network: EVMNetwork | SVMNetwork) { : undefined; } -async function copyFile(src: string, dest: string) { +/** + * Copies a file from the source path to the destination path. + * + * @param {string} src - The source file path. + * @param {string} dest - The destination file path. + * @returns {Promise} A promise that resolves when the file is copied. + */ +async function copyFile(src: string, dest: string): Promise { await fs.copyFile(src, dest); } -async function copyDir(src: string, dest: string) { +/** + * Recursively copies a directory from the source path to the destination path. + * + * @param {string} src - The source directory path. + * @param {string} dest - The destination directory path. + * @returns {Promise} A promise that resolves when the directory and its contents are copied. + */ +async function copyDir(src: string, dest: string): Promise { await fs.mkdir(dest, { recursive: true }); const entries = await fs.readdir(src, { withFileTypes: true }); @@ -37,8 +57,14 @@ async function copyDir(src: string, dest: string) { }), ); } - -export async function optimizedCopy(src: string, dest: string) { +/** + * Recursively copies a file or directory from the source path to the destination path. + * + * @param {string} src - The source file or directory path. + * @param {string} dest - The destination file or directory path. + * @returns {Promise} A promise that resolves when the copy operation is complete. + */ +export async function optimizedCopy(src: string, dest: string): Promise { const stat = await fs.stat(src); if (stat.isDirectory()) { await copyDir(src, dest); @@ -47,16 +73,35 @@ export async function optimizedCopy(src: string, dest: string) { } } +/** + * Generates a clickable terminal hyperlink using ANSI escape codes. + * + * @param {string} text - The display text for the link. + * @param {string} url - The URL the link points to. + * @returns {string} The formatted clickable link string. + */ export function createClickableLink(text: string, url: string): string { // OSC 8 ;; URL \a TEXT \a return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`; } -export function isValidPackageName(projectName: string) { +/** + * Validates whether a given string is a valid npm package name. + * + * @param {string} projectName - The package name to validate. + * @returns {boolean} `true` if the package name is valid, otherwise `false`. + */ +export function isValidPackageName(projectName: string): boolean { return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(projectName); } -export function toValidPackageName(projectName: string) { +/** + * Converts a given project name into a valid npm package name. + * + * @param {string} projectName - The input project name. + * @returns {string} A sanitized, valid npm package name. + */ +export function toValidPackageName(projectName: string): string { return projectName .trim() .toLowerCase() @@ -65,6 +110,14 @@ export function toValidPackageName(projectName: string) { .replace(/[^a-z\d\-~]+/g, "-"); } +/** + * Detects the package manager currently being used. + * + * Checks the `npm_config_user_agent` environment variable to determine if `npm`, `yarn`, `pnpm`, or `bun` is being used. + * If no package manager is detected, it defaults to `npm`. + * + * @returns {string} The detected package manager (`npm`, `yarn`, `pnpm`, or `bun`). + */ export function detectPackageManager(): string { if (process.env.npm_config_user_agent) { if (process.env.npm_config_user_agent.startsWith("yarn")) { @@ -80,16 +133,42 @@ export function detectPackageManager(): string { return "bun"; } } - return "npm"; // default to npm if unable to detect + return "npm"; // Default to npm if unable to detect } -export const getWalletProviders = (network?: Network) => { +/** + * Retrieves the available wallet providers for a given blockchain network. + * + * - If a `network` is provided, returns the corresponding wallet providers. + * - If no network is specified, returns a default list of EVM wallet providers. + * + * @param {Network} [network] - The optional network to get wallet providers for. + * @returns {WalletProviderChoice[]} An array of wallet providers for the specified network. + */ +export const getWalletProviders = (network?: Network): WalletProviderChoice[] => { if (network) { return NetworkToWalletProviders[network]; } return NON_CDP_SUPPORTED_EVM_WALLET_PROVIDERS; }; +/** + * Handles the selection of a network and wallet provider, updating the project configuration accordingly. + * + * This function: + * - Determines the network family (`EVM` or `SVM`) based on the provided network or chain ID. + * - Retrieves the correct route configuration for the selected wallet provider. + * - Creates or updates the `.env.local` file with required and optional environment variables. + * - Moves the selected API route file to `api/agent/route.ts`. + * - Deletes all unselected API routes and cleans up empty directories. + * + * @param {string} root - The root directory of the project. + * @param {WalletProviderChoice} walletProvider - The selected wallet provider. + * @param {Network} [network] - The optional blockchain network. + * @param {string} [chainId] - The optional chain ID for the network. + * @throws {Error} If neither `network` nor `chainId` are provided, or if the selected combination is invalid. + * @returns {Promise} A promise that resolves when the selection process is complete. + */ export async function handleSelection( root: string, walletProvider: WalletProviderChoice, @@ -148,14 +227,10 @@ export async function handleSelection( await fs.rm(routePath, { recursive: true, force: true }); // If directory is empty, remove directory - try { - const parentFolder = path.dirname(routePath); - const files = await fs.readdir(parentFolder); - if (files.length === 0) { - await fs.rm(parentFolder, { recursive: true, force: true }); - } - } catch (error) { - // Skip removing directory + const parentFolder = path.dirname(routePath); + const files = await fs.readdir(parentFolder); + if (files.length === 0) { + await fs.rm(parentFolder, { recursive: true, force: true }); } } await fs.rm(path.join(agentDir, "evm"), { recursive: true, force: true }); From 0992d2c480d8da3e399735214bfd63e529787f94 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 15:59:59 -0800 Subject: [PATCH 38/49] fix: package.lock --- package-lock.json | 502 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 472 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index bffe1619e..2861f3b75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "license": "Apache-2.0", "workspaces": [ "typescript/agentkit", - "typescript/agentkit/create-onchain-agent", + "typescript/create-onchain-agent", "typescript/framework-extensions/langchain", "typescript/examples/langchain-cdp-chatbot", "typescript/examples/langchain-twitter-chatbot", @@ -1853,6 +1853,16 @@ "@types/node": "*" } }, + "node_modules/@types/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -1963,6 +1973,17 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, + "node_modules/@types/prompts": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz", + "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "kleur": "^3.0.3" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -2980,6 +3001,15 @@ "node": ">=6.14.2" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -3207,6 +3237,33 @@ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -3340,6 +3397,10 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-onchain-agent": { + "resolved": "typescript/create-onchain-agent", + "link": true + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -3350,7 +3411,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4788,6 +4848,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", @@ -5631,6 +5703,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -5866,8 +5950,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isomorphic-ws": { "version": "4.0.1", @@ -6751,7 +6834,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, "engines": { "node": ">=6" } @@ -7097,6 +7179,18 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -7589,6 +7683,131 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "license": "MIT" + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -7794,7 +8013,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -7817,8 +8035,7 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -8035,7 +8252,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -8441,6 +8657,49 @@ "node": ">=10" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -8711,7 +8970,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -8723,7 +8981,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -8826,8 +9083,7 @@ "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "node_modules/slash": { "version": "3.0.0", @@ -8932,6 +9188,18 @@ "node": ">=8" } }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -9949,7 +10217,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -10254,6 +10521,25 @@ "viem": "^2.21.26" } }, + "typescript/create-onchain-agent": { + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "cross-spawn": "^7.0.3", + "ora": "^8.1.0", + "picocolors": "^1.1.0", + "prompts": "^2.4.2" + }, + "bin": { + "create-onchain-agent": "dist/esm/cli.js" + }, + "devDependencies": { + "@types/cross-spawn": "^6.0.6", + "@types/node": "^20.12.10", + "@types/prompts": "^2.4.9" + } + }, "typescript/examples/langchain-cdp-chatbot": { "name": "@coinbase/cdp-langchain-chatbot-example", "version": "1.0.0", @@ -11991,6 +12277,15 @@ "@types/node": "*" } }, + "@types/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -12101,6 +12396,16 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, + "@types/prompts": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz", + "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==", + "dev": true, + "requires": { + "@types/node": "*", + "kleur": "^3.0.3" + } + }, "@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -12817,6 +13122,11 @@ "node-gyp-build": "^4.3.0" } }, + "cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==" + }, "call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -12958,6 +13268,19 @@ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, + "cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "requires": { + "restore-cursor": "^5.0.0" + } + }, + "cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==" + }, "cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -13066,6 +13389,19 @@ "prompts": "^2.0.1" } }, + "create-onchain-agent": { + "version": "file:typescript/create-onchain-agent", + "requires": { + "@types/cross-spawn": "^6.0.6", + "@types/node": "^20.12.10", + "@types/prompts": "^2.4.9", + "cac": "^6.7.14", + "cross-spawn": "^7.0.3", + "ora": "^8.1.0", + "picocolors": "^1.1.0", + "prompts": "^2.4.2" + } + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -13076,7 +13412,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -14132,6 +14467,11 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==" + }, "get-intrinsic": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", @@ -14703,6 +15043,11 @@ "is-extglob": "^2.1.1" } }, + "is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==" + }, "is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -14845,8 +15190,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "isomorphic-ws": { "version": "4.0.1", @@ -15514,8 +15858,7 @@ "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" }, "langsmith": { "version": "0.3.4", @@ -15782,6 +16125,11 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==" + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -16132,6 +16480,78 @@ "word-wrap": "^1.2.5" } }, + "ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "requires": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==" + }, + "chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==" + }, + "emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" + }, + "is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==" + }, + "log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "requires": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "dependencies": { + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==" + } + } + }, + "string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "requires": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, "own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -16270,8 +16690,7 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.7", @@ -16288,8 +16707,7 @@ "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { "version": "2.3.1", @@ -16446,7 +16864,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, "requires": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -16738,6 +17155,30 @@ "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true }, + "restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "requires": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "dependencies": { + "onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "requires": { + "mimic-function": "^5.0.0" + } + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + } + } + }, "retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -16933,7 +17374,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -16941,8 +17381,7 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "side-channel": { "version": "1.1.0", @@ -17015,8 +17454,7 @@ "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "slash": { "version": "3.0.0", @@ -17113,6 +17551,11 @@ } } }, + "stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -17827,7 +18270,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } From eb46ffc0824d9ee4eb93eb2f9f1e79b6ac9abc06 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 16:07:52 -0800 Subject: [PATCH 39/49] bump: 0.1.1 --- typescript/create-onchain-agent/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index 827fabfa7..b7da6ac8a 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -1,7 +1,7 @@ { "name": "create-onchain-agent", "description": "Instantly create onchain-agent applications with Coinbase AgentKit.", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "scripts": { "build": "npm run clean && npm run build:esm+types", From 3de135bebd0cd905ae3671373509b93226b62511 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 16:58:25 -0800 Subject: [PATCH 40/49] chore: typescript readme --- typescript/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/typescript/README.md b/typescript/README.md index a65e3700f..c1cd9ad92 100644 --- a/typescript/README.md +++ b/typescript/README.md @@ -16,3 +16,8 @@ LangChain extension of AgentKit. Enables agentic workflows to interact with onch See [AgentKit LangChain](./framework-extensions/langchain/README.md) to get started! +### `create-onchain-app` + +A quickstart CLI tool to scaffold out an application using Coinbase AgentKit. Runnable via `npm create onchain-agent@latest`. + +See [Create Onchain Agent](./create-onchain-agent/README.md) to get started! \ No newline at end of file From 278113342c196f02aa2f14ce0746106c54a455ec Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 17:02:21 -0800 Subject: [PATCH 41/49] chore: fix constants lint error --- .../create-onchain-agent/src/constants.ts | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/typescript/create-onchain-agent/src/constants.ts b/typescript/create-onchain-agent/src/constants.ts index 2cd0a764e..42b5a9055 100644 --- a/typescript/create-onchain-agent/src/constants.ts +++ b/typescript/create-onchain-agent/src/constants.ts @@ -70,21 +70,29 @@ export const WalletProviderRouteConfigurations: Record< }, Viem: { env: { - topComments: ["Export private key from your Ethereum wallet and save"], + topComments: [ + "Export private key from your Ethereum wallet and save", + "Get keys from CDP Portal: https://portal.cdp.coinbase.com/", + ], required: ["PRIVATE_KEY="], - optional: [], + optional: ["CDP_API_KEY_NAME=", "CDP_API_KEY_PRIVATE_KEY="], }, apiRoute: "evm/viem/route.ts", }, Privy: { env: { - topComments: ["Get keys from Privy Dashboard: https://dashboard.privy.io/"], + topComments: [ + "Get keys from Privy Dashboard: https://dashboard.privy.io/", + "Get keys from CDP Portal: https://portal.cdp.coinbase.com/", + ], required: ["PRIVY_APP_ID=", "PRIVY_APP_SECRET="], optional: [ "CHAIN_ID=", "PRIVY_WALLET_ID=", "PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY=", "PRIVY_WALLET_AUTHORIZATION_KEY_ID=", + "CDP_API_KEY_NAME=", + "CDP_API_KEY_PRIVATE_KEY=", ], }, apiRoute: "evm/privy/route.ts", @@ -93,20 +101,28 @@ export const WalletProviderRouteConfigurations: Record< SVM: { SolanaKeypair: { env: { - topComments: ["Export private key from your Solana wallet and save"], + topComments: [ + "Export private key from your Solana wallet and save", + "Get keys from CDP Portal: https://portal.cdp.coinbase.com/", + ], required: ["SOLANA_PRIVATE_KEY="], - optional: ["SOLANA_RPC_URL="], + optional: ["SOLANA_RPC_URL=", "CDP_API_KEY_NAME=", "CDP_API_KEY_PRIVATE_KEY="], }, apiRoute: "svm/solanaKeypair/route.ts", }, Privy: { env: { - topComments: ["Get keys from Privy Dashboard: https://dashboard.privy.io/"], + topComments: [ + "Get keys from Privy Dashboard: https://dashboard.privy.io/", + "Get keys from CDP Portal: https://portal.cdp.coinbase.com/", + ], required: ["PRIVY_APP_ID=", "PRIVY_APP_SECRET="], optional: [ "PRIVY_WALLET_ID=", "PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY=", "PRIVY_WALLET_AUTHORIZATION_KEY_ID=", + "CDP_API_KEY_NAME=", + "CDP_API_KEY_PRIVATE_KEY=", ], }, apiRoute: "svm/privy/route.ts", From 881ed43a2f2d26edef35739e4226b5ae82f11f17 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 17:10:13 -0800 Subject: [PATCH 42/49] feat: add dynamic message modifier and faucet to template --- .../create-onchain-agent/src/constants.ts | 26 ++++++++++++------- .../next/app/api/agent/evm/cdp/route.ts | 13 ++++++++++ .../next/app/api/agent/evm/privy/route.ts | 18 +++++++++++++ .../next/app/api/agent/evm/viem/route.ts | 17 ++++++++++++ .../next/app/api/agent/svm/privy/route.ts | 24 ++++++++++++++++- .../app/api/agent/svm/solanaKeypair/route.ts | 24 ++++++++++++++++- 6 files changed, 110 insertions(+), 12 deletions(-) diff --git a/typescript/create-onchain-agent/src/constants.ts b/typescript/create-onchain-agent/src/constants.ts index 42b5a9055..ecabf459e 100644 --- a/typescript/create-onchain-agent/src/constants.ts +++ b/typescript/create-onchain-agent/src/constants.ts @@ -74,8 +74,8 @@ export const WalletProviderRouteConfigurations: Record< "Export private key from your Ethereum wallet and save", "Get keys from CDP Portal: https://portal.cdp.coinbase.com/", ], - required: ["PRIVATE_KEY="], - optional: ["CDP_API_KEY_NAME=", "CDP_API_KEY_PRIVATE_KEY="], + required: ["PRIVATE_KEY=", "CDP_API_KEY_NAME=", "CDP_API_KEY_PRIVATE_KEY="], + optional: [], }, apiRoute: "evm/viem/route.ts", }, @@ -85,14 +85,17 @@ export const WalletProviderRouteConfigurations: Record< "Get keys from Privy Dashboard: https://dashboard.privy.io/", "Get keys from CDP Portal: https://portal.cdp.coinbase.com/", ], - required: ["PRIVY_APP_ID=", "PRIVY_APP_SECRET="], + required: [ + "PRIVY_APP_ID=", + "PRIVY_APP_SECRET=", + "CDP_API_KEY_NAME=", + "CDP_API_KEY_PRIVATE_KEY=", + ], optional: [ "CHAIN_ID=", "PRIVY_WALLET_ID=", "PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY=", "PRIVY_WALLET_AUTHORIZATION_KEY_ID=", - "CDP_API_KEY_NAME=", - "CDP_API_KEY_PRIVATE_KEY=", ], }, apiRoute: "evm/privy/route.ts", @@ -105,8 +108,8 @@ export const WalletProviderRouteConfigurations: Record< "Export private key from your Solana wallet and save", "Get keys from CDP Portal: https://portal.cdp.coinbase.com/", ], - required: ["SOLANA_PRIVATE_KEY="], - optional: ["SOLANA_RPC_URL=", "CDP_API_KEY_NAME=", "CDP_API_KEY_PRIVATE_KEY="], + required: ["SOLANA_PRIVATE_KEY=", "CDP_API_KEY_NAME=", "CDP_API_KEY_PRIVATE_KEY="], + optional: ["SOLANA_RPC_URL="], }, apiRoute: "svm/solanaKeypair/route.ts", }, @@ -116,13 +119,16 @@ export const WalletProviderRouteConfigurations: Record< "Get keys from Privy Dashboard: https://dashboard.privy.io/", "Get keys from CDP Portal: https://portal.cdp.coinbase.com/", ], - required: ["PRIVY_APP_ID=", "PRIVY_APP_SECRET="], + required: [ + "PRIVY_APP_ID=", + "PRIVY_APP_SECRET=", + "CDP_API_KEY_NAME=", + "CDP_API_KEY_PRIVATE_KEY=", + ], optional: [ "PRIVY_WALLET_ID=", "PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY=", "PRIVY_WALLET_AUTHORIZATION_KEY_ID=", - "CDP_API_KEY_NAME=", - "CDP_API_KEY_PRIVATE_KEY=", ], }, apiRoute: "svm/privy/route.ts", diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts index 31554cae9..c730d0cbd 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts @@ -103,10 +103,23 @@ async function getOrInitializeAgent(): Promise Date: Fri, 21 Feb 2025 17:13:19 -0800 Subject: [PATCH 43/49] chore: comment regarding faucet --- .../templates/next/app/api/agent/evm/privy/route.ts | 1 + .../templates/next/app/api/agent/evm/viem/route.ts | 1 + .../templates/next/app/api/agent/svm/privy/route.ts | 1 + .../templates/next/app/api/agent/svm/solanaKeypair/route.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts index 61d97d3a4..f3baeb47c 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/privy/route.ts @@ -91,6 +91,7 @@ async function getOrInitializeAgent(): Promise Date: Fri, 21 Feb 2025 17:17:33 -0800 Subject: [PATCH 44/49] chore: fixed package.lock --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 2861f3b75..76da39651 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10522,7 +10522,7 @@ } }, "typescript/create-onchain-agent": { - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "dependencies": { "cac": "^6.7.14", From d38c56bafb2b3de3b5efb9d179000b7de53891fc Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 17:18:49 -0800 Subject: [PATCH 45/49] fix: typo in readme --- typescript/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/README.md b/typescript/README.md index c1cd9ad92..01aa59f2b 100644 --- a/typescript/README.md +++ b/typescript/README.md @@ -16,7 +16,7 @@ LangChain extension of AgentKit. Enables agentic workflows to interact with onch See [AgentKit LangChain](./framework-extensions/langchain/README.md) to get started! -### `create-onchain-app` +### `create-onchain-agent` A quickstart CLI tool to scaffold out an application using Coinbase AgentKit. Runnable via `npm create onchain-agent@latest`. From 5c5dda562da3fb148ffbc2cae4f44209b8078095 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 17:21:55 -0800 Subject: [PATCH 46/49] chore: updated readme to better reflect the outcome of create-onchain-agent --- typescript/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/README.md b/typescript/README.md index 01aa59f2b..37b381ca7 100644 --- a/typescript/README.md +++ b/typescript/README.md @@ -18,6 +18,6 @@ See [AgentKit LangChain](./framework-extensions/langchain/README.md) to get star ### `create-onchain-agent` -A quickstart CLI tool to scaffold out an application using Coinbase AgentKit. Runnable via `npm create onchain-agent@latest`. +A quickstart CLI tool to scaffold out a fullstack chatbot application using Coinbase AgentKit. Runnable via `npm create onchain-agent@latest`. See [Create Onchain Agent](./create-onchain-agent/README.md) to get started! \ No newline at end of file From f5c8ccb326ff7ce19ac41b3b72b9982f74dbfe60 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 17:24:48 -0800 Subject: [PATCH 47/49] bump: 0.1.2 --- typescript/create-onchain-agent/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/create-onchain-agent/package.json b/typescript/create-onchain-agent/package.json index b7da6ac8a..31a85abea 100644 --- a/typescript/create-onchain-agent/package.json +++ b/typescript/create-onchain-agent/package.json @@ -1,7 +1,7 @@ { "name": "create-onchain-agent", "description": "Instantly create onchain-agent applications with Coinbase AgentKit.", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "scripts": { "build": "npm run clean && npm run build:esm+types", From 4df8e5d1766d4073df1c703911d39fa3733a4463 Mon Sep 17 00:00:00 2001 From: Carson Roscoe Date: Fri, 21 Feb 2025 17:46:21 -0800 Subject: [PATCH 48/49] feat: added wallet persistance for cdp --- .../next/app/api/agent/evm/cdp/route.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts index c730d0cbd..7473039c8 100644 --- a/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts +++ b/typescript/create-onchain-agent/templates/next/app/api/agent/evm/cdp/route.ts @@ -14,6 +14,7 @@ import { ChatOpenAI } from "@langchain/openai"; import { MemorySaver } from "@langchain/langgraph"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; import { AgentRequest, AgentResponse } from "@/app/types/api"; +import * as fs from "fs"; /** * AgentKit Integration Route @@ -53,6 +54,9 @@ import { AgentRequest, AgentResponse } from "@/app/types/api"; // The agent let agent: ReturnType; +// Configure a file to persist the agent's CDP MPC Wallet Data +const WALLET_DATA_FILE = "wallet_data.txt"; + /** * Initializes and returns an instance of the AI agent. * If an agent instance already exists, it returns the existing one. @@ -74,11 +78,24 @@ async function getOrInitializeAgent(): Promise Date: Fri, 21 Feb 2025 17:55:44 -0800 Subject: [PATCH 49/49] chore: updated package-lock after bump --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 76da39651..75deea26a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10522,7 +10522,7 @@ } }, "typescript/create-onchain-agent": { - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "dependencies": { "cac": "^6.7.14",