diff --git a/package-lock.json b/package-lock.json index c04b67e93..cf28d96e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "typescript/framework-extensions/langchain", "typescript/examples/langchain-cdp-chatbot", "typescript/examples/langchain-twitter-chatbot", - "typescript/examples/langchain-farcaster-chatbot" + "typescript/examples/langchain-farcaster-chatbot", + "typescript/examples/langchain-privy-chatbot" ], "devDependencies": { "@types/jest": "^29.5.14", @@ -580,6 +581,10 @@ "resolved": "typescript/examples/langchain-farcaster-chatbot", "link": true }, + "node_modules/@coinbase/langchain-privy-chatbot-example": { + "resolved": "typescript/examples/langchain-privy-chatbot", + "link": true + }, "node_modules/@coinbase/twitter-langchain-chatbot-example": { "resolved": "typescript/examples/langchain-twitter-chatbot", "link": true @@ -1390,6 +1395,43 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@privy-io/server-auth": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/@privy-io/server-auth/-/server-auth-1.18.5.tgz", + "integrity": "sha512-EYvGRCewVZlpsyT71IY0ToKzqNUVaNJjXR2oHOXciB+URl1B9oYWtu6OIi9FzwF2URQcjexDfnN1TDXSGdgNhg==", + "dependencies": { + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@solana/web3.js": "^1.95.8", + "canonicalize": "^2.0.0", + "dotenv": "^16.0.3", + "jose": "^4.10.4", + "node-fetch-native": "^1.4.0", + "redaxios": "^0.5.1", + "svix": ">=1.29.0 <= 1.37.0 || ^1.40.0", + "ts-case-convert": "^2.0.2", + "type-fest": "^3.6.1" + }, + "peerDependencies": { + "viem": "^2" + }, + "peerDependenciesMeta": { + "viem": { + "optional": true + } + } + }, + "node_modules/@privy-io/server-auth/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1517,6 +1559,11 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -2827,6 +2874,11 @@ } ] }, + "node_modules/canonicalize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-2.0.0.tgz", + "integrity": "sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w==" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4267,6 +4319,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" + }, "node_modules/fast-stable-stringify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", @@ -6338,6 +6395,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tiktoken": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.16.tgz", @@ -6956,6 +7021,11 @@ } } }, + "node_modules/node-fetch-native": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", + "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==" + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -7819,6 +7889,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8008,6 +8083,11 @@ "node": ">=8.10.0" } }, + "node_modules/redaxios": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/redaxios/-/redaxios-0.5.1.tgz", + "integrity": "sha512-FSD2AmfdbkYwl7KDExYQlVvIrFz6Yd83pGfaGjBzM9F6rpq8g652Q4Yq5QD4c+nf4g2AgeElv1y+8ajUPiOYMg==" + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -8085,8 +8165,7 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { "version": "1.22.10", @@ -8846,6 +8925,41 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svix": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/svix/-/svix-1.45.1.tgz", + "integrity": "sha512-KQ4G9xralBsmAx/5VFVlOivFlBux2EhRwPJOstmr+US2w2Uz9K31kVqmwbYbsxJIlZtOrWrmVCz6rQd1Hoq+vA==", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "@types/node": "^22.7.5", + "es6-promise": "^4.2.8", + "fast-sha256": "^1.3.0", + "svix-fetch": "^3.0.0", + "url-parse": "^1.5.10" + } + }, + "node_modules/svix-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/svix-fetch/-/svix-fetch-3.0.0.tgz", + "integrity": "sha512-rcADxEFhSqHbraZIsjyZNh4TF6V+koloX1OzZ+AQuObX9mZ2LIMhm1buZeuc5BIZPftZpJCMBsSiBaeszo9tRw==", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/svix/node_modules/@types/node": { + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/svix/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, "node_modules/synckit": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", @@ -8967,6 +9081,11 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-case-convert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-case-convert/-/ts-case-convert-2.1.0.tgz", + "integrity": "sha512-Ye79el/pHYXfoew6kqhMwCoxp4NWjKNcm2kBzpmEMIU9dd9aBmHNNFtZ+WTm0rz1ngyDmfqDXDlyUnBXayiD0w==" + }, "node_modules/ts-jest": { "version": "29.2.5", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", @@ -9456,6 +9575,15 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -9589,6 +9717,11 @@ "node": ">=12" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -9867,6 +10000,7 @@ "license": "Apache-2.0", "dependencies": { "@coinbase/coinbase-sdk": "^0.17.0", + "@privy-io/server-auth": "^1.18.4", "@solana/web3.js": "^1.98.0", "md5": "^2.3.0", "reflect-metadata": "^0.2.2", @@ -10009,6 +10143,24 @@ } } }, + "typescript/examples/langchain-privy-chatbot": { + "name": "@coinbase/langchain-privy-chatbot-example", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@coinbase/agentkit": "^0.1.0", + "@coinbase/agentkit-langchain": "^0.1.0", + "@langchain/core": "^0.3.19", + "@langchain/langgraph": "^0.2.21", + "@langchain/openai": "^0.3.14", + "dotenv": "^16.4.5", + "zod": "^3.22.4" + }, + "devDependencies": { + "nodemon": "^3.1.0", + "ts-node": "^10.9.2" + } + }, "typescript/examples/langchain-twitter-chatbot": { "name": "@coinbase/twitter-langchain-chatbot-example", "version": "1.0.0", @@ -10424,6 +10576,7 @@ "version": "file:typescript/agentkit", "requires": { "@coinbase/coinbase-sdk": "^0.17.0", + "@privy-io/server-auth": "^1.18.4", "@solana/web3.js": "^1.98.0", "@types/jest": "^29.5.14", "@types/secp256k1": "^4.0.6", @@ -10558,6 +10711,20 @@ } } }, + "@coinbase/langchain-privy-chatbot-example": { + "version": "file:typescript/examples/langchain-privy-chatbot", + "requires": { + "@coinbase/agentkit": "^0.1.0", + "@coinbase/agentkit-langchain": "^0.1.0", + "@langchain/core": "^0.3.19", + "@langchain/langgraph": "^0.2.21", + "@langchain/openai": "^0.3.14", + "dotenv": "^16.4.5", + "nodemon": "^3.1.0", + "ts-node": "^10.9.2", + "zod": "^3.22.4" + } + }, "@coinbase/twitter-langchain-chatbot-example": { "version": "file:typescript/examples/langchain-twitter-chatbot", "requires": { @@ -11184,6 +11351,31 @@ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true }, + "@privy-io/server-auth": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/@privy-io/server-auth/-/server-auth-1.18.5.tgz", + "integrity": "sha512-EYvGRCewVZlpsyT71IY0ToKzqNUVaNJjXR2oHOXciB+URl1B9oYWtu6OIi9FzwF2URQcjexDfnN1TDXSGdgNhg==", + "requires": { + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@solana/web3.js": "^1.95.8", + "canonicalize": "^2.0.0", + "dotenv": "^16.0.3", + "jose": "^4.10.4", + "node-fetch-native": "^1.4.0", + "redaxios": "^0.5.1", + "svix": ">=1.29.0 <= 1.37.0 || ^1.40.0", + "ts-case-convert": "^2.0.2", + "type-fest": "^3.6.1" + }, + "dependencies": { + "type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==" + } + } + }, "@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -11301,6 +11493,11 @@ } } }, + "@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==" + }, "@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -12270,6 +12467,11 @@ "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==", "dev": true }, + "canonicalize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-2.0.0.tgz", + "integrity": "sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w==" + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -13326,6 +13528,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" + }, "fast-stable-stringify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", @@ -14783,6 +14990,11 @@ } } }, + "jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==" + }, "js-tiktoken": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.16.tgz", @@ -15244,6 +15456,11 @@ "whatwg-url": "^5.0.0" } }, + "node-fetch-native": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", + "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==" + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -15856,6 +16073,11 @@ "side-channel": "^1.1.0" } }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -15993,6 +16215,11 @@ "picomatch": "^2.2.1" } }, + "redaxios": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/redaxios/-/redaxios-0.5.1.tgz", + "integrity": "sha512-FSD2AmfdbkYwl7KDExYQlVvIrFz6Yd83pGfaGjBzM9F6rpq8g652Q4Yq5QD4c+nf4g2AgeElv1y+8ajUPiOYMg==" + }, "redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -16052,8 +16279,7 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "resolve": { "version": "1.22.10", @@ -16609,6 +16835,43 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "svix": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/svix/-/svix-1.45.1.tgz", + "integrity": "sha512-KQ4G9xralBsmAx/5VFVlOivFlBux2EhRwPJOstmr+US2w2Uz9K31kVqmwbYbsxJIlZtOrWrmVCz6rQd1Hoq+vA==", + "requires": { + "@stablelib/base64": "^1.0.0", + "@types/node": "^22.7.5", + "es6-promise": "^4.2.8", + "fast-sha256": "^1.3.0", + "svix-fetch": "^3.0.0", + "url-parse": "^1.5.10" + }, + "dependencies": { + "@types/node": { + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "requires": { + "undici-types": "~6.20.0" + } + }, + "undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + } + } + }, + "svix-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/svix-fetch/-/svix-fetch-3.0.0.tgz", + "integrity": "sha512-rcADxEFhSqHbraZIsjyZNh4TF6V+koloX1OzZ+AQuObX9mZ2LIMhm1buZeuc5BIZPftZpJCMBsSiBaeszo9tRw==", + "requires": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, "synckit": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", @@ -16706,6 +16969,11 @@ "dev": true, "requires": {} }, + "ts-case-convert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-case-convert/-/ts-case-convert-2.1.0.tgz", + "integrity": "sha512-Ye79el/pHYXfoew6kqhMwCoxp4NWjKNcm2kBzpmEMIU9dd9aBmHNNFtZ+WTm0rz1ngyDmfqDXDlyUnBXayiD0w==" + }, "ts-jest": { "version": "29.2.5", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", @@ -17015,6 +17283,15 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -17116,6 +17393,11 @@ "iconv-lite": "0.6.3" } }, + "whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index 458043bd7..628b09247 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "typescript/framework-extensions/langchain", "typescript/examples/langchain-cdp-chatbot", "typescript/examples/langchain-twitter-chatbot", - "typescript/examples/langchain-farcaster-chatbot" + "typescript/examples/langchain-farcaster-chatbot", + "typescript/examples/langchain-privy-chatbot" ], "packageManager": "npm@8.9.0", "scripts": { @@ -54,4 +55,4 @@ "typedoc": "^0.27.2", "typescript": "^5.4.5" } -} +} \ No newline at end of file diff --git a/tsconfig.base.json b/tsconfig.base.json index 84f2b2193..2358d807d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -7,7 +7,7 @@ "strict": true, "resolveJsonModule": true, "esModuleInterop": true, - "moduleResolution": "node", + "moduleResolution": "node16", "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, diff --git a/typescript/agentkit/CHANGELOG.md b/typescript/agentkit/CHANGELOG.md index 57cb497a9..028c983b7 100644 --- a/typescript/agentkit/CHANGELOG.md +++ b/typescript/agentkit/CHANGELOG.md @@ -7,6 +7,7 @@ - Added `svmWalletProvider` abstract class for interacting with Solana. - Added `solanaKeypairWalletProvider` to concretely implement `svmWalletProvider` with a local keypair. - Added gas configuration parameters (`gasLimitMultiplier`, `feePerGasMultiplier`) to `CdpWalletProvider` and `ViemWalletProvider`. +- Added `privyWalletProvider` to use a Privy server wallet for agent actions. ## [0.1.2] - 2025-02-07 diff --git a/typescript/agentkit/README.md b/typescript/agentkit/README.md index 4ca5b8e15..1df076163 100644 --- a/typescript/agentkit/README.md +++ b/typescript/agentkit/README.md @@ -27,6 +27,9 @@ AgentKit is a framework for easily enabling AI agents to take actions onchain. I - [Configuring gas parameters](#configuring-cdpwalletprovider-gas-parameters) - [ViemWalletProvider](#viemwalletprovider) - [Configuring gas parameters](#configuring-viemwalletprovider-gas-parameters) + - [PrivyWalletProvider](#privywalletprovider) + - [Authorization Keys](#authorization-keys) + - [Exporting Privy Wallet information](#exporting-privy-wallet-information) - [Contributing](#contributing) ## Getting Started @@ -398,6 +401,7 @@ Wallet providers give an agent access to a wallet. AgentKit currently supports t EVM: - [CdpWalletProvider](./src/wallet-providers/cdpWalletProvider.ts) - [ViemWalletProvider](./src/wallet-providers/viemWalletProvider.ts) +- [PrivyWalletProvider](./src/wallet-providers/privyWalletProvider.ts) ### CdpWalletProvider @@ -543,6 +547,49 @@ const walletProvider = new ViemWalletProvider(client, { }); ``` +### PrivyWalletProvider + +The `PrivyWalletProvider` is a wallet provider that uses [Privy Server Wallets](https://docs.privy.io/guide/server-wallets/). This implementation extends the `ViemWalletProvider`. + +```typescript +import { PrivyWalletProvider } from "@coinbase/agentkit"; + +// Configure Wallet Provider +const config = { + appId: "PRIVY_APP_ID", + appSecret: "PRIVY_APP_SECRET", + chainId: "84532", // base-sepolia + walletId: "PRIVY_WALLET_ID", // optional, otherwise a new wallet will be created + authorizationPrivateKey: PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY, // optional, required if your account is using authorization keys + authorizationKeyId: PRIVY_WALLET_AUTHORIZATION_KEY_ID, // optional, only required to create a new wallet if walletId is not provided +}; + +const walletProvider = await PrivyWalletProvider.configureWithWallet(config); +``` + +#### Authorization Keys + +Privy offers the option to use authorization keys to secure your server wallets. + +You can manage authorization keys from your [Privy dashboard](https://dashboard.privy.io/account) or programmatically [using the API](https://docs.privy.io/guide/server-wallets/authorization/signatures). + +When using authorization keys, you must provide the `authorizationPrivateKey` and `authorizationKeyId` parameters to the `configureWithWallet` method if you are creating a new wallet. Please note that when creating a key, if you enable "Create and modify wallets", you will be required to use that key when creating new wallets via the PrivyWalletProvider. + +#### Exporting Privy Wallet information + +The `PrivyWalletProvider` can export wallet information by calling the `exportWallet` method. + +```typescript +const walletData = await walletProvider.exportWallet(); + +// walletData will be in the following format: +{ + walletId: string; + authorizationKey: string | undefined; + chainId: string | undefined; +} +``` + ## Contributing diff --git a/typescript/agentkit/package.json b/typescript/agentkit/package.json index e773b2622..3cd03e182 100644 --- a/typescript/agentkit/package.json +++ b/typescript/agentkit/package.json @@ -41,6 +41,7 @@ "dependencies": { "@coinbase/coinbase-sdk": "^0.17.0", "@solana/web3.js": "^1.98.0", + "@privy-io/server-auth": "^1.18.4", "md5": "^2.3.0", "reflect-metadata": "^0.2.2", "twitter-api-v2": "^1.18.2", diff --git a/typescript/agentkit/src/network/network.ts b/typescript/agentkit/src/network/network.ts index a26d9aa4f..a91239bab 100644 --- a/typescript/agentkit/src/network/network.ts +++ b/typescript/agentkit/src/network/network.ts @@ -11,6 +11,7 @@ import { polygonMumbai, polygon, } from "viem/chains"; +import * as chains from "viem/chains"; /** * Maps EVM chain IDs to Coinbase network IDs @@ -56,3 +57,14 @@ export const NETWORK_ID_TO_VIEM_CHAIN: Record = { "optimism-mainnet": optimism, "optimism-sepolia": optimismSepolia, }; + +/** + * Get a chain from the viem chains object + * + * @param id - The chain ID + * @returns The chain + */ +export const getChain = (id: string): Chain => { + const chainList = Object.values(chains); + return chainList.find(chain => chain.id === parseInt(id)) as Chain; +}; diff --git a/typescript/agentkit/src/wallet-providers/index.ts b/typescript/agentkit/src/wallet-providers/index.ts index 6cae3efa0..55267dd48 100644 --- a/typescript/agentkit/src/wallet-providers/index.ts +++ b/typescript/agentkit/src/wallet-providers/index.ts @@ -4,3 +4,4 @@ export * from "./viemWalletProvider"; export * from "./cdpWalletProvider"; export * from "./svmWalletProvider"; export * from "./solanaKeypairWalletProvider"; +export * from "./privyWalletProvider"; diff --git a/typescript/agentkit/src/wallet-providers/privyWalletProvider.ts b/typescript/agentkit/src/wallet-providers/privyWalletProvider.ts new file mode 100644 index 000000000..58319429b --- /dev/null +++ b/typescript/agentkit/src/wallet-providers/privyWalletProvider.ts @@ -0,0 +1,169 @@ +import { PrivyClient } from "@privy-io/server-auth"; +import { createViemAccount } from "@privy-io/server-auth/viem"; +import { ViemWalletProvider } from "./viemWalletProvider"; +import { createWalletClient, http, WalletClient } from "viem"; +import { getChain } from "../network/network"; +/** + * Configuration options for the Privy wallet provider. + * + * @interface + */ +interface PrivyWalletConfig { + /** The Privy application ID */ + appId: string; + /** The Privy application secret */ + appSecret: string; + /** The ID of the wallet to use, if not provided a new wallet will be created */ + walletId?: string; + /** Optional chain ID to connect to */ + chainId?: string; + /** Optional authorization key for the wallet API */ + authorizationPrivateKey?: string; + /** Optional authorization key ID for creating new wallets */ + authorizationKeyId?: string; +} + +type PrivyWalletExport = { + walletId: string; + authorizationPrivateKey: string | undefined; + chainId: string | undefined; +}; + +/** + * A wallet provider that uses Privy's server wallet API. + * This provider extends the ViemWalletProvider to provide Privy-specific wallet functionality + * while maintaining compatibility with the base wallet provider interface. + */ +export class PrivyWalletProvider extends ViemWalletProvider { + #walletId: string; + #authorizationPrivateKey: string | undefined; + + /** + * Private constructor to enforce use of factory method. + * + * @param walletClient - The Viem wallet client instance + * @param config - The configuration options for the Privy wallet + */ + private constructor( + walletClient: WalletClient, + config: PrivyWalletConfig & { walletId: string }, // Require walletId in constructor + ) { + super(walletClient); + this.#walletId = config.walletId; // Now guaranteed to exist + this.#authorizationPrivateKey = config.authorizationPrivateKey; + } + + /** + * Creates and configures a new PrivyWalletProvider instance. + * + * @param config - The configuration options for the Privy wallet + * @returns A configured PrivyWalletProvider instance + * + * @example + * ```typescript + * const provider = await PrivyWalletProvider.configureWithWallet({ + * appId: "your-app-id", + * appSecret: "your-app-secret", + * walletId: "wallet-id", + * chainId: "84532" + * }); + * ``` + */ + public static async configureWithWallet(config: PrivyWalletConfig): Promise { + const privy = new PrivyClient(config.appId, config.appSecret, { + walletApi: config.authorizationPrivateKey + ? { + authorizationPrivateKey: config.authorizationPrivateKey, + } + : undefined, + }); + + let walletId: string; + let address: `0x${string}`; + + if (!config.walletId) { + if (config.authorizationPrivateKey && !config.authorizationKeyId) { + throw new Error( + "authorizationKeyId is required when creating a new wallet with an authorization key, this can be found in your Privy Dashboard", + ); + } + + if (config.authorizationKeyId && !config.authorizationPrivateKey) { + throw new Error( + "authorizationPrivateKey is required when creating a new wallet with an authorizationKeyId. " + + "If you don't have it, you can create a new one in your Privy Dashboard, or delete the authorization key.", + ); + } + + try { + const wallet = await privy.walletApi.create({ + chainType: "ethereum", + authorizationKeyIds: config.authorizationKeyId ? [config.authorizationKeyId] : undefined, + }); + walletId = wallet.id; + address = wallet.address as `0x${string}`; + } catch (error) { + console.error(error); + if ( + error instanceof Error && + error.message.includes("Missing `privy-authorization-signature` header") + ) { + // Providing a more informative error message, see context: https://github.com/coinbase/agentkit/pull/242#discussion_r1956428617 + throw new Error( + "Privy error: you have an authorization key on your account which can create and modify wallets, please delete this key or pass it to the PrivyWalletProvider to create a new wallet", + ); + } + throw new Error("Failed to create wallet"); + } + } else { + walletId = config.walletId; + const wallet = await privy.walletApi.getWallet({ id: walletId }); + if (!wallet) { + throw new Error(`Wallet with ID ${walletId} not found`); + } + address = wallet.address as `0x${string}`; + } + + const account = await createViemAccount({ + walletId, + address, + privy, + }); + + const chainId = config.chainId || "84532"; + + const chain = getChain(chainId); + if (!chain) { + throw new Error(`Chain with ID ${chainId} not found`); + } + + const walletClient = createWalletClient({ + account, + chain, + transport: http(), + }); + return new PrivyWalletProvider(walletClient, { ...config, walletId }); + } + + /** + * Gets the name of the wallet provider. + * + * @returns The string identifier for this wallet provider + */ + getName(): string { + return "privy_wallet_provider"; + } + + /** + * Exports the wallet data. + * + * @returns The wallet data + */ + exportWallet(): PrivyWalletExport { + return { + walletId: this.#walletId, + authorizationPrivateKey: this.#authorizationPrivateKey, + chainId: this.getNetwork().chainId, + }; + } +} diff --git a/typescript/agentkit/tsconfig.json b/typescript/agentkit/tsconfig.json index 2e2cc2c33..58cc4a26b 100644 --- a/typescript/agentkit/tsconfig.json +++ b/typescript/agentkit/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", - "rootDir": "./src" + "rootDir": "./src", + "module": "Node16" }, "include": ["src/**/*.ts"], "exclude": ["src/tests"] diff --git a/typescript/examples/langchain-privy-chatbot/.env-local b/typescript/examples/langchain-privy-chatbot/.env-local new file mode 100644 index 000000000..25e2978b1 --- /dev/null +++ b/typescript/examples/langchain-privy-chatbot/.env-local @@ -0,0 +1,14 @@ +OPENAI_API_KEY= + +# Privy Configuration - get these from your Privy dashboard +PRIVY_APP_ID= +PRIVY_APP_SECRET= + +# Optional Wallet ID, otherwise a new wallet will be created +PRIVY_WALLET_ID= + +# Optional Authorization Private Key, if you are using them for your server wallets +PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY= + +# Optional Authorization Key ID from your Privy Dashboard, if you want to create a new wallet +PRIVY_WALLET_AUTHORIZATION_KEY_ID= \ No newline at end of file diff --git a/typescript/examples/langchain-privy-chatbot/.eslintrc.json b/typescript/examples/langchain-privy-chatbot/.eslintrc.json new file mode 100644 index 000000000..fc9385e78 --- /dev/null +++ b/typescript/examples/langchain-privy-chatbot/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": ["../../../.eslintrc.base.json"] +} diff --git a/typescript/examples/langchain-privy-chatbot/README.md b/typescript/examples/langchain-privy-chatbot/README.md new file mode 100644 index 000000000..890f1202e --- /dev/null +++ b/typescript/examples/langchain-privy-chatbot/README.md @@ -0,0 +1,52 @@ +# Privy AgentKit LangChain Extension Examples - Chatbot Typescript + +This example demonstrates an agent setup as a self-aware terminal style chatbot with a [Privy server wallet](https://docs.privy.io/guide/server-wallets/). + +Privy's server wallets enable you to securely provision and manage cross-chain wallets via a flexible API - learn more at https://docs.privy.io/guide/server-wallets/. The Agentkit integration assumes you have a Privy server wallet ID which you want to use for your agent - creation and management of Privy wallets can be done via the Privy dashboard or API. + +## Ask the chatbot to engage in the Web3 ecosystem! + +- "Transfer a portion of your ETH to a random address" +- "What is the price of BTC?" +- "What kind of wallet do you have?" + +## Requirements + +- Node.js 18+ +- [Privy](https://dashboard.privy.io/apps) (see ENV Vars below for details) + +### Checking Node Version + +Before using the example, ensure that you have the correct version of Node.js installed. The example requires Node.js 18 or higher. You can check your Node version by running: + +```bash +node --version +npm --version +``` + +## Installation + +```bash +npm install +``` + +## Run the Chatbot + +### Set ENV Vars + +- Ensure the following ENV Vars from your Privy dashboard are set in `.env`: + - PRIVY_APP_ID= + - PRIVY_APP_SECRET= + - PRIVY_WALLET_ID=[optional, otherwise a new wallet will be created] + - PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY=[optional, only if you are using authorization keys for your server wallets] + - PRIVY_WALLET_AUTHORIZATION_KEY_ID=[optional, only if walletId is not provided in order to create a new wallet, this can be found in your Privy Dashboard] + +You can manage authorization keys from your [Privy dashboard](https://dashboard.privy.io/account) or programmatically [using the API](https://docs.privy.io/guide/server-wallets/authorization/signatures). + +```bash +npm start +``` + +## License + +Apache-2.0 diff --git a/typescript/examples/langchain-privy-chatbot/chatbot.ts b/typescript/examples/langchain-privy-chatbot/chatbot.ts new file mode 100644 index 000000000..56383c2bd --- /dev/null +++ b/typescript/examples/langchain-privy-chatbot/chatbot.ts @@ -0,0 +1,284 @@ +import { + AgentKit, + PrivyWalletProvider, + wethActionProvider, + walletActionProvider, + erc20ActionProvider, + pythActionProvider, +} from "@coinbase/agentkit"; +import { getLangChainTools } from "@coinbase/agentkit-langchain"; +import { HumanMessage } from "@langchain/core/messages"; +import { MemorySaver } from "@langchain/langgraph"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { ChatOpenAI } from "@langchain/openai"; +import * as dotenv from "dotenv"; +import * as readline from "readline"; +import fs from "fs"; + +dotenv.config(); + +const WALLET_DATA_FILE = "wallet_data.txt"; + +/** + * Validates that required environment variables are set + * + * @throws {Error} - If required environment variables are missing + * @returns {void} + */ +function validateEnvironment(): void { + const missingVars: string[] = []; + + // Check required variables + const requiredVars = ["OPENAI_API_KEY", "PRIVY_APP_ID", "PRIVY_APP_SECRET"]; + requiredVars.forEach(varName => { + if (!process.env[varName]) { + missingVars.push(varName); + } + }); + + // Exit if any required variables are missing + if (missingVars.length > 0) { + console.error("Error: Required environment variables are not set"); + missingVars.forEach(varName => { + console.error(`${varName}=your_${varName.toLowerCase()}_here`); + }); + process.exit(1); + } + + // Warn about optional CHAIN_ID + if (!process.env.CHAIN_ID) { + console.warn("Warning: CHAIN_ID not set, defaulting to base-sepolia testnet"); + } +} + +// Add this right after imports and before any other code +validateEnvironment(); + +/** + * Initialize the agent with Privy Agentkit + * + * @returns Agent executor and config + */ +async function initializeAgent() { + try { + // Initialize LLM + const llm = new ChatOpenAI({ + model: "gpt-4o-mini", + }); + + // Configure Wallet Provider + const config = { + appId: process.env.PRIVY_APP_ID as string, + appSecret: process.env.PRIVY_APP_SECRET as string, + chainId: process.env.CHAIN_ID || "84532", + walletId: process.env.PRIVY_WALLET_ID as string, + authorizationPrivateKey: process.env.PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY, + authorizationKeyId: process.env.PRIVY_WALLET_AUTHORIZATION_KEY_ID, + }; + + // Try to load saved wallet data + if (fs.existsSync(WALLET_DATA_FILE)) { + const savedWallet = JSON.parse(fs.readFileSync(WALLET_DATA_FILE, "utf8")); + config.walletId = savedWallet.walletId; + config.authorizationPrivateKey = savedWallet.authorizationPrivateKey; + config.chainId = savedWallet.chainId; + } + + const walletProvider = await PrivyWalletProvider.configureWithWallet(config); + + // Initialize AgentKit + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ], + }); + + const tools = await getLangChainTools(agentkit); + + // Store buffered conversation history in memory + const memory = new MemorySaver(); + const agentConfig = { configurable: { thread_id: "Privy AgentKit Chatbot Example!" } }; + + // Create React Agent using the LLM and Privy AgentKit tools + const agent = createReactAgent({ + llm, + tools, + checkpointSaver: memory, + messageModifier: ` + You are a helpful agent with a Privy server wallet that can interact onchain using the Coinbase Developer + Platform AgentKit. You are empowered to interact onchain using your tools. If you ever need funds, you can + request them from the faucet if you are on network ID 'base-sepolia'. If not, you can provide your wallet + details and request funds from the user. Before executing your first action, get the wallet details to see + what network you're on. If there is a 5XX (internal) HTTP error code, ask the user to try again later. If + someone asks you to do something you can't do with your currently available tools, you must say so, and + encourage them to implement it themselves using the CDP SDK + Agentkit, recommend they go to + docs.cdp.coinbase.com for more information. Be concise and helpful with your responses. Refrain from + restating your tools' descriptions unless it is explicitly requested. + `, + }); + + // Save wallet data + const exportedWallet = walletProvider.exportWallet(); + fs.writeFileSync(WALLET_DATA_FILE, JSON.stringify(exportedWallet)); + + return { agent, config: agentConfig }; + } catch (error) { + console.error("Failed to initialize agent:", error); + throw error; // Re-throw to be handled by caller + } +} + +/** + * Run the agent autonomously with specified intervals + * + * @param agent - The agent executor + * @param config - Agent configuration + * @param interval - Time interval between actions in seconds + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +async function runAutonomousMode(agent: any, config: any, interval = 10) { + console.log("Starting autonomous mode..."); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + const thought = + "Be creative and do something interesting on the blockchain. " + + "Choose an action or set of actions and execute it that highlights your abilities."; + + const stream = await agent.stream({ messages: [new HumanMessage(thought)] }, config); + + for await (const chunk of stream) { + if ("agent" in chunk) { + console.log(chunk.agent.messages[0].content); + } else if ("tools" in chunk) { + console.log(chunk.tools.messages[0].content); + } + console.log("-------------------"); + } + + await new Promise(resolve => setTimeout(resolve, interval * 1000)); + } catch (error) { + if (error instanceof Error) { + console.error("Error:", error.message); + } + process.exit(1); + } + } +} + +/** + * Run the agent interactively based on user input + * + * @param agent - The agent executor + * @param config - Agent configuration + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +async function runChatMode(agent: any, config: any) { + console.log("Starting chat mode... Type 'exit' to end."); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const question = (prompt: string): Promise => + new Promise(resolve => rl.question(prompt, resolve)); + + try { + // eslint-disable-next-line no-constant-condition + while (true) { + const userInput = await question("\nPrompt: "); + + if (userInput.toLowerCase() === "exit") { + break; + } + + const stream = await agent.stream({ messages: [new HumanMessage(userInput)] }, config); + + for await (const chunk of stream) { + if ("agent" in chunk) { + console.log(chunk.agent.messages[0].content); + } else if ("tools" in chunk) { + console.log(chunk.tools.messages[0].content); + } + console.log("-------------------"); + } + } + } catch (error) { + if (error instanceof Error) { + console.error("Error:", error.message); + } + process.exit(1); + } finally { + rl.close(); + } +} + +/** + * Choose whether to run in autonomous or chat mode based on user input + * + * @returns Selected mode + */ +async function chooseMode(): Promise<"chat" | "auto"> { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const question = (prompt: string): Promise => + new Promise(resolve => rl.question(prompt, resolve)); + + // eslint-disable-next-line no-constant-condition + while (true) { + console.log("\nAvailable modes:"); + console.log("1. chat - Interactive chat mode"); + console.log("2. auto - Autonomous action mode"); + + const choice = (await question("\nChoose a mode (enter number or name): ")) + .toLowerCase() + .trim(); + + if (choice === "1" || choice === "chat") { + rl.close(); + return "chat"; + } else if (choice === "2" || choice === "auto") { + rl.close(); + return "auto"; + } + console.log("Invalid choice. Please try again."); + } +} + +/** + * Start the chatbot agent + */ +async function main() { + try { + const { agent, config } = await initializeAgent(); + const mode = await chooseMode(); + + if (mode === "chat") { + await runChatMode(agent, config); + } else { + await runAutonomousMode(agent, config); + } + } catch (error) { + if (error instanceof Error) { + console.error("Error:", error.message); + } + process.exit(1); + } +} + +if (require.main === module) { + console.log("Starting Agent..."); + main().catch(error => { + console.error("Fatal error:", error); + process.exit(1); + }); +} diff --git a/typescript/examples/langchain-privy-chatbot/package.json b/typescript/examples/langchain-privy-chatbot/package.json new file mode 100644 index 000000000..a48c7d8b1 --- /dev/null +++ b/typescript/examples/langchain-privy-chatbot/package.json @@ -0,0 +1,28 @@ +{ + "name": "@coinbase/langchain-privy-chatbot-example", + "description": "Privy Agentkit LangChain Extension Chatbot Example", + "version": "1.0.0", + "author": "Coinbase Inc.", + "license": "Apache-2.0", + "scripts": { + "start": "NODE_OPTIONS='--no-warnings' ts-node ./chatbot.ts", + "dev": "nodemon ./chatbot.ts", + "lint": "eslint -c .eslintrc.json *.ts", + "lint-fix": "eslint -c .eslintrc.json *.ts --fix", + "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"", + "format-check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"" + }, + "dependencies": { + "@coinbase/agentkit": "^0.1.0", + "@coinbase/agentkit-langchain": "^0.1.0", + "@langchain/langgraph": "^0.2.21", + "@langchain/openai": "^0.3.14", + "@langchain/core": "^0.3.19", + "dotenv": "^16.4.5", + "zod": "^3.22.4" + }, + "devDependencies": { + "nodemon": "^3.1.0", + "ts-node": "^10.9.2" + } +} diff --git a/typescript/examples/langchain-privy-chatbot/tsconfig.json b/typescript/examples/langchain-privy-chatbot/tsconfig.json new file mode 100644 index 000000000..7f58cda63 --- /dev/null +++ b/typescript/examples/langchain-privy-chatbot/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "preserveSymlinks": true, + "outDir": "./dist", + "rootDir": ".", + "module": "Node16", + }, + "include": ["*.ts"] +} diff --git a/typescript/framework-extensions/langchain/tsconfig.json b/typescript/framework-extensions/langchain/tsconfig.json index 68cc79109..e6f5e5ea8 100644 --- a/typescript/framework-extensions/langchain/tsconfig.json +++ b/typescript/framework-extensions/langchain/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", - "rootDir": "./src" + "rootDir": "./src", + "module": "Node16" }, "include": ["src/**/*.ts"], "exclude": ["src/tests"]