diff --git a/.env.example b/.env.example
index 749195a4d0..be40a045d5 100644
--- a/.env.example
+++ b/.env.example
@@ -5,8 +5,15 @@ DISCORD_VOICE_CHANNEL_ID= # The ID of the voice channel the bot should join (opt
# AI Model API Keys
OPENAI_API_KEY= # OpenAI API key, starting with sk-
+SMALL_OPENAI_MODEL= # Default: gpt-4o-mini
+MEDIUM_OPENAI_MODEL= # Default: gpt-4o
+LARGE_OPENAI_MODEL= # Default: gpt-4o
+EMBEDDING_OPENAI_MODEL= # Default: text-embedding-3-small
+IMAGE_OPENAI_MODEL= # Default: dall-e-3
+# Eternal AI's Decentralized Inference API
ETERNALAI_URL=
+ETERNALAI_MODEL= #Default: "neuralmagic/Meta-Llama-3.1-405B-Instruct-quantized.w4a16"
ETERNALAI_API_KEY=
GROK_API_KEY= # GROK API Key
@@ -16,6 +23,14 @@ GOOGLE_GENERATIVE_AI_API_KEY= # Gemini API key
ALI_BAILIAN_API_KEY= # Ali Bailian API Key
VOLENGINE_API_KEY= # VolEngine API Key
+NANOGPT_API_KEY= # NanoGPT API Key
+
+HYPERBOLIC_API_KEY= # Hyperbolic API Key
+HYPERBOLIC_MODEL=
+IMAGE_HYPERBOLIC_MODEL= # Default: FLUX.1-dev
+SMALL_HYPERBOLIC_MODEL= # Default: meta-llama/Llama-3.2-3B-Instruct
+MEDIUM_HYPERBOLIC_MODEL= # Default: meta-llama/Meta-Llama-3.1-70B-Instruct
+LARGE_HYPERBOLIC_MODEL= # Default: meta-llama/Meta-Llama-3.1-405-Instruct
# Speech Synthesis
ELEVENLABS_XI_API_KEY= # API key from elevenlabs
@@ -81,25 +96,49 @@ MEDIUM_GOOGLE_MODEL= # Default: gemini-1.5-flash-latest
LARGE_GOOGLE_MODEL= # Default: gemini-1.5-pro-latest
EMBEDDING_GOOGLE_MODEL= # Default: text-embedding-004
+# Groq Configuration
+SMALL_GROQ_MODEL= # Default: llama-3.1-8b-instant
+MEDIUM_GROQ_MODEL= # Default: llama-3.3-70b-versatile
+LARGE_GROQ_MODEL= # Default: llama-3.2-90b-vision-preview
+EMBEDDING_GROQ_MODEL= # Default: llama-3.1-8b-instant
+# NanoGPT Configuration
+SMALL_NANOGPT_MODEL= # Default: gpt-4o-mini
+MEDIUM_NANOGPT_MODEL= # Default: gpt-4o
+LARGE_NANOGPT_MODEL= # Default: gpt-4o
#LlamaLocal Configuration
LLAMALOCAL_PATH= # Default: "" which is the current directory in plugin-node/dist/ which gets destroyed and recreated on every build
# API Keys
ANTHROPIC_API_KEY= # For Claude
+SMALL_ANTHROPIC_MODEL= # Default: claude-3-haiku-20240307
+MEDIUM_ANTHROPIC_MODEL= # Default: claude-3-5-sonnet-20241022
+LARGE_ANTHROPIC_MODEL= # Default: claude-3-5-sonnet-20241022
+
HEURIST_API_KEY= # Get from https://heurist.ai/dev-access
# Heurist Models
-SMALL_HEURIST_LANGUAGE_MODEL=
-MEDIUM_HEURIST_LANGUAGE_MODEL=
-LARGE_HEURIST_LANGUAGE_MODEL=
-HEURIST_IMAGE_MODEL=
+SMALL_HEURIST_MODEL= # Default: meta-llama/llama-3-70b-instruct
+MEDIUM_HEURIST_MODEL= # Default: meta-llama/llama-3-70b-instruct
+LARGE_HEURIST_MODEL= # Default: meta-llama/llama-3.1-405b-instruct
+HEURIST_IMAGE_MODEL= # Default: PepeXL
# Gaianet Configuration
GAIANET_MODEL=
GAIANET_SERVER_URL=
+
+SMALL_GAIANET_MODEL= # Default: llama3b
+SMALL_GAIANET_SERVER_URL= # Default: https://llama3b.gaia.domains/v1
+
+MEDIUM_GAIANET_MODEL= # Default: llama
+MEDIUM_GAIANET_SERVER_URL= # Default: https://llama8b.gaia.domains/v1
+
+LARGE_GAIANET_MODEL= # Default: qwen72b
+LARGE_GAIANET_SERVER_URL= # Default: https://qwen72b.gaia.domains/v1
+
GAIANET_EMBEDDING_MODEL=
+USE_GAIANET_EMBEDDING= # Set to TRUE for GAIANET/768, leave blank for local
# EVM
EVM_PRIVATE_KEY=
@@ -143,6 +182,8 @@ INTIFACE_WEBSOCKET_URL=ws://localhost:12345
FARCASTER_FID= # the FID associated with the account your are sending casts from
FARCASTER_NEYNAR_API_KEY= # Neynar API key: https://neynar.com/
FARCASTER_NEYNAR_SIGNER_UUID= # signer for the account you are sending casts from. create a signer here: https://dev.neynar.com/app
+FARCASTER_DRY_RUN=false # Set to true if you want to run the bot without actually publishing casts
+FARCASTER_POLL_INTERVAL=120 # How often (in seconds) the bot should check for farcaster interactions (replies and mentions)
# Coinbase
COINBASE_COMMERCE_KEY= # from coinbase developer portal
@@ -167,7 +208,12 @@ ZEROG_PRIVATE_KEY=
ZEROG_FLOW_ADDRESS=
# TEE Configuration
-DSTACK_SIMULATOR_ENDPOINT=
+# TEE_MODE options:
+# - LOCAL: Uses simulator at localhost:8090 (for local development)
+# - DOCKER: Uses simulator at host.docker.internal:8090 (for docker development)
+# - PRODUCTION: No simulator, uses production endpoints
+# Defaults to OFF if not specified
+TEE_MODE=OFF #LOCAL|DOCKER|PRODUCTION
WALLET_SECRET_SALT= # ONLY DEFINE IF YOU WANT TO USE TEE Plugin, otherwise it will throw errors
# Galadriel Configuration
@@ -204,4 +250,11 @@ ECHOCHAMBERS_API_KEY=testingkey0011
ECHOCHAMBERS_USERNAME=eliza
ECHOCHAMBERS_DEFAULT_ROOM=general
ECHOCHAMBERS_POLL_INTERVAL=60
-ECHOCHAMBERS_MAX_MESSAGES=10
\ No newline at end of file
+ECHOCHAMBERS_MAX_MESSAGES=10
+
+# AWS S3 Configuration Settings for File Upload
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+AWS_REGION=
+AWS_S3_BUCKET=
+AWS_S3_UPLOAD_PATH=
diff --git a/.github/workflows/image.yaml b/.github/workflows/image.yaml
new file mode 100644
index 0000000000..ddd543b0b9
--- /dev/null
+++ b/.github/workflows/image.yaml
@@ -0,0 +1,61 @@
+#
+name: Create and publish a Docker image
+
+# Configures this workflow to run every time a change is pushed to the branch called `release`.
+on:
+ release:
+ types: [created]
+ workflow_dispatch:
+
+# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
+env:
+ REGISTRY: ghcr.io
+ IMAGE_NAME: ${{ github.repository }}
+
+# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
+jobs:
+ build-and-push-image:
+ runs-on: ubuntu-latest
+ # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
+ permissions:
+ contents: read
+ packages: write
+ attestations: write
+ id-token: write
+ #
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
+ - name: Log in to the Container registry
+ uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
+ # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
+ # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
+ - name: Build and push Docker image
+ id: push
+ uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+
+ # This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
+ - name: Generate artifact attestation
+ uses: actions/attest-build-provenance@v1
+ with:
+ subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
+ subject-digest: ${{ steps.push.outputs.digest }}
+ push-to-registry: true
+
diff --git a/.github/workflows/require-develop.yml b/.github/workflows/require-develop.yml
new file mode 100644
index 0000000000..4519a3b5ca
--- /dev/null
+++ b/.github/workflows/require-develop.yml
@@ -0,0 +1,18 @@
+name: Check pull request source branch
+on:
+ pull_request_target:
+ types:
+ - opened
+ - reopened
+ - synchronize
+ - edited
+jobs:
+ check-branches:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check branches
+ run: |
+ if [ ${{ github.head_ref }} != "develop" ] && [ ${{ github.base_ref }} == "main" ]; then
+ echo "Merge requests to main branch are only allowed from dev branch."
+ exit 1
+ fi
diff --git a/Dockerfile b/Dockerfile
index a16b33ac0c..d97ed212cb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -25,7 +25,7 @@ COPY characters ./characters
# Install dependencies and build the project
RUN pnpm install \
- && pnpm build \
+ && pnpm build-docker \
&& pnpm prune --prod
# Create a new stage for the final image
diff --git a/README.md b/README.md
index b7b35c45eb..b87419cfad 100644
--- a/README.md
+++ b/README.md
@@ -1,133 +1,133 @@
-# Eliza 🤖
-
-
-
-
-
-
-
- 📖 [Documentation](https://ai16z.github.io/eliza/) | 🎯 [Examples](https://github.com/thejoven/awesome-eliza)
-
-
-
-## 🌍 README Translations
-
-[中文说明](./README_CN.md) | [日本語の説明](./README_JA.md) | [한국어 설명](./README_KOR.md) | [Français](./README_FR.md) | [Português](./README_PTBR.md) | [Türkçe](./README_TR.md) | [Русский](./README_RU.md) | [Español](./README_ES.md) | [Italiano](./README_IT.md)
-
-## ✨ Features
-
-- 🛠️ Full-featured Discord, Twitter and Telegram connectors
-- 🔗 Support for every model (Llama, Grok, OpenAI, Anthropic, etc.)
-- 👥 Multi-agent and room support
-- 📚 Easily ingest and interact with your documents
-- 💾 Retrievable memory and document store
-- 🚀 Highly extensible - create your own actions and clients
-- ☁️ Supports many models (local Llama, OpenAI, Anthropic, Groq, etc.)
-- 📦 Just works!
-
-## 🎯 Use Cases
-
-- 🤖 Chatbots
-- 🕵️ Autonomous Agents
-- 📈 Business Process Handling
-- 🎮 Video Game NPCs
-- 🧠 Trading
-
-## 🚀 Quick Start
-
-### Prerequisites
-
-- [Python 2.7+](https://www.python.org/downloads/)
-- [Node.js 23+](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
-- [pnpm](https://pnpm.io/installation)
-
-> **Note for Windows Users:** [WSL 2](https://learn.microsoft.com/en-us/windows/wsl/install-manual) is required.
-
-### Use the Starter (Recommended)
-
-```bash
-git clone https://github.com/ai16z/eliza-starter.git
-
-cp .env.example .env
-
-pnpm i && pnpm start
-```
-
-Then read the [Documentation](https://ai16z.github.io/eliza/) to learn how to customize your Eliza.
-
-### Manually Start Eliza (Only recommended if you know what you are doing)
-
-```bash
-# Clone the repository
-git clone https://github.com/ai16z/eliza.git
-
-# Checkout the latest release
-# This project iterates fast, so we recommend checking out the latest release
-git checkout $(git describe --tags --abbrev=0)
-```
-
-### Start Eliza with Gitpod
-
-[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ai16z/eliza/tree/main)
-
-### Edit the .env file
-
-Copy .env.example to .env and fill in the appropriate values.
-
-```
-cp .env.example .env
-```
-
-Note: .env is optional. If your planning to run multiple distinct agents, you can pass secrets through the character JSON
-
-### Automatically Start Eliza
-
-This will run everything to setup the project and start the bot with the default character.
-
-```bash
-sh scripts/start.sh
-```
-
-### Edit the character file
-
-1. Open `agent/src/character.ts` to modify the default character. Uncomment and edit.
-
-2. To load custom characters:
- - Use `pnpm start --characters="path/to/your/character.json"`
- - Multiple character files can be loaded simultaneously
-3. Connect with X (Twitter)
- - change `"clients": []` to `"clients": ["twitter"]` in the character file to connect with X
-
-### Manually Start Eliza
-
-```bash
-pnpm i
-pnpm build
-pnpm start
-
-# The project iterates fast, sometimes you need to clean the project if you are coming back to the project
-pnpm clean
-```
-
-#### Additional Requirements
-
-You may need to install Sharp. If you see an error when starting up, try installing it with the following command:
-
-```
-pnpm install --include=optional sharp
-```
-
-### Community & contact
-
-- [GitHub Issues](https://github.com/ai16z/eliza/issues). Best for: bugs you encounter using Eliza, and feature proposals.
-- [Discord](https://discord.gg/ai16z). Best for: sharing your applications and hanging out with the community.
-
-## Contributors
-
-
-
-
-
-## Star History
-
-[![Star History Chart](https://api.star-history.com/svg?repos=ai16z/eliza&type=Date)](https://star-history.com/#ai16z/eliza&Date)
+# Eliza 🤖
+
+
+
+
+
+
+
+ 📖 [Documentation](https://ai16z.github.io/eliza/) | 🎯 [Examples](https://github.com/thejoven/awesome-eliza)
+
+
+
+## 🌍 README Translations
+
+[中文说明](./README_CN.md) | [日本語の説明](./README_JA.md) | [한국어 설명](./README_KOR.md) | [Français](./README_FR.md) | [Português](./README_PTBR.md) | [Türkçe](./README_TR.md) | [Русский](./README_RU.md) | [Español](./README_ES.md) | [Italiano](./README_IT.md) | [ไทย](./README_TH.md) | [Deutsch](./README_DE.md)
+
+## ✨ Features
+
+- 🛠️ Full-featured Discord, Twitter and Telegram connectors
+- 🔗 Support for every model (Llama, Grok, OpenAI, Anthropic, etc.)
+- 👥 Multi-agent and room support
+- 📚 Easily ingest and interact with your documents
+- 💾 Retrievable memory and document store
+- 🚀 Highly extensible - create your own actions and clients
+- ☁️ Supports many models (local Llama, OpenAI, Anthropic, Groq, etc.)
+- 📦 Just works!
+
+## 🎯 Use Cases
+
+- 🤖 Chatbots
+- 🕵️ Autonomous Agents
+- 📈 Business Process Handling
+- 🎮 Video Game NPCs
+- 🧠 Trading
+
+## 🚀 Quick Start
+
+### Prerequisites
+
+- [Python 2.7+](https://www.python.org/downloads/)
+- [Node.js 23+](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
+- [pnpm](https://pnpm.io/installation)
+
+> **Note for Windows Users:** [WSL 2](https://learn.microsoft.com/en-us/windows/wsl/install-manual) is required.
+
+### Use the Starter (Recommended)
+
+```bash
+git clone https://github.com/ai16z/eliza-starter.git
+
+cp .env.example .env
+
+pnpm i && pnpm start
+```
+
+Then read the [Documentation](https://ai16z.github.io/eliza/) to learn how to customize your Eliza.
+
+### Manually Start Eliza (Only recommended if you know what you are doing)
+
+```bash
+# Clone the repository
+git clone https://github.com/ai16z/eliza.git
+
+# Checkout the latest release
+# This project iterates fast, so we recommend checking out the latest release
+git checkout $(git describe --tags --abbrev=0)
+```
+
+### Start Eliza with Gitpod
+
+[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ai16z/eliza/tree/main)
+
+### Edit the .env file
+
+Copy .env.example to .env and fill in the appropriate values.
+
+```
+cp .env.example .env
+```
+
+Note: .env is optional. If your planning to run multiple distinct agents, you can pass secrets through the character JSON
+
+### Automatically Start Eliza
+
+This will run everything to setup the project and start the bot with the default character.
+
+```bash
+sh scripts/start.sh
+```
+
+### Edit the character file
+
+1. Open `agent/src/character.ts` to modify the default character. Uncomment and edit.
+
+2. To load custom characters:
+ - Use `pnpm start --characters="path/to/your/character.json"`
+ - Multiple character files can be loaded simultaneously
+3. Connect with X (Twitter)
+ - change `"clients": []` to `"clients": ["twitter"]` in the character file to connect with X
+
+### Manually Start Eliza
+
+```bash
+pnpm i
+pnpm build
+pnpm start
+
+# The project iterates fast, sometimes you need to clean the project if you are coming back to the project
+pnpm clean
+```
+
+#### Additional Requirements
+
+You may need to install Sharp. If you see an error when starting up, try installing it with the following command:
+
+```
+pnpm install --include=optional sharp
+```
+
+### Community & contact
+
+- [GitHub Issues](https://github.com/ai16z/eliza/issues). Best for: bugs you encounter using Eliza, and feature proposals.
+- [Discord](https://discord.gg/ai16z). Best for: sharing your applications and hanging out with the community.
+
+## Contributors
+
+
+
+
+
+## Star History
+
+[![Star History Chart](https://api.star-history.com/svg?repos=ai16z/eliza&type=Date)](https://star-history.com/#ai16z/eliza&Date)
diff --git a/README_DE.md b/README_DE.md
new file mode 100644
index 0000000000..c6db5476a2
--- /dev/null
+++ b/README_DE.md
@@ -0,0 +1,128 @@
+# Eliza 🤖
+
+
+
+
+
+
+
+ 📖 [Dokumentation](https://ai16z.github.io/eliza/) | 🎯 [Beispiele](https://github.com/thejoven/awesome-eliza)
+
+
+
+## ✨ Funktionen
+
+- 🛠️ Voll ausgestattete Konnektoren für Discord, Twitter und Telegram
+- 👥 Multi-Agenten- und Raumunterstützung
+- 📚 Einfache Verarbeitung und Interaktion mit deinen Dokumenten
+- 💾 Abrufbarer Speicher und Dokumentenspeicher
+- 🚀 Hochgradig erweiterbar – erstelle deine eigenen Aktionen und Clients
+- ☁️ Unterstützt viele Modelle (lokales Llama, OpenAI, Anthropic, Groq usw.)
+- 📦 Einfach funktionsfähig!
+
+## 🎯 Anwendungsfälle
+
+- 🤖 Chatbots
+- 🕵️ Autonome Agenten
+- 📈 Geschäftsprozessmanagement
+- 🎮 NPCs in Videospielen
+- 🧠 Handel
+
+## 🚀 Schnelleinstieg
+
+### Voraussetzungen
+
+- [Python 2.7+](https://www.python.org/downloads/)
+- [Node.js 23+](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
+- [pnpm](https://pnpm.io/installation)
+
+> **Hinweis für Windows-Benutzer:** [WSL 2](https://learn.microsoft.com/de-de/windows/wsl/install-manual) ist erforderlich.
+
+### Nutzung des Starters (Empfohlen)
+
+```bash
+git clone https://github.com/ai16z/eliza-starter.git
+
+cp .env.example .env
+
+pnpm i && pnpm start
+```
+
+Lies dann die [Dokumentation](https://ai16z.github.io/eliza/), um zu erfahren, wie du Eliza anpassen kannst.
+
+### Manuelles Starten von Eliza (Nur empfohlen, wenn du genau weißt, was du tust)
+
+```bash
+# Repository klonen
+git clone https://github.com/ai16z/eliza.git
+
+# Wechsle zur neuesten Version
+# Dieses Projekt entwickelt sich schnell weiter, daher empfehlen wir, die neueste Version zu verwenden
+git checkout $(git describe --tags --abbrev=0)
+```
+
+### Eliza mit Gitpod starten
+
+[![In Gitpod öffnen](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ai16z/eliza/tree/main)
+
+### Bearbeite die .env-Datei
+
+Kopiere .env.example nach .env und fülle die entsprechenden Werte aus.
+
+```
+cp .env.example .env
+```
+
+Hinweis: .env ist optional. Wenn du vorhast, mehrere unterschiedliche Agenten auszuführen, kannst du Geheimnisse über die Charakter-JSON übergeben.
+
+### Eliza automatisch starten
+
+Dies führt alle notwendigen Schritte aus, um das Projekt einzurichten und den Bot mit dem Standardcharakter zu starten.
+
+```bash
+sh scripts/start.sh
+```
+
+### Charakterdatei bearbeiten
+
+1. Öffne `agent/src/character.ts`, um den Standardcharakter zu bearbeiten. Kommentiere und bearbeite ihn.
+
+2. Um benutzerdefinierte Charaktere zu laden:
+ - Verwende `pnpm start --characters="path/to/your/character.json"`
+ - Mehrere Charakterdateien können gleichzeitig geladen werden.
+3. Verbinde mit X (Twitter)
+ - Ändere `"clients": []` zu `"clients": ["twitter"]` in der Charakterdatei, um eine Verbindung mit X herzustellen.
+
+### Eliza manuell starten
+
+```bash
+pnpm i
+pnpm build
+pnpm start
+
+# Das Projekt entwickelt sich schnell weiter. Manchmal musst du das Projekt bereinigen, wenn du es nach einiger Zeit erneut aufrufst.
+pnpm clean
+```
+
+#### Zusätzliche Anforderungen
+
+Möglicherweise musst du Sharp installieren. Wenn beim Starten ein Fehler auftritt, versuche es mit folgendem Befehl:
+
+```
+pnpm install --include=optional sharp
+```
+
+### Community & Kontakt
+
+- [GitHub Issues](https://github.com/ai16z/eliza/issues). Am besten geeignet für: Bugs, die du bei der Nutzung von Eliza findest, und Feature-Vorschläge.
+- [Discord](https://discord.gg/ai16z). Am besten geeignet für: das Teilen deiner Anwendungen und den Austausch mit der Community.
+
+## Contributors
+
+
+
+
+
+## Star History
+
+[![Star History Chart](https://api.star-history.com/svg?repos=ai16z/eliza&type=Date)](https://star-history.com/#ai16z/eliza&Date)
diff --git a/README_KOR.md b/README_KOR.md
index ac73a6dab3..05383eae47 100644
--- a/README_KOR.md
+++ b/README_KOR.md
@@ -114,7 +114,7 @@ pnpm install --include=optional sharp
### Community & contact
-- [깃헙 이슈](https://github.com/ai16z/eliza/issues). 용도: Eliza 사용 중 발견된 버그 리포트, 기능 제안.
+- [Github Issues](https://github.com/ai16z/eliza/issues). 용도: Eliza 사용 중 발견된 버그 리포트, 기능 제안.
- [Discord](https://discord.gg/ai16z). 용도: 애플리케이션 공유 및 커뮤니티 활동.
## 컨트리뷰터
diff --git a/README_TH.md b/README_TH.md
new file mode 100644
index 0000000000..f65a790dc0
--- /dev/null
+++ b/README_TH.md
@@ -0,0 +1,130 @@
+# Eliza (อีไลซ่า) 🤖
+
+
+
+
+
+
+
+ 📖 [คู่มือ](https://ai16z.github.io/eliza/) | 🎯 [ตัวอย่าง](https://github.com/thejoven/awesome-eliza)
+
+
+
+## ✨ ฟีเจอร์
+
+- 🛠️ สามารถเชื่อมต่อ Discord, Twitter และ Telegram ได้
+- 🔗 ซัพพอร์ตครบทุกโมเดล (Llama, Grok, OpenAI, Anthropic, ฯลฯ)
+- 👥 มัลติเอเจนต์และห้องสนทนา
+- 📚 ง่ายต่อการดึงและเข้าถึงข้อมูลเอกสาร
+- 💾 มีหน่วยความจำและที่จัดเก็บข้อมูล
+- 🚀 ง่ายต่อการปรับแต่งไม่ว่าจะเป็นการสร้าง clients หรือกำหนด action
+- ☁️ รองรับหลายโมเดล (local Llama, OpenAI, Anthropic, Groq, ฯลฯ)
+- 📦 ครบเครื่องเรื่อง AI agent!
+
+## 🎯 ตัวอย่างการนำไปใช้
+
+- 🤖 แชทบอท
+- 🕵️ เอเจนต์อิสระ (Autonomous Agent)
+- 📈 จัดการฝั่งธุรกิจ
+- 🎮 ตัวละครในเกมที่ไม่ใช่ผู้เล่น (NPC)
+- 🧠 การเทรด
+
+## 🚀 เริ่มต้นการใช้งาน
+
+### สิ่งที่จำเป็นก่อนเริ่มใช้งาน
+
+- [Python 2.7+](https://www.python.org/downloads/)
+- [Node.js 23+](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
+- [pnpm](https://pnpm.io/installation)
+
+> **หมายเหตุสำหรับผู้ใช้ Windows:** จำเป็นต้องมี [WSL 2](https://learn.microsoft.com/en-us/windows/wsl/install-manual)
+
+### เริ่มใช้งานด้วยตัวอย่าง (แนะนำ)
+
+```bash
+git clone https://github.com/ai16z/eliza-starter.git
+
+cp .env.example .env
+
+pnpm i && pnpm start
+```
+
+จากนั้นอ่าน [คู่มือ](https://ai16z.github.io/eliza/) เพื่อศึกษาวิธีการปรับแต่ง Eliza
+
+### เริ่มใช้งาน Eliza ด้วยตนเอง (แนะนำสำหรับคนที่มีประสบการณ์)
+
+```bash
+# โคลน repo
+git clone https://github.com/ai16z/eliza.git
+
+# Checkout release ล่าสุด
+# โปรเจกต์นี้มีการอัปเดตอยู่บ่อยครั้ง เราแนะนำให้ checkout release ล่าสุดเสมอ
+git checkout $(git describe --tags --abbrev=0)
+```
+
+### เริ่มใช้งาน Eliza ผ่าน Gitpod
+
+[![เปิดใน Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ai16z/eliza/tree/main)
+
+### แก้ไขไฟล์ .env
+
+คัดลอก .env.example ไปเป็น .env และระบุค่าที่เหมาะสม
+
+```
+cp .env.example .env
+```
+
+หมายเหตุ: ไม่จำเป็นต้องใช้ .env หากคุณอยากใช้งานเอเจนต์หลายๆตัวพร้อมกัน คุณสามารถส่ง secrets ผ่าน JSON ของตัวละครได้
+
+### เริ่มใช้งาน Eliza แบบอัตโนมัติ
+
+จะทำการติดตั้งโปรเจกต์ทั้งหมด และเริ่มต้น bot ด้วยตัวละครแบบค่าเริ่มต้น
+
+```bash
+sh scripts/start.sh
+```
+
+### แก้ไขไฟล์ตัวละคร
+
+1. เปิด `agent/src/character.ts` เพื่อแก้ไขตัวละครเริ่มต้น นำคอมเม้นออกและเริ่มแก้ไข
+
+
+2. การโหลดตัวละคร custom:
+ - ใช้ `pnpm start --characters="path/to/your/character.json"`
+ - สามารถโหลดหลายๆตัวละครพร้อมกันได้
+3. เชื่อมต่อกับ X (Twitter)
+ - เปลี่ยน `"clients": []` เป็น `"clients": ["twitter"]` ในไฟล์ตัวละครเพื่อเชื่อมต่อกับ X (Twitter)
+
+### เริ่มใช้งาน Eliza ด้วยตนเอง
+
+```bash
+pnpm i
+pnpm build
+pnpm start
+
+# โปรเจกต์นี้มีการอัปเดตอยู่บ่อยครั้ง บางครั้งอาจต้องทำการ clean โปรเจกต์ถ้าหากกลับมาทำใหม่
+pnpm clean
+```
+
+#### สิ่งที่จำเป็นเพิ่มเติม
+
+คุณอาจต้องติดตั้ง Sharp ถ้าหากคุณเห็นข้อความ error เมื่อเริ่มต้น สามารถลองติดตั้งด้วยคำสั่งต่อไปนี้:
+
+```
+pnpm install --include=optional sharp
+```
+
+### ชุมชนและข้อมูลการติดต่อ
+
+- [GitHub Issues](https://github.com/ai16z/eliza/issues). เหมาะสำหรับ: เมื่อปัญหาที่พบเมื่อใช้ Eliza และข้อเสนอแนะเกี่ยวกับฟีเจอร์เพิ่มเติม
+- [Discord](https://discord.gg/ai16z). เหมาะสำหรับ: แชร์ผลงานแอปพลิเคชั่นและพบปะกับคอมมูนิตี้
+
+## ผู้มีส่วนร่วม
+
+
+
+
+
+## ประวัติดาว
+
+[![Star History Chart](https://api.star-history.com/svg?repos=ai16z/eliza&type=Date)](https://star-history.com/#ai16z/eliza&Date)
diff --git a/agent/package.json b/agent/package.json
index 6724b5a5d2..e27d4aa5ee 100644
--- a/agent/package.json
+++ b/agent/package.json
@@ -29,7 +29,7 @@
"@ai16z/plugin-0g": "workspace:*",
"@ai16z/plugin-aptos": "workspace:*",
"@ai16z/plugin-bootstrap": "workspace:*",
- "@ai16z/plugin-buttplug": "workspace:*",
+ "@ai16z/plugin-intiface": "workspace:*",
"@ai16z/plugin-coinbase": "workspace:*",
"@ai16z/plugin-conflux": "workspace:*",
"@ai16z/plugin-evm": "workspace:*",
diff --git a/agent/src/index.ts b/agent/src/index.ts
index ea92ff84f5..8aab1b5d38 100644
--- a/agent/src/index.ts
+++ b/agent/src/index.ts
@@ -25,24 +25,25 @@ import {
validateCharacterConfig,
} from "@ai16z/eliza";
import { zgPlugin } from "@ai16z/plugin-0g";
-import { goatPlugin } from "@ai16z/plugin-goat";
+import createGoatPlugin from "@ai16z/plugin-goat";
import { bootstrapPlugin } from "@ai16z/plugin-bootstrap";
-// import { buttplugPlugin } from "@ai16z/plugin-buttplug";
+// import { intifacePlugin } from "@ai16z/plugin-intiface";
import {
coinbaseCommercePlugin,
coinbaseMassPaymentsPlugin,
tradePlugin,
tokenContractPlugin,
webhookPlugin,
+ advancedTradePlugin,
} from "@ai16z/plugin-coinbase";
import { confluxPlugin } from "@ai16z/plugin-conflux";
import { imageGenerationPlugin } from "@ai16z/plugin-image-generation";
import { evmPlugin } from "@ai16z/plugin-evm";
import { createNodePlugin } from "@ai16z/plugin-node";
import { solanaPlugin } from "@ai16z/plugin-solana";
+import { teePlugin, TEEMode } from "@ai16z/plugin-tee";
import { aptosPlugin, TransferAptosToken } from "@ai16z/plugin-aptos";
import { flowPlugin } from "@ai16z/plugin-flow";
-import { teePlugin } from "@ai16z/plugin-tee";
import Database from "better-sqlite3";
import fs from "fs";
import path from "path";
@@ -269,6 +270,16 @@ export function getTokenForProvider(
character.settings?.secrets?.VOLENGINE_API_KEY ||
settings.VOLENGINE_API_KEY
);
+ case ModelProviderName.NANOGPT:
+ return (
+ character.settings?.secrets?.NANOGPT_API_KEY ||
+ settings.NANOGPT_API_KEY
+ );
+ case ModelProviderName.HYPERBOLIC:
+ return (
+ character.settings?.secrets?.HYPERBOLIC_API_KEY ||
+ settings.HYPERBOLIC_API_KEY
+ );
}
}
@@ -353,7 +364,7 @@ function getSecret(character: Character, secret: string) {
let nodePlugin: any | undefined;
-export function createAgent(
+export async function createAgent(
character: Character,
db: IDatabaseAdapter,
cache: ICacheManager,
@@ -367,6 +378,24 @@ export function createAgent(
nodePlugin ??= createNodePlugin();
+ const teeMode = getSecret(character, "TEE_MODE") || "OFF";
+ const walletSecretSalt = getSecret(character, "WALLET_SECRET_SALT");
+
+ // Validate TEE configuration
+ if (teeMode !== TEEMode.OFF && !walletSecretSalt) {
+ elizaLogger.error(
+ "WALLET_SECRET_SALT required when TEE_MODE is enabled"
+ );
+ throw new Error("Invalid TEE configuration");
+ }
+
+ let goatPlugin: any | undefined;
+ if (getSecret(character, "ALCHEMY_API_KEY")) {
+ goatPlugin = await createGoatPlugin((secret) =>
+ getSecret(character, secret)
+ );
+ }
+
return new AgentRuntime({
databaseAdapter: db,
token,
@@ -386,7 +415,7 @@ export function createAgent(
: null,
getSecret(character, "EVM_PRIVATE_KEY") ||
(getSecret(character, "WALLET_PUBLIC_KEY") &&
- !getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
+ getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
? evmPlugin
: null,
getSecret(character, "ZEROG_PRIVATE_KEY") ? zgPlugin : null,
@@ -400,14 +429,21 @@ export function createAgent(
: null,
...(getSecret(character, "COINBASE_API_KEY") &&
getSecret(character, "COINBASE_PRIVATE_KEY")
- ? [coinbaseMassPaymentsPlugin, tradePlugin, tokenContractPlugin]
+ ? [
+ coinbaseMassPaymentsPlugin,
+ tradePlugin,
+ tokenContractPlugin,
+ advancedTradePlugin,
+ ]
+ : []),
+ ...(teeMode !== TEEMode.OFF && walletSecretSalt
+ ? [teePlugin, solanaPlugin]
: []),
getSecret(character, "COINBASE_API_KEY") &&
getSecret(character, "COINBASE_PRIVATE_KEY") &&
getSecret(character, "COINBASE_NOTIFICATION_URI")
? webhookPlugin
: null,
- getSecret(character, "WALLET_SECRET_SALT") ? teePlugin : null,
getSecret(character, "ALCHEMY_API_KEY") ? goatPlugin : null,
getSecret(character, "FLOW_ADDRESS") &&
getSecret(character, "FLOW_PRIVATE_KEY")
@@ -423,14 +459,14 @@ export function createAgent(
});
}
-function intializeFsCache(baseDir: string, character: Character) {
+function initializeFsCache(baseDir: string, character: Character) {
const cacheDir = path.resolve(baseDir, character.id, "cache");
const cache = new CacheManager(new FsCacheAdapter(cacheDir));
return cache;
}
-function intializeDbCache(character: Character, db: IDatabaseCacheAdapter) {
+function initializeDbCache(character: Character, db: IDatabaseCacheAdapter) {
const cache = new CacheManager(new DbCacheAdapter(db, character.id));
return cache;
}
@@ -453,8 +489,8 @@ async function startAgent(character: Character, directClient) {
await db.init();
- const cache = intializeDbCache(character, db);
- const runtime = createAgent(character, db, cache, token);
+ const cache = initializeDbCache(character, db);
+ const runtime = await createAgent(character, db, cache, token);
await runtime.initialize();
diff --git a/docs/api/enumerations/ModelProviderName.md b/docs/api/enumerations/ModelProviderName.md
index f80340a1c9..8a117db0e1 100644
--- a/docs/api/enumerations/ModelProviderName.md
+++ b/docs/api/enumerations/ModelProviderName.md
@@ -12,9 +12,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:214](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L214)
+[packages/core/src/types.ts:215](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L215)
-***
+---
### ETERNALAI
@@ -22,9 +22,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:215](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L215)
+[packages/core/src/types.ts:216](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L216)
-***
+---
### ANTHROPIC
@@ -32,9 +32,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:216](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L216)
+[packages/core/src/types.ts:217](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L217)
-***
+---
### GROK
@@ -42,9 +42,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:217](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L217)
+[packages/core/src/types.ts:218](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L218)
-***
+---
### GROQ
@@ -52,9 +52,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:218](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L218)
+[packages/core/src/types.ts:219](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L219)
-***
+---
### LLAMACLOUD
@@ -62,9 +62,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:219](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L219)
+[packages/core/src/types.ts:220](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L220)
-***
+---
### TOGETHER
@@ -72,9 +72,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:220](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L220)
+[packages/core/src/types.ts:221](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L221)
-***
+---
### LLAMALOCAL
@@ -82,9 +82,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:221](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L221)
+[packages/core/src/types.ts:222](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L222)
-***
+---
### GOOGLE
@@ -92,19 +92,19 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:222](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L222)
+[packages/core/src/types.ts:223](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L223)
-***
+---
-### CLAUDE\_VERTEX
+### CLAUDE_VERTEX
-> **CLAUDE\_VERTEX**: `"claude_vertex"`
+> **CLAUDE_VERTEX**: `"claude_vertex"`
#### Defined in
-[packages/core/src/types.ts:223](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L223)
+[packages/core/src/types.ts:224](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L224)
-***
+---
### REDPILL
@@ -112,9 +112,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:224](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L224)
+[packages/core/src/types.ts:225](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L225)
-***
+---
### OPENROUTER
@@ -122,9 +122,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:225](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L225)
+[packages/core/src/types.ts:226](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L226)
-***
+---
### OLLAMA
@@ -132,9 +132,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:226](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L226)
+[packages/core/src/types.ts:227](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L227)
-***
+---
### HEURIST
@@ -142,19 +142,19 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:227](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L227)
+[packages/core/src/types.ts:228](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L228)
-***
+---
### GALADRIEL
-> **GALADRIEL**: `"galadriel"`
+**GALADRIEL**: `"galadriel"`
#### Defined in
-[packages/core/src/types.ts:228](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L228)
+[packages/core/src/types.ts:229](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L229)
-***
+---
### FAL
@@ -162,9 +162,9 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:229](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L229)
+[packages/core/src/types.ts:230](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L230)
-***
+---
### GAIANET
@@ -172,19 +172,19 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:230](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L230)
+[packages/core/src/types.ts:231](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L231)
-***
+---
-### ALI\_BAILIAN
+### ALI_BAILIAN
-> **ALI\_BAILIAN**: `"ali_bailian"`
+> **ALI_BAILIAN**: `"ali_bailian"`
#### Defined in
-[packages/core/src/types.ts:231](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L231)
+[packages/core/src/types.ts:232](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L232)
-***
+---
### VOLENGINE
@@ -192,4 +192,14 @@ Available model providers
#### Defined in
-[packages/core/src/types.ts:232](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L232)
+[packages/core/src/types.ts:233](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L233)
+
+***
+
+### HYPERBOLIC
+
+> **HYPERBOLIC**: `"hyperbolic"`
+
+#### Defined in
+
+[packages/core/src/types.ts:234](https://github.com/ai16z/eliza/blob/main/packages/core/src/types.ts#L234)
diff --git a/docs/api/type-aliases/Models.md b/docs/api/type-aliases/Models.md
index 55a68338ef..198887ec94 100644
--- a/docs/api/type-aliases/Models.md
+++ b/docs/api/type-aliases/Models.md
@@ -36,6 +36,10 @@ Model configurations by provider
> **together**: [`Model`](Model.md)
+### together
+
+> **together**: [`Model`](Model.md)
+
### llama\_local
> **llama\_local**: [`Model`](Model.md)
@@ -84,6 +88,10 @@ Model configurations by provider
> **volengine**: [`Model`](Model.md)
+### hyperbolic
+
+> **hyperbolic**: [`Model`](Model.md)
+
## Defined in
[packages/core/src/types.ts:188](https://github.com/monilpat/eliza/blob/main/packages/core/src/types.ts#L188)
diff --git a/docs/community/Streams/12-2024/2024-12-03.md b/docs/community/Streams/12-2024/2024-12-03.md
index ace4e649c8..4e1fec33c1 100644
--- a/docs/community/Streams/12-2024/2024-12-03.md
+++ b/docs/community/Streams/12-2024/2024-12-03.md
@@ -1,3 +1,9 @@
+---
+sidebar_position: 1
+title: "AI Agent Dev School Part 2"
+description: "Building Complex AI Agents with Actions, Providers, & Evaluators"
+---
+
# AI Agent Dev School Part 2
**Building Complex AI Agents with Actions, Providers, & Evaluators**
diff --git a/docs/community/Streams/12-2024/2024-12-05.md b/docs/community/Streams/12-2024/2024-12-05.md
new file mode 100644
index 0000000000..ef738add02
--- /dev/null
+++ b/docs/community/Streams/12-2024/2024-12-05.md
@@ -0,0 +1,104 @@
+---
+sidebar_position: 2
+title: "AI Agent Dev School Part 3"
+description: "Form-Filling Frenzy & Eliza's Wild Ride"
+---
+
+# AI Agent Dev School Part 3
+
+**Form-Filling Frenzy & Eliza's Wild Ride**
+
+Date: 2024-12-05
+YouTube Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU
+
+## Timestamps
+
+**00:00:00** - Intro & Housekeeping:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=0
+- Recap of previous sessions (Typescript, plugins, actions)
+- Importance of staying on the latest Eliza branch
+- How to pull latest changes and stash local modifications
+
+**00:08:05** - Building a Form-Filling Agent:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=485
+- Introduction to Providers & Evaluators
+- Practical use case: Extracting user data (name, location, job)
+- Steps for a provider-evaluator loop to gather info and trigger actions
+
+**00:16:15** - Deep Dive into Evaluators:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=975
+- Understanding "Evaluator" in Eliza's context
+- When they run, their role in agent's self-reflection
+
+**00:27:45** - Code walkthrough of the "Fact Evaluator":
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=1675
+- Code walkthrough of the "Fact Evaluator"
+
+**00:36:07** - Building a User Data Evaluator:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=2167
+- Starting from scratch, creating a basic evaluator
+- Registering the evaluator directly in the agent (no plugin)
+- Logging evaluator activity and inspecting context
+
+**00:51:50** - Exploring Eliza's Cache Manager:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=3110
+- Shaw uses Code2Prompt to analyze cache manager code
+- Applying cache manager principles to user data storage
+
+**01:06:01** - Using Claude AI for Code Generation:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=3961
+- Pasting code into Claude and giving instructions
+- Iterative process: Refining code and providing feedback to Claude
+
+**01:21:18** - Testing the User Data Flow:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=4878
+- Running the agent and interacting with it
+- Observing evaluator logs and context injections
+- Troubleshooting and iterating on code based on agent behavior
+
+**01:30:27** - Adding a Dynamic Provider Based on Completion:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=5427
+- Creating a new provider that only triggers after user data is collected
+- Example: Providing a secret code or access link as a reward
+
+**01:37:16** - Q&A with the Audience:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=5836
+- Python vs. TypeScript agents
+- Pre-evaluation vs. post-evaluation hooks
+- Agent overwhelm with many plugins/evaluators
+- Agentic app use cases beyond chat
+- Running stateless agents
+- Building AIXBT agents
+
+**01:47:31** - Outro and Next Steps:
+- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=6451
+- Recap of key learnings and the potential of provider-evaluator loops
+- Call to action: Share project ideas and feedback for future sessions
+
+## Summary
+
+This is the third part of the live stream series "AI Agent Dev School" hosted by Shaw from ai16z, focusing on building AI agents using the Eliza framework.
+
+**Key takeaways:**
+
+* **Updating Eliza:** Shaw emphasizes staying up-to-date with the rapidly evolving Eliza project due to frequent bug fixes and new features. He provides instructions on pulling the latest changes from the main branch on GitHub.
+* **Focus on Providers and Evaluators:** The stream focuses on building a practical provider-evaluator loop to demonstrate a popular use case for AI agents – filling out a form by extracting user information.
+* **Form Builder Example:** Shaw walks the audience through building a "form provider" that gathers a user's name, location, and job. This provider utilizes a cache to store already extracted information and instructs the agent to prompt the user for any missing details.
+* **Evaluator Role:** The evaluator continually checks the cache for the completeness of user data. Once all information is extracted, the evaluator triggers an action to send the collected data to an external API (simulated in the example).
+* **Live Coding and AI Assistance:** Shaw live codes the example, using tools like "Code2Prompt" and Claude AI to help generate and refine the code. He advocates for writing code in a human-readable manner, utilizing comments to provide context and guidance for both developers and AI assistants.
+* **Agentic Applications:** Shaw highlights the potential of agentic applications to replicate existing website functionality through conversational interfaces, bringing services directly to users within their preferred social media platforms.
+* **Community Engagement:** Shaw encourages active participation from the community, suggesting contributions to the project through pull requests and feedback on desired features and patterns for future Dev School sessions.
+
+**Overall, this live stream provided a practical tutorial on building a common AI agent use case (form filling) while emphasizing the potential of the Eliza framework for developing a wide range of agentic applications.**
+
+## Hot Takes
+
+1. **"I'm just going to struggle bus some code today." (00:09:31,664)** - Shaw embraces a "struggle bus" approach, showcasing live coding with errors and debugging, reflecting the reality of AI agent development. This contrasts with polished tutorials, highlighting the iterative and messy nature of this new technology.
+
+2. **"I'm actually not gonna put this in a plugin. I'm gonna put this in the agent... just so you can see what happens if you were to, like, make your own agent without using a plugin at all." (00:37:24,793)** - Shaw goes against the Eliza framework's plugin structure, showing viewers how to bypass it entirely. This bold move emphasizes flexibility, but could spark debate on best practices and potential drawbacks.
+
+3. **"I really don't remember conversations from people very well, like verbatim, but I definitely remember like the gist, the context, the really needy ideas." (00:24:48,180)** - Shaw draws a controversial parallel between human memory and the Eliza agent's fact extraction. Reducing human interaction to "needy ideas" is provocative, questioning the depth of social understanding AI agents currently possess.
+
+4. **"It's just an LLM. It's just making those numbers up. It could be off. I don't really buy the confidence here." (01:13:56,971)** - Shaw dismisses the confidence scores generated by the Large Language Model (LLM), revealing a distrust of these black-box outputs. This skepticism is crucial in a field where relying solely on AI's self-assessment can be misleading.
+
+5. **"Dude, that's a $250 million market cap token. Let's get that shit in Bubba Cat." (01:45:34,809)** - Shaw throws out a blunt, market-driven statement regarding the AIXBT token. Bringing finance directly into the technical discussion highlights the intertwined nature of AI development and potential financial incentives, a topic often tiptoed around.
diff --git a/docs/community/Streams/12-2024/2024-12-06.md b/docs/community/Streams/12-2024/2024-12-06.md
index b0a1d9905a..9e80c6ed55 100644
--- a/docs/community/Streams/12-2024/2024-12-06.md
+++ b/docs/community/Streams/12-2024/2024-12-06.md
@@ -1,98 +1,175 @@
-# AI Agent Dev School Part 3
+---
+sidebar_position: 3
+title: "What Did You Get Done This Week? #4"
+description: "Communications, Updates and Accountability"
+---
-**Form-Filling Frenzy & Eliza's Wild Ride**
+# What Did You Get Done This Week? #4
-Date: 2024-12-05
-YouTube Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU
+**Communications, Updates and Accountability**
+
+Date: 2024-12-06
+Twitter Spaces: https://x.com/i/spaces/1lDxLlryWXaxm
+YouTube Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4
## Timestamps
-**00:00:00** - Intro & Housekeeping:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=0
-- Recap of previous sessions (Typescript, plugins, actions)
-- Importance of staying on the latest Eliza branch
-- How to pull latest changes and stash local modifications
-
-**00:08:05** - Building a Form-Filling Agent:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=485
-- Introduction to Providers & Evaluators
-- Practical use case: Extracting user data (name, location, job)
-- Steps for a provider-evaluator loop to gather info and trigger actions
-
-**00:16:15** - Deep Dive into Evaluators:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=975
-- Understanding "Evaluator" in Eliza's context
-- When they run, their role in agent's self-reflection
-
-**00:27:45** - Code walkthrough of the "Fact Evaluator":
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=1675
-- Code walkthrough of the "Fact Evaluator"
-
-**00:36:07** - Building a User Data Evaluator:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=2167
-- Starting from scratch, creating a basic evaluator
-- Registering the evaluator directly in the agent (no plugin)
-- Logging evaluator activity and inspecting context
-
-**00:51:50** - Exploring Eliza's Cache Manager:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=3110
-- Shaw uses Code2Prompt to analyze cache manager code
-- Applying cache manager principles to user data storage
-
-**01:06:01** - Using Claude AI for Code Generation:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=3961
-- Pasting code into Claude and giving instructions
-- Iterative process: Refining code and providing feedback to Claude
-
-**01:21:18** - Testing the User Data Flow:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=4878
-- Running the agent and interacting with it
-- Observing evaluator logs and context injections
-- Troubleshooting and iterating on code based on agent behavior
-
-**01:30:27** - Adding a Dynamic Provider Based on Completion:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=5427
-- Creating a new provider that only triggers after user data is collected
-- Example: Providing a secret code or access link as a reward
-
-**01:37:16** - Q&A with the Audience:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=5836
-- Python vs. TypeScript agents
-- Pre-evaluation vs. post-evaluation hooks
-- Agent overwhelm with many plugins/evaluators
-- Agentic app use cases beyond chat
-- Running stateless agents
-- Building AIXBT agents
-
-**01:47:31** - Outro and Next Steps:
-- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=6451
-- Recap of key learnings and the potential of provider-evaluator loops
-- Call to action: Share project ideas and feedback for future sessions
+**00:01:09** - Meeting start, expectations (5-minute updates, focus on this week's achievements).
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=69
+
+**00:02:50** - Shaw's update (dev school, in-person meetup).
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=170
+
+**00:04:59** - Project growth, coordination challenges, need for AI project management tools.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=299
+
+**00:09:22** - Call for contributors to speak, starting with Reality Spiral.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=562
+
+**00:10:04** - Reality Spiral: Github integration, testing framework, Coinbase work.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=604
+
+**00:17:13** - Boyaloxer: Plugin Feel (emotional adjustments for agents).
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1033
+
+**00:18:37** - Spaceodili: Discord growth, summarization systems.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1117
+
+**00:19:33** - Yodamaster726: Using agents in university classes, championing Llama.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1173
+
+**00:23:32** - Wiki: Suggestion for a project newsletter. Discussion about contributor summarization.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1412
+
+**00:26:00** - Hashwarlock: Remote Attestation Explorer upgrades, Reddit client, TEE as a service.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1560
+
+**00:28:45** - KyleSt4rgarden: Eliza Framework Council, focus on stability and unified messaging bus.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1725
+
+**00:33:22** - Nasdao_: Self-sustaining AI DAO, AI agent running validator.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=2002
+
+**00:34:57** - Evepredict: Slack integration, Reddit client/search, text/video to video project.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=2097
+
+**00:44:02** - ByornOeste: Dark Sun project launch, uncensored agent, video generator.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=2642
+
+**00:47:37** - Empyrealdev: LayerZero integrations, Python tooling for Solana.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=2857
+
+**00:52:16** - SkotiVi: Question about ai16z bot tech stack (it's Eliza).
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3136
+
+**00:54:19** - YoungBalla1000x: 15-year-old builder, project update, wallet drained.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3259
+
+**00:56:47** - SOL_CryptoGamer: Cizem’s PFP collection launch and success.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3407
+
+**01:02:17** - Angelocass: Experimenting with agents, excited about the potential.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3737
+
+**01:03:15** - DAOJonesPumpAI: Spam bot detection, FAL API PR, Solana wallet prototype.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3795
+
+**01:06:38** - RodrigoSotoAlt: 3D NFTs for Bosu, 3D portal, using latest Eliza version.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3998
+
+**01:10:43** - cryptocomix1: Job interviews, learning about AI agents, interested in 3D design.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=4243
+
+**01:13:54** - TheBigOneGG: ERC20/SPL integration in game, ai16z cosmetic items.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=4434
+
+**01:15:18** - Louround_: Thales project update, data sources, MPC wallet plugin.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=4518
+
+**01:22:59** - btspoony: Flow blockchain integration PR merged, multi-account control.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=4979
+
+**01:25:48** - 0xamericanspiri: Goldman Stanley DAO launch on daos.fun, using hyperliquid airdrop.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=5148
+
+**01:28:24** - Hawkeye_Picks: Experimenting with Degen Spartan AI, exploring AI in collectibles.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=5304
+
+**01:36:33** - BV_Bloom1: Live video chat plugin modifications, integrating conversation models into 3D environment.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=5793
+
+**01:39:44** - pawgDAO: Gamified governance experiments, using Cursor, integrating AI16z.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=5984
+
+**01:43:24** - jpegyguggenheim: Artist interested in AI, exploring dev school.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6204
+
+**01:44:07** - heathenft: Super Swarm DevNet launch on fxn.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6247
+
+**01:46:28** - Roberto9211999: (Brief interruption) Grok discussion.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6388
+
+**01:48:18** - godfreymeyer: Unity scaffolding for 3D AI TV project.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6498
+
+**01:51:16** - Victor28612594: Fungo team building AlphaScan agent, data enrichment plugin.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6676
+
+**01:53:23** - SidDegen: OnlyCalls launch, data pipeline, beta release plans.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6803
+
+**01:55:00** - O_on_X: Ico onboarding, 2D video models, comfyUI for art.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6900
+
+**02:01:00** - yikesawjeez: Memecoin cleanup crew, github.io profiles, security team, screenpipe/supabase.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=7260
+
+**02:05:31** - TrenchBuddy: Launching AI agent, working on EC2 and authorization.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=7531
+
+**02:09:49** - TSSnft: Sneakerhead Society introduction, exploring AI agent solutions.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=7789
+
+**02:11:40** - SidDegen: Question about the future of AI agents.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=7900
+
+**02:16:15** - GoatOfGamblers: Building a permissionless polymarket for memecoins.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=8175
+
+**02:18:01** - Shaw's closing remarks, focus on stability and applications, call for contributions.
+ - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=8281
+
## Summary
-This is the third part of the live stream series "AI Agent Dev School" hosted by Shaw from ai16z, focusing on building AI agents using the Eliza framework.
+The fourth weekly ai16z meeting, hosted by Shaw, focused on accountability and sharing updates on the Eliza AI agent project. The key themes and events were:
+
+**Individual Updates** - Many contributors gave updates on their work, including new plugins (like a plugin for emotional responses and Coinbase integration), infrastructure improvements (like GitHub integration, a testing framework, and a unified messaging bus), applications (like a validator run by an AI agent, Reddit and Slack integration, text/video to video projects, uncensored AI agents, and a Solana wallet prototype), and community projects (like an AI-generated PFP collection). Many of the updates focused on shipping and getting things done, rather than future plans.
+
+**Education and Outreach** - Shaw highlighted the AI agent dev school as a key achievement, emphasizing education and onboarding for new developers. Yoda, a CS professor, shared his experience using agents in his classes.
+
+**Community Growth and Coordination** - The rapid growth of the project led to discussion about improving communication and coordination. Ideas included a newsletter, better documentation, summarizing contributions (which Jin had already started working on), and AI agents to help manage open source projects and communities.
+
+**Trending Topics** - Plugins, TEEs (Trusted Execution Environments), and multi-chain integration were discussed extensively. There was also a lot of excitement about the potential for AI agents in gaming and metaverse applications.
+
+**Partnerships and Announcements** - Shaw hinted at exciting, unannounced partnerships and projects. The successful launch of an AI-generated PFP collection by a partner was also highlighted, demonstrating community engagement and the potential for partner-led projects (though it was emphasized that this PFP project was not officially affiliated with ai16z). Discussion around the project suggested that future airdrops to contributors are a possibility.
+
+**Technical Details** - TypeScript was identified as the dominant language for agent development. There was a brief discussion about the role of Python for data-related tasks. The importance of build stability and a good developer experience was emphasized. Cursor and Claude were recommended as developer tools.
-**Key takeaways:**
+**Open Source and Community Contributions** - The open-source nature of the project was stressed, with encouragement for community contributions and collaboration. Several contributors offered their expertise and services to others in the community.
-* **Updating Eliza:** Shaw emphasizes staying up-to-date with the rapidly evolving Eliza project due to frequent bug fixes and new features. He provides instructions on pulling the latest changes from the main branch on GitHub.
-* **Focus on Providers and Evaluators:** The stream focuses on building a practical provider-evaluator loop to demonstrate a popular use case for AI agents – filling out a form by extracting user information.
-* **Form Builder Example:** Shaw walks the audience through building a "form provider" that gathers a user's name, location, and job. This provider utilizes a cache to store already extracted information and instructs the agent to prompt the user for any missing details.
-* **Evaluator Role:** The evaluator continually checks the cache for the completeness of user data. Once all information is extracted, the evaluator triggers an action to send the collected data to an external API (simulated in the example).
-* **Live Coding and AI Assistance:** Shaw live codes the example, using tools like "Code2Prompt" and Claude AI to help generate and refine the code. He advocates for writing code in a human-readable manner, utilizing comments to provide context and guidance for both developers and AI assistants.
-* **Agentic Applications:** Shaw highlights the potential of agentic applications to replicate existing website functionality through conversational interfaces, bringing services directly to users within their preferred social media platforms.
-* **Community Engagement:** Shaw encourages active participation from the community, suggesting contributions to the project through pull requests and feedback on desired features and patterns for future Dev School sessions.
+Overall, the meeting conveyed a sense of rapid progress, excitement, and a strong community spirit driving the Eliza project forward.
-**Overall, this live stream provided a practical tutorial on building a common AI agent use case (form filling) while emphasizing the potential of the Eliza framework for developing a wide range of agentic applications.**
## Hot Takes
-1. **"I'm just going to struggle bus some code today." (00:09:31,664)** - Shaw embraces a "struggle bus" approach, showcasing live coding with errors and debugging, reflecting the reality of AI agent development. This contrasts with polished tutorials, highlighting the iterative and messy nature of this new technology.
+1. **"But they're really fucking cool. Really, really, really cool stuff...you're going to have to see it on the timeline when it drops." (00:03:43)** - Shaw teases secret projects with strong conviction, building anticipation and hype, but offering zero specifics. This generates buzz but can also frustrate listeners wanting concrete info.
-2. **"I'm actually not gonna put this in a plugin. I'm gonna put this in the agent... just so you can see what happens if you were to, like, make your own agent without using a plugin at all." (00:37:24,793)** - Shaw goes against the Eliza framework's plugin structure, showing viewers how to bypass it entirely. This bold move emphasizes flexibility, but could spark debate on best practices and potential drawbacks.
+2. **"The whole server is kind of set up where...this project can start to feel more like a video game in a way. And we have these interfaces and AI agents that are playing all sorts of different roles throughout." (00:08:05)** - Jin envisions the project evolving into a gamified experience. This could be a controversial approach to open-source development, as it might prioritize gamification over core functionality or alienate some contributors.
-3. **"I really don't remember conversations from people very well, like verbatim, but I definitely remember like the gist, the context, the really needy ideas." (00:24:48,180)** - Shaw draws a controversial parallel between human memory and the Eliza agent's fact extraction. Reducing human interaction to "needy ideas" is provocative, questioning the depth of social understanding AI agents currently possess.
+3. **"if we're really going to go to AGI we got to have some kind of Ubi component in there." (00:14:44)** - Reality Spiral casually connects AGI development with Universal Basic Income, a potentially contentious socio-economic topic that intertwines technological advancement with wealth distribution.
-4. **"It's just an LLM. It's just making those numbers up. It could be off. I don't really buy the confidence here." (01:13:56,971)** - Shaw dismisses the confidence scores generated by the Large Language Model (LLM), revealing a distrust of these black-box outputs. This skepticism is crucial in a field where relying solely on AI's self-assessment can be misleading.
+4. **"I've been screaming out of my lungs for grok 3 to come out and uh y'all are slow Developers." (01:47:12)** - Roberto jokingly (or perhaps not) criticizes the development speed of Grok, creating a moment of tension and sparking debate about the pace of AI development in general (especially relative to open-source community projects).
-5. **"Dude, that's a $250 million market cap token. Let's get that shit in Bubba Cat." (01:45:34,809)** - Shaw throws out a blunt, market-driven statement regarding the AIXBT token. Bringing finance directly into the technical discussion highlights the intertwined nature of AI development and potential financial incentives, a topic often tiptoed around.
+5. **"agents are going to replace the application layer...I am the fucking website" (02:12:11 & 02:12:58)** - Shaw's bold prediction about the future of the internet and applications, positioning agents not just as tools but as the very foundation of online interaction. This radical vision is sure to stir debate about the future of web development and user experience.
diff --git a/docs/docs/advanced/eliza-in-tee.md b/docs/docs/advanced/eliza-in-tee.md
new file mode 100644
index 0000000000..3763005ae7
--- /dev/null
+++ b/docs/docs/advanced/eliza-in-tee.md
@@ -0,0 +1,314 @@
+---
+sidebar_position: 17
+---
+
+# 🫖 Eliza in TEE
+
+![](/img/eliza_in_tee.jpg)
+
+## Overview
+
+The Eliza agent can be deployed in a TEE environment to ensure the security and privacy of the agent's data. This guide will walk you through the process of setting up and running an Eliza agent in a TEE environment by utilizing the TEE Plugin in the Eliza Framework.
+
+### Background
+
+The TEE Plugin in the Eliza Framework is built on top of the [Dstack SDK](https://github.com/Dstack-TEE/dstack), which is designed to simplify the steps for developers to deploy programs to CVM (Confidential VM), and to follow the security best practices by default. The main features include:
+
+- Convert any docker container to a CVM image to deploy on supported TEEs
+- Remote Attestation API and a chain-of-trust visualization on Web UI
+- Automatic RA-HTTPS wrapping with content addressing domain on 0xABCD.dstack.host
+- Decouple the app execution and state persistent from specific hardware with decentralized Root-of-Trust
+
+---
+
+## Core Components
+
+Eliza's TEE implementation consists of two primary providers that handle secure key managementoperations and remote attestations.
+
+These components work together to provide:
+
+1. Secure key derivation within the TEE
+2. Verifiable proof of TEE execution
+3. Support for both development (simulator) and production environments
+
+The providers are typically used together, as seen in the wallet key derivation process where each derived key includes an attestation quote to prove it was generated within the TEE environment.
+
+---
+
+### Derive Key Provider
+
+The DeriveKeyProvider enables secure key derivation within TEE environments. It supports:
+
+- Multiple TEE modes:
+ - `LOCAL`: Connects to simulator at `localhost:8090` for local development on Mac/Windows
+ - `DOCKER`: Connects to simulator via `host.docker.internal:8090` for local development on Linux
+ - `PRODUCTION`: Connects to actual TEE environment when deployed to the [TEE Cloud](https://teehouse.vercel.app)
+
+Key features:
+
+- Support to deriveEd25519 (Solana) and ECDSA (EVM) keypairs
+- Generates deterministic keys based on a secret salt and agent ID
+- Includes remote attestation for each derived key
+- Supports raw key derivation for custom use cases
+
+Example usage:
+
+```typescript
+const provider = new DeriveKeyProvider(teeMode);
+// For Solana
+const { keypair, attestation } = await provider.deriveEd25519Keypair(
+ "/",
+ secretSalt,
+ agentId
+);
+// For EVM
+const { keypair, attestation } = await provider.deriveEcdsaKeypair(
+ "/",
+ secretSalt,
+ agentId
+);
+```
+
+---
+
+### Remote Attestation Provider
+
+The RemoteAttestationProvider handles TEE environment verification and quote generation. It:
+
+- Connects to the same TEE modes as DeriveKeyProvider
+- Generates TDX quotes with replay protection (RTMRs)
+- Provides attestation data that can be verified by third parties
+
+Key features:
+
+- Generates attestation quotes with custom report data
+- Includes timestamp for quote verification
+- Supports both simulator and production environments
+
+Example usage:
+
+```typescript
+const provider = new RemoteAttestationProvider(teeMode);
+const quote = await provider.generateAttestation(reportData);
+```
+
+## Tutorial
+
+---
+
+### Prerequisites
+
+Before getting started with Eliza, ensure you have:
+
+- [Docker Desktop](https://www.docker.com/products/docker-desktop/) or [Orbstack](https://orbstack.dev/) (Orbstack is recommended)
+- For Mac/Windows: Check the prerequisites from [Quickstart Guide](./quickstart.md)
+- For Linux: You just need Docker
+
+---
+
+### Environment Setup
+
+To set up your environment for TEE development:
+
+1. **Configure TEE Mode**
+
+ Set the `TEE_MODE` environment variable to one of:
+
+ ```env
+ # For Mac/Windows local development
+ TEE_MODE=LOCAL
+
+ # For Linux/Docker local development
+ TEE_MODE=DOCKER
+
+ # For production deployment
+ TEE_MODE=PRODUCTION
+ ```
+
+2. **Set Required Environment Variables**
+
+ ```env
+ # Required for key derivation
+ WALLET_SECRET_SALT=your_secret_salt
+ ```
+
+3. **Start the TEE Simulator**
+
+ ```bash
+ docker pull phalanetwork/tappd-simulator:latest
+ # by default the simulator is available in localhost:8090
+ docker run --rm -p 8090:8090 phalanetwork/tappd-simulator:latest
+ ```
+
+### Run an Eliza Agent Locally with TEE Simulator
+
+1. **Configure Eliza Agent**
+
+ Go through the [configuration guide](./configuration.md) to set up your Eliza agent.
+2. **Start the TEE Simulator**
+ Follow the simulator setup instructions above based on your TEE mode.
+
+3. **For Mac/Windows**
+
+ Make sure to set the `TEE_MODE` environment variable to `LOCAL`. Then you can install the dependencies and run the agent locally:
+
+ ```bash
+ pnpm i
+ pnpm build
+ pnpm start --character=./characters/yourcharacter.character.json
+ ```
+
+4. **Verify TEE Attestation**
+
+ You can verify the TEE attestation quote by going to the [TEE RA Explorer](https://ra-quote-explorer.vercel.app/) and pasting the attestation quote from the agent logs. Here's an example of interacting with the Eliza agent to ask for the agent's wallet address:
+
+ ```bash
+ You: what's your wallet address?
+ ```
+
+ Log output from the agent:
+
+ ```bash
+ Generating attestation for: {"agentId":"025e0996-69d7-0dce-8189-390e354fd1c1","publicKey":"9yZBmCRRFEBtA3KYokxC24igv1ijFp6tyvzKxRs3khTE"}
+ rtmr0: a4a17452e7868f62f77ea2039bd2840e7611a928c26e87541481256f57bfbe3647f596abf6e8f6b5a0e7108acccc6e89
+ rtmr1: db6bcc74a3ac251a6398eca56b2fcdc8c00a9a0b36bc6299e06fb4bb766cb9ecc96de7e367c56032c7feff586f9e557e
+ rtmr2: 2cbe156e110b0cc4b2418600dfa9fb33fc60b3f04b794ec1b8d154b48f07ba8c001cd31f75ca0d0fb516016552500d07
+ rtmr3: eb7110de9956d7b4b1a3397f843b39d92df4caac263f5083e34e3161e4d6686c46c3239e7fbf61241a159d8da6dc6bd1f
+ Remote attestation quote: {
+ quote: '0x0400030081000000736940f888442c8ca8cb432d7a87145f9b7aeab1c5d129ce901716a7506375426ea8741ca69be68e92c5df29f539f103eb60ab6780c56953b0d81af523a031617b32d5e8436cceb019177103f4aceedbf114a846baf8e8e2b8e6d3956e96d6b89d94a0f1a366e6c309d77c77c095a13d2d5e2f8e2d7f51ece4ae5ffc5fe8683a37387bfdb9acb8528f37342360abb64ec05ff438f7e4fad73c69a627de245a31168f69823883ed8ba590c454914690946b7b07918ded5b89dc663c70941f8704978b91a24b54d88038c30d20d14d85016a524f7176c7a7cff7233a2a4405da9c31c8569ac3adfe5147bdb92faee0f075b36e8ce794aaf596facd881588167fbcf5a7d059474c1e4abff645bba8a813f3083c5a425fcc88cd706b19494dedc04be2bc3ab1d71b2a062ddf62d0393d8cb421393cccc932a19d43e315a18a10d216aea4a1752cf3f3b0b2fb36bea655822e2b27c6156970d18e345930a4a589e1850fe84277e0913ad863dffb1950fbeb03a4a17452e7868f62f77ea2039bd2840e7611a928c26e87541481256f57bfbe3647f596abf6e8f6b5a0e7108acccc6e89db6bcc74a3ac251a6398eca56b2fcdc8c00a9a0b36bc6299e06fb4bb766cb9ecc96de7e367c56032c7feff586f9e557e2cbe156e110b0cc4b2418600dfa9fb33fc60b3f04b794ec1b8d154b48f07ba8c001cd31f75ca0d0fb516016552500d07eb7110de9956d7b4b1a3397f843b39d92df4caac263f5083e34e3161e4d6686c46c3239e7fbf61241a159d8da6dc6bd13df734883d4d0d78d670a1d17e28ef09dffbbfbd15063b73113cb5bed692d68cc30c38cb9389403fe6a1c32c35dbac75464b77597e27b854839db51dfde0885462020000530678b9eb99d1b9e08a6231ef00055560f7d3345f54ce355da68725bb38cab0caf84757ddb93db87577758bb06de7923c4ee3583453f284c8b377a1ec2ef613491e051c801a63da5cb42b9c12e26679fcf489f3b14bd5e8f551227b09d976975e0fbd68dcdf129110a5ca8ed8d163dafb60e1ec4831d5285a7fbae81d0e39580000dc010000ebb282d5c6aca9053a21814e9d65a1516ebeaacf6fc88503e794d75cfc5682e86aa04e9d6e58346e013c5c1203afc5c72861e2a7052afcdcb3ddcccd102dd0daeb595968edb6a6c513db8e2155fc302eeca7a34c9ba81289d6941c4c813db9bf7bd0981d188ab131e5ae9c4bb831e4243b20edb7829a6a7a9cf0eae1214b450109d990e2c824c2a60a47faf90c24992583bc5c3da3b58bd8830a4f0ad5c650aa08ae0e067d4251d251e56d70972ad901038082ee9340f103fd687ec7d91a9b8b8652b1a2b7befb4cbfdb6863f00142e0b2e67198ddc8ddbe96dc02762d935594394f173114215cb5abcf55b9815eb545683528c990bfae34c34358dbb19dfc1426f56cba12af325d7a2941c0d45d0ea4334155b790554d3829e3be618eb1bfc6f3a06f488bbeb910b33533c6741bff6c8a0ca43eb2417eec5ecc2f50f65c3b40d26174376202915337c7992cdd44471dee7a7b2038605415a7af593fd9066661e594b26f4298baf6d001906aa8fc1c460966fbc17b2c35e0973f613399936173802cf0453a4e7d8487b6113a77947eef190ea8d47ba531ce51abf5166448c24a54de09d671fd57cbd68154f5995aee6c2ccfd6738387cf3ad9f0ad5e8c7d46fb0a0000000000000000000000bd920a00000000000000000000000000',
+ timestamp: 1733606453433
+ }
+ ```
+
+ Take the `quote` field and paste it into the [TEE RA Explorer](https://ra-quote-explorer.vercel.app/) to verify the attestation. **Note**: The verification will be unverified since the quote is generated from the TEE simulator.
+
+ ![](https://i.imgur.com/xYGMeP4.png)
+
+ ![](https://i.imgur.com/BugdNUy.png)
+
+### Build, Test, and Publish an Eliza Agent Docker Image
+
+Now that we have run the Eliza agent in the TEE simulator, we can build and publish an Eliza agent Docker image to prepare for deployment to a real TEE environment.
+
+First, you need to create a Docker account and publish your image to a container registry. Here we will use [Docker Hub](https://hub.docker.com/) as an example.
+
+Login to Docker Hub:
+
+```bash
+docker login
+```
+
+Build the Docker image:
+
+```bash
+# For Linux/AMD64 machines run
+docker build -t username/eliza-agent:latest .
+
+# For architecture other than AMD64, run
+docker buildx build --platform=linux/amd64 -t username/eliza-agent:latest .
+```
+
+For Linux/AMD64 machines, you can now test the agent locally by updating the `TEE_MODE` environment variable to `DOCKER` and setting the environment variables in the [docker-compose.yaml](https://github.com/ai16z/eliza/blob/main/docker-compose.yaml) file. Once you have done that, you can start the agent by running:
+
+> **Note**: Make sure the TEE simulator is running before starting the agent through docker compose.
+
+```bash
+docker compose up
+```
+
+Publish the Docker image to a container registry:
+
+```bash
+docker push username/eliza-agent:latest
+```
+
+Now we are ready to deploy the Eliza agent to a real TEE environment.
+
+### Run an Eliza Agent in a Real TEE Environment
+
+Before deploying the Eliza agent to a real TEE environment, you need to create a new TEE account on the [TEE Cloud](https://teehouse.vercel.app). Reach out to Phala Network on [Discord](https://discord.gg/phalanetwork) if you need help.
+
+Next, you will need to take the docker-compose.yaml file in the root folder of the project and edit it based on your agent configuration.
+
+> **Note**: The API Keys and other secret environment variables should be set in your secret environment variables configuration in the TEE Cloud dashboard.
+
+```yaml
+# docker-compose.yaml
+services:
+ tee:
+ command: ["pnpm", "start", "--character=./characters/yourcharacter.character.json"]
+ image: username/eliza-agent:latest
+ stdin_open: true
+ tty: true
+ volumes:
+ - /var/run/tappd.sock:/var/run/tappd.sock
+ - tee:/app/packages/client-twitter/src/tweetcache
+ - tee:/app/db.sqlite
+ environment:
+ - REDPILL_API_KEY=$REDPILL_API_KEY
+ - SMALL_REDPILL_MODEL=anthropic/claude-3-5-sonnet
+ - MEDIUM_REDPILL_MODEL=anthropic/claude-3-5-sonnet
+ - LARGE_REDPILL_MODEL=anthropic/claude-3-opus
+ - ELEVENLABS_XI_API_KEY=$ELEVENLABS_XI_API_KEY
+ - ELEVENLABS_MODEL_ID=eleven_multilingual_v2
+ - ELEVENLABS_VOICE_ID=21m00Tcm4TlvDq8ikWAM
+ - ELEVENLABS_VOICE_STABILITY=0.5
+ - ELEVENLABS_VOICE_SIMILARITY_BOOST=0.9
+ - ELEVENLABS_VOICE_STYLE=0.66
+ - ELEVENLABS_VOICE_USE_SPEAKER_BOOST=false
+ - ELEVENLABS_OPTIMIZE_STREAMING_LATENCY=4
+ - ELEVENLABS_OUTPUT_FORMAT=pcm_16000
+ - TWITTER_DRY_RUN=false
+ - TWITTER_USERNAME=$TWITTER_USERNAME
+ - TWITTER_PASSWORD=$TWITTER_PASSWORD
+ - TWITTER_EMAIL=$TWITTER_EMAIL
+ - X_SERVER_URL=$X_SERVER_URL
+ - BIRDEYE_API_KEY=$BIRDEYE_API_KEY
+ - SOL_ADDRESS=So11111111111111111111111111111111111111112
+ - SLIPPAGE=1
+ - RPC_URL=https://api.mainnet-beta.solana.com
+ - HELIUS_API_KEY=$HELIUS_API_KEY
+ - SERVER_PORT=3000
+ - WALLET_SECRET_SALT=$WALLET_SECRET_SALT
+ - TEE_MODE=PRODUCTION
+ ports:
+ - "3000:80"
+ restart: always
+
+volumes:
+ tee:
+```
+
+Now you can deploy the Eliza agent to a real TEE environment. Go to the [TEE Cloud](https://teehouse.vercel.app) and click on the `Create VM` button to configure your Eliza agent deployment.
+
+Click on the `Compose Manifest Mode` tab and paste the docker-compose.yaml file content into the `Compose Manifest` field.
+
+![Compose Manifest](https://i.imgur.com/wl6pddX.png)
+
+Next, go to the `Resources` tab and configure your VM resources.
+
+> **Note**: The `CPU` and `Memory` resources should be greater than the minimum requirements for your agent configuration (Recommended: 2 CPU, 4GB Memory, 50GB Disk).
+
+![Resources](https://i.imgur.com/HsmupO1.png)
+
+Finally, click on the `Submit` button to deploy your Eliza agent.
+
+This will take a few minutes to complete. Once the deployment is complete, you can click on the `View` button to view your Eliza agent.
+
+Here is an example of a deployed agent named `vitailik2077`:
+
+![Deployed Agent](https://i.imgur.com/ie8gpg9.png)
+
+I can go to the dashboard and view the remote attestation info:
+
+![Agent Dashboard](https://i.imgur.com/vUqHGjF.png)
+
+Click on the `Logs` tab to view the agent logs.
+
+![Agent Logs](https://i.imgur.com/aU3i0Dv.png)
+
+Now we can verify the REAL TEE attestation quote by going to the [TEE RA Explorer](https://ra-quote-explorer.vercel.app/) and pasting the attestation quote from the agent logs.
+
+![TEE RA Explorer](https://i.imgur.com/TJ5299l.png)
+
+Congratulations! You have successfully run an Eliza agent in a real TEE environment.
diff --git a/docs/docs/core/characterfile.md b/docs/docs/core/characterfile.md
index a57e5318d1..e25fe58348 100644
--- a/docs/docs/core/characterfile.md
+++ b/docs/docs/core/characterfile.md
@@ -272,7 +272,7 @@ The `settings` object defines additional configurations like secrets and voice m
Use the provided tools to convert documents into knowledge:
- [folder2knowledge](https://github.com/ai16z/characterfile/blob/main/scripts/folder2knowledge.js)
-- [knowledge2folder](https://github.com/ai16z/characterfile/blob/main/scripts/knowledge2character.js)
+- [knowledge2character](https://github.com/ai16z/characterfile/blob/main/scripts/knowledge2character.js)
- [tweets2character](https://github.com/ai16z/characterfile/blob/main/scripts/tweets2character.js)
Example:
diff --git a/docs/docs/faq.md b/docs/docs/faq.md
index d06d28653c..1d6d4a3226 100644
--- a/docs/docs/faq.md
+++ b/docs/docs/faq.md
@@ -16,7 +16,7 @@ To begin building your own AI agents with Eliza, follow these steps:
1. **Install Python, Node.js and pnpm**: Ensure you have the necessary software prerequisites installed on your system. We use node v23.
2. **Set up your environment**: Create a `.env` file and populate it with the required API keys, database configuration, and platform-specific tokens.
-3. **Install Eliza**: Use the command `npm install eliza` or `pnpm install eliza` to install the Eliza package.
+3. **Install Eliza**: Use the command `npm install @ai16z/eliza` or `pnpm add @ai16z/eliza` to install the Eliza package.
4. **Configure your database**: Eliza currently relies on Supabase for local development. Follow the instructions in the documentation to set up your Supabase project and database.
5. **Define your agent's character**: Create a character file using the provided JSON format to specify your agent's personality, knowledge, and behavior.
6. **Run Eliza locally**: Use the provided commands to start the Eliza framework and interact with your agent.
diff --git a/docs/docs/guides/advanced.md b/docs/docs/guides/advanced.md
index 5b9ec91a64..a75cbb0a59 100644
--- a/docs/docs/guides/advanced.md
+++ b/docs/docs/guides/advanced.md
@@ -399,3 +399,4 @@ debug("Detailed operation info: %O", {
- [Trust Engine Documentation](../advanced/trust-engine.md) for scoring system
- [Autonomous Trading Guide](../advanced/autonomous-trading.md) for trading features
- [Fine-tuning Guide](../advanced/fine-tuning.md) for model optimization
+- [Eliza in TEE](../advanced/eliza-in-tee.md) for TEE integration
diff --git a/docs/docs/guides/local-development.md b/docs/docs/guides/local-development.md
index ca871c4b0e..26f66dec59 100644
--- a/docs/docs/guides/local-development.md
+++ b/docs/docs/guides/local-development.md
@@ -94,6 +94,22 @@ pnpm run test:watch # Run tests in watch mode
pnpm run lint # Lint code
```
+### Direct Client Chat UI
+
+```
+# Open a terminal and Start with specific character
+pnpm run dev --characters="characters/my-character.json"
+```
+```
+# Open a 2nd terminal and start the client
+pnpm start:client
+```
+
+Look for the message:
+` ➜ Local: http://localhost:5173/`
+Click on that link or open a browser window to that location. Once you do that you should see the chat interface connect with the system and you can start interacting with your character.
+
+
## Database Development
### SQLite (Recommended for Development)
diff --git a/docs/docs/guides/wsl.md b/docs/docs/guides/wsl.md
new file mode 100644
index 0000000000..3945e3a030
--- /dev/null
+++ b/docs/docs/guides/wsl.md
@@ -0,0 +1,84 @@
+---
+sidebar_position: 5
+title: WSL Setup Guide
+description: Guide for setting up Eliza on Windows using WSL (Windows Subsystem for Linux)
+---
+
+# WSL Setup Guide
+Steps to run Eliza on Windows computer using WSL.
+[AI Dev School Tutorial](https://www.youtube.com/watch?v=ArptLpQiKfI)
+
+
+## Install WSL
+
+1. Open PowerShell as Administrator and run:
+```powershell
+wsl --install
+```
+
+2. Restart your computer
+3. Launch Ubuntu from the Start menu and create your Linux username/password
+
+## Install Dependencies
+
+1. Update Ubuntu packages:
+```bash
+sudo apt update && sudo apt upgrade -y
+```
+
+2. Install system dependencies:
+```bash
+sudo apt install -y \
+ build-essential \
+ python3 \
+ python3-pip \
+ git \
+ curl \
+ ffmpeg \
+ libtool-bin \
+ autoconf \
+ automake \
+ libopus-dev
+```
+
+3. Install Node.js via nvm:
+```bash
+curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
+source ~/.bashrc
+nvm install 23
+nvm use 23
+```
+
+4. Install pnpm:
+```bash
+curl -fsSL https://get.pnpm.io/install.sh | sh -
+source ~/.bashrc
+```
+
+## Optional: CUDA Support
+
+If you have an NVIDIA GPU and want CUDA support:
+
+1. Install CUDA Toolkit on Windows from [NVIDIA's website](https://developer.nvidia.com/cuda-downloads)
+2. WSL will automatically detect and use the Windows CUDA installation
+
+## Clone and Setup Eliza
+
+Follow the [Quickstart Guide](../quickstart.md) starting from the "Installation" section.
+
+## Troubleshooting
+
+- If you encounter `node-gyp` errors, ensure build tools are installed:
+```bash
+sudo apt install -y nodejs-dev node-gyp
+```
+
+- For audio-related issues, verify ffmpeg installation:
+```bash
+ffmpeg -version
+```
+
+- For permission issues, ensure your user owns the project directory:
+```bash
+sudo chown -R $USER:$USER ~/path/to/eliza
+```
\ No newline at end of file
diff --git a/docs/docs/quickstart.md b/docs/docs/quickstart.md
index 8fa9c5645d..206e148602 100644
--- a/docs/docs/quickstart.md
+++ b/docs/docs/quickstart.md
@@ -7,8 +7,6 @@ sidebar_position: 2
## Prerequisites
Before getting started with Eliza, ensure you have:
-
-- [Python 2.7+](https://www.python.org/downloads/)
- [Node.js 23+](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
- [pnpm 9+](https://pnpm.io/installation)
- Git for version control
@@ -78,7 +76,7 @@ Before getting started with Eliza, ensure you have:
Eliza supports multiple AI models:
- **Heurist**: Set `modelProvider: "heurist"` in your character file. Most models are uncensored.
- - LLM: Select available LLMs [here](https://docs.heurist.ai/dev-guide/supported-models#large-language-models-llms) and configure `SMALL_HEURIST_LANGUAGE_MODEL`,`MEDIUM_HEURIST_LANGUAGE_MODEL`,`LARGE_HEURIST_LANGUAGE_MODEL`
+ - LLM: Select available LLMs [here](https://docs.heurist.ai/dev-guide/supported-models#large-language-models-llms) and configure `SMALL_HEURIST_MODEL`,`MEDIUM_HEURIST_MODEL`,`LARGE_HEURIST_MODEL`
- Image Generation: Select available Stable Diffusion or Flux models [here](https://docs.heurist.ai/dev-guide/supported-models#image-generation-models) and configure `HEURIST_IMAGE_MODEL` (default is FLUX.1-dev)
- **Llama**: Set `XAI_MODEL=meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo`
- **Grok**: Set `XAI_MODEL=grok-beta`
@@ -125,6 +123,22 @@ You set which model to use inside the character JSON file
pnpm start --characters="characters/trump.character.json,characters/tate.character.json"
```
+3. **Interact with the Agent**
+
+ Now you're ready to start a conversation with your agent!
+ Open a new terminal window
+
+ ```bash
+ pnpm start:client
+ ```
+
+ Once the client is running, you'll see a message like this:
+```
+➜ Local: http://localhost:5173/
+```
+
+ Simply click the link or open your browser to `http://localhost:5173/`. You'll see the chat interface connect to the system, and you can begin interacting with your character.
+
## Platform Integration
### Discord Bot Setup
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 680b8fef40..841fa896a5 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -80,6 +80,11 @@ const sidebars = {
id: "guides/local-development",
label: "Local Development",
},
+ {
+ type: "doc",
+ id: "guides/wsl",
+ label: "WSL Setup",
+ },
],
},
{
@@ -107,6 +112,11 @@ const sidebars = {
id: "advanced/autonomous-trading",
label: "Autonomous Trading",
},
+ {
+ type: "doc",
+ id: "advanced/eliza-in-tee",
+ label: "Eliza in TEE",
+ },
],
},
{
diff --git a/docs/sidebars.js.bak b/docs/sidebars.js.bak
index 13b55a0f76..d1b16db730 100644
--- a/docs/sidebars.js.bak
+++ b/docs/sidebars.js.bak
@@ -102,6 +102,11 @@ const sidebars = {
id: "advanced/autonomous-trading",
label: "Autonomous Trading",
},
+ {
+ type: "doc",
+ id: "advanced/eliza-in-tee",
+ label: "Eliza in TEE",
+ },
],
},
{
diff --git a/docs/static/img/eliza_in_tee.jpg b/docs/static/img/eliza_in_tee.jpg
new file mode 100644
index 0000000000..507afc6f6a
Binary files /dev/null and b/docs/static/img/eliza_in_tee.jpg differ
diff --git a/package.json b/package.json
index 7e9b1a9194..8c8886662a 100644
--- a/package.json
+++ b/package.json
@@ -3,8 +3,9 @@
"scripts": {
"preinstall": "npx only-allow pnpm",
"build": "turbo run build --filter=!eliza-docs",
+ "build-docker": "turbo run build",
"start": "pnpm --filter \"@ai16z/agent\" start --isRoot",
- "start:client": "pnpm --dir client start --isRoot",
+ "start:client": "pnpm --dir client dev",
"start:debug": "cross-env NODE_ENV=development VERBOSE=true DEBUG=eliza:* pnpm --filter \"@ai16z/agent\" start --isRoot",
"dev": "bash ./scripts/dev.sh",
"lint": "bash ./scripts/lint.sh",
diff --git a/packages/client-farcaster/pnpm-lock.yaml b/packages/client-farcaster/pnpm-lock.yaml
deleted file mode 100644
index 3ed01a948f..0000000000
--- a/packages/client-farcaster/pnpm-lock.yaml
+++ /dev/null
@@ -1,1478 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-importers:
-
- .:
- dependencies:
- '@farcaster/hub-nodejs':
- specifier: ^0.12.7
- version: 0.12.7(zod@3.23.8)
- viem:
- specifier: ^2.21.47
- version: 2.21.49(zod@3.23.8)
- devDependencies:
- tsup:
- specifier: ^8.3.5
- version: 8.3.5(postcss@8.4.49)(yaml@2.6.1)
-
-packages:
-
- '@adraffy/ens-normalize@1.11.0':
- resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==}
-
- '@esbuild/aix-ppc64@0.24.0':
- resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [aix]
-
- '@esbuild/android-arm64@0.24.0':
- resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm@0.24.0':
- resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-x64@0.24.0':
- resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/darwin-arm64@0.24.0':
- resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-x64@0.24.0':
- resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [darwin]
-
- '@esbuild/freebsd-arm64@0.24.0':
- resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [freebsd]
-
- '@esbuild/freebsd-x64@0.24.0':
- resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/linux-arm64@0.24.0':
- resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm@0.24.0':
- resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-ia32@0.24.0':
- resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-loong64@0.24.0':
- resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==}
- engines: {node: '>=18'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-mips64el@0.24.0':
- resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==}
- engines: {node: '>=18'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.24.0':
- resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.24.0':
- resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==}
- engines: {node: '>=18'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-s390x@0.24.0':
- resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==}
- engines: {node: '>=18'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-x64@0.24.0':
- resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/netbsd-x64@0.24.0':
- resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/openbsd-arm64@0.24.0':
- resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openbsd]
-
- '@esbuild/openbsd-x64@0.24.0':
- resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/sunos-x64@0.24.0':
- resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/win32-arm64@0.24.0':
- resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-ia32@0.24.0':
- resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-x64@0.24.0':
- resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [win32]
-
- '@faker-js/faker@7.6.0':
- resolution: {integrity: sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==}
- engines: {node: '>=14.0.0', npm: '>=6.0.0'}
-
- '@farcaster/core@0.15.6':
- resolution: {integrity: sha512-Vxq2JhqdZYEMjH4PIwPXQkalY46S4Mol2oCSHUafXenDx6WSoQJqF/4an4KyxGQbmA7Cf8+hl6zuNzkkHXEUyQ==}
-
- '@farcaster/hub-nodejs@0.12.7':
- resolution: {integrity: sha512-05zXNqnHRBSbOkHl0KDh6l60nHK5MiKFky0JBGbdOZXdkFCk4FIiHv9AGLxjFXr/FxA3jSTHUJfvRRe5TonjRw==}
-
- '@grpc/grpc-js@1.11.3':
- resolution: {integrity: sha512-i9UraDzFHMR+Iz/MhFLljT+fCpgxZ3O6CxwGJ8YuNYHJItIHUzKJpW2LvoFZNnGPwqc9iWy9RAucxV0JoR9aUQ==}
- engines: {node: '>=12.10.0'}
-
- '@grpc/proto-loader@0.7.13':
- resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==}
- engines: {node: '>=6'}
- hasBin: true
-
- '@isaacs/cliui@8.0.2':
- resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
- engines: {node: '>=12'}
-
- '@jridgewell/gen-mapping@0.3.5':
- resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/resolve-uri@3.1.2':
- resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/set-array@1.2.1':
- resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/sourcemap-codec@1.5.0':
- resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
-
- '@jridgewell/trace-mapping@0.3.25':
- resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
-
- '@js-sdsl/ordered-map@4.4.2':
- resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
-
- '@noble/curves@1.6.0':
- resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==}
- engines: {node: ^14.21.3 || >=16}
-
- '@noble/curves@1.7.0':
- resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==}
- engines: {node: ^14.21.3 || >=16}
-
- '@noble/hashes@1.5.0':
- resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==}
- engines: {node: ^14.21.3 || >=16}
-
- '@noble/hashes@1.6.0':
- resolution: {integrity: sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==}
- engines: {node: ^14.21.3 || >=16}
-
- '@pkgjs/parseargs@0.11.0':
- resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
- engines: {node: '>=14'}
-
- '@protobufjs/aspromise@1.1.2':
- resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
-
- '@protobufjs/base64@1.1.2':
- resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
-
- '@protobufjs/codegen@2.0.4':
- resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
-
- '@protobufjs/eventemitter@1.1.0':
- resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
-
- '@protobufjs/fetch@1.1.0':
- resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
-
- '@protobufjs/float@1.0.2':
- resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
-
- '@protobufjs/inquire@1.1.0':
- resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
-
- '@protobufjs/path@1.1.2':
- resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
-
- '@protobufjs/pool@1.1.0':
- resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
-
- '@protobufjs/utf8@1.1.0':
- resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
-
- '@rollup/rollup-android-arm-eabi@4.27.4':
- resolution: {integrity: sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==}
- cpu: [arm]
- os: [android]
-
- '@rollup/rollup-android-arm64@4.27.4':
- resolution: {integrity: sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==}
- cpu: [arm64]
- os: [android]
-
- '@rollup/rollup-darwin-arm64@4.27.4':
- resolution: {integrity: sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==}
- cpu: [arm64]
- os: [darwin]
-
- '@rollup/rollup-darwin-x64@4.27.4':
- resolution: {integrity: sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==}
- cpu: [x64]
- os: [darwin]
-
- '@rollup/rollup-freebsd-arm64@4.27.4':
- resolution: {integrity: sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==}
- cpu: [arm64]
- os: [freebsd]
-
- '@rollup/rollup-freebsd-x64@4.27.4':
- resolution: {integrity: sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==}
- cpu: [x64]
- os: [freebsd]
-
- '@rollup/rollup-linux-arm-gnueabihf@4.27.4':
- resolution: {integrity: sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm-musleabihf@4.27.4':
- resolution: {integrity: sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm64-gnu@4.27.4':
- resolution: {integrity: sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==}
- cpu: [arm64]
- os: [linux]
-
- '@rollup/rollup-linux-arm64-musl@4.27.4':
- resolution: {integrity: sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==}
- cpu: [arm64]
- os: [linux]
-
- '@rollup/rollup-linux-powerpc64le-gnu@4.27.4':
- resolution: {integrity: sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==}
- cpu: [ppc64]
- os: [linux]
-
- '@rollup/rollup-linux-riscv64-gnu@4.27.4':
- resolution: {integrity: sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==}
- cpu: [riscv64]
- os: [linux]
-
- '@rollup/rollup-linux-s390x-gnu@4.27.4':
- resolution: {integrity: sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==}
- cpu: [s390x]
- os: [linux]
-
- '@rollup/rollup-linux-x64-gnu@4.27.4':
- resolution: {integrity: sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==}
- cpu: [x64]
- os: [linux]
-
- '@rollup/rollup-linux-x64-musl@4.27.4':
- resolution: {integrity: sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==}
- cpu: [x64]
- os: [linux]
-
- '@rollup/rollup-win32-arm64-msvc@4.27.4':
- resolution: {integrity: sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==}
- cpu: [arm64]
- os: [win32]
-
- '@rollup/rollup-win32-ia32-msvc@4.27.4':
- resolution: {integrity: sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==}
- cpu: [ia32]
- os: [win32]
-
- '@rollup/rollup-win32-x64-msvc@4.27.4':
- resolution: {integrity: sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==}
- cpu: [x64]
- os: [win32]
-
- '@scure/base@1.1.9':
- resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==}
-
- '@scure/bip32@1.5.0':
- resolution: {integrity: sha512-8EnFYkqEQdnkuGBVpCzKxyIwDCBLDVj3oiX0EKUFre/tOjL/Hqba1D6n/8RcmaQy4f95qQFrO2A8Sr6ybh4NRw==}
-
- '@scure/bip39@1.4.0':
- resolution: {integrity: sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==}
-
- '@types/estree@1.0.6':
- resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
-
- '@types/node@22.9.3':
- resolution: {integrity: sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==}
-
- abitype@1.0.6:
- resolution: {integrity: sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==}
- peerDependencies:
- typescript: '>=5.0.4'
- zod: ^3 >=3.22.0
- peerDependenciesMeta:
- typescript:
- optional: true
- zod:
- optional: true
-
- ansi-regex@5.0.1:
- resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
- engines: {node: '>=8'}
-
- ansi-regex@6.1.0:
- resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
- engines: {node: '>=12'}
-
- ansi-styles@4.3.0:
- resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
- engines: {node: '>=8'}
-
- ansi-styles@6.2.1:
- resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
- engines: {node: '>=12'}
-
- any-promise@1.3.0:
- resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
-
- balanced-match@1.0.2:
- resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
-
- base-x@4.0.0:
- resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==}
-
- brace-expansion@2.0.1:
- resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
-
- bs58@5.0.0:
- resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==}
-
- bundle-require@5.0.0:
- resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
- peerDependencies:
- esbuild: '>=0.18'
-
- cac@6.7.14:
- resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
- engines: {node: '>=8'}
-
- chokidar@4.0.1:
- resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==}
- engines: {node: '>= 14.16.0'}
-
- cliui@8.0.1:
- resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
- engines: {node: '>=12'}
-
- color-convert@2.0.1:
- resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
- engines: {node: '>=7.0.0'}
-
- color-name@1.1.4:
- resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-
- commander@4.1.1:
- resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
- engines: {node: '>= 6'}
-
- consola@3.2.3:
- resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==}
- engines: {node: ^14.18.0 || >=16.10.0}
-
- cross-spawn@7.0.6:
- resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
- engines: {node: '>= 8'}
-
- debug@4.3.7:
- resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
- engines: {node: '>=6.0'}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
- eastasianwidth@0.2.0:
- resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
-
- emoji-regex@8.0.0:
- resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-
- emoji-regex@9.2.2:
- resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
-
- esbuild@0.24.0:
- resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==}
- engines: {node: '>=18'}
- hasBin: true
-
- escalade@3.2.0:
- resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
- engines: {node: '>=6'}
-
- eventemitter3@5.0.1:
- resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
-
- fdir@6.4.2:
- resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==}
- peerDependencies:
- picomatch: ^3 || ^4
- peerDependenciesMeta:
- picomatch:
- optional: true
-
- foreground-child@3.3.0:
- resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
- engines: {node: '>=14'}
-
- fsevents@2.3.3:
- resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
- get-caller-file@2.0.5:
- resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
- engines: {node: 6.* || 8.* || >= 10.*}
-
- glob@10.4.5:
- resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
- hasBin: true
-
- is-fullwidth-code-point@3.0.0:
- resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
- engines: {node: '>=8'}
-
- isexe@2.0.0:
- resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
-
- isows@1.0.6:
- resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==}
- peerDependencies:
- ws: '*'
-
- jackspeak@3.4.3:
- resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
-
- joycon@3.1.1:
- resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
- engines: {node: '>=10'}
-
- lilconfig@3.1.2:
- resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
- engines: {node: '>=14'}
-
- lines-and-columns@1.2.4:
- resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
-
- load-tsconfig@0.2.5:
- resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
- lodash.camelcase@4.3.0:
- resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
-
- lodash.sortby@4.7.0:
- resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
-
- long@5.2.3:
- resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==}
-
- lru-cache@10.4.3:
- resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
-
- minimatch@9.0.5:
- resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
- engines: {node: '>=16 || 14 >=14.17'}
-
- minipass@7.1.2:
- resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
- engines: {node: '>=16 || 14 >=14.17'}
-
- ms@2.1.3:
- resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
-
- mz@2.7.0:
- resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
-
- nanoid@3.3.7:
- resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- neverthrow@6.2.2:
- resolution: {integrity: sha512-POR1FACqdK9jH0S2kRPzaZEvzT11wsOxLW520PQV/+vKi9dQe+hXq19EiOvYx7lSRaF5VB9lYGsPInynrnN05w==}
-
- object-assign@4.1.1:
- resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
- engines: {node: '>=0.10.0'}
-
- ox@0.1.2:
- resolution: {integrity: sha512-ak/8K0Rtphg9vnRJlbOdaX9R7cmxD2MiSthjWGaQdMk3D7hrAlDoM+6Lxn7hN52Za3vrXfZ7enfke/5WjolDww==}
- peerDependencies:
- typescript: '>=5.4.0'
- peerDependenciesMeta:
- typescript:
- optional: true
-
- package-json-from-dist@1.0.1:
- resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
-
- path-key@3.1.1:
- resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
- engines: {node: '>=8'}
-
- path-scurry@1.11.1:
- resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
- engines: {node: '>=16 || 14 >=14.18'}
-
- picocolors@1.1.1:
- resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
-
- picomatch@4.0.2:
- resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
- engines: {node: '>=12'}
-
- pirates@4.0.6:
- resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
- engines: {node: '>= 6'}
-
- postcss-load-config@6.0.1:
- resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
- engines: {node: '>= 18'}
- peerDependencies:
- jiti: '>=1.21.0'
- postcss: '>=8.0.9'
- tsx: ^4.8.1
- yaml: ^2.4.2
- peerDependenciesMeta:
- jiti:
- optional: true
- postcss:
- optional: true
- tsx:
- optional: true
- yaml:
- optional: true
-
- postcss@8.4.49:
- resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
- engines: {node: ^10 || ^12 || >=14}
-
- protobufjs@7.4.0:
- resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==}
- engines: {node: '>=12.0.0'}
-
- punycode@2.3.1:
- resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
- engines: {node: '>=6'}
-
- readdirp@4.0.2:
- resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==}
- engines: {node: '>= 14.16.0'}
-
- require-directory@2.1.1:
- resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
- engines: {node: '>=0.10.0'}
-
- resolve-from@5.0.0:
- resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
- engines: {node: '>=8'}
-
- rollup@4.27.4:
- resolution: {integrity: sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==}
- engines: {node: '>=18.0.0', npm: '>=8.0.0'}
- hasBin: true
-
- shebang-command@2.0.0:
- resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
- engines: {node: '>=8'}
-
- shebang-regex@3.0.0:
- resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
- engines: {node: '>=8'}
-
- signal-exit@4.1.0:
- resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
- engines: {node: '>=14'}
-
- source-map-js@1.2.1:
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
- engines: {node: '>=0.10.0'}
-
- source-map@0.8.0-beta.0:
- resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
- engines: {node: '>= 8'}
-
- string-width@4.2.3:
- resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
- engines: {node: '>=8'}
-
- string-width@5.1.2:
- resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
- engines: {node: '>=12'}
-
- strip-ansi@6.0.1:
- resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
- engines: {node: '>=8'}
-
- strip-ansi@7.1.0:
- resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
- engines: {node: '>=12'}
-
- sucrase@3.35.0:
- resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
- engines: {node: '>=16 || 14 >=14.17'}
- hasBin: true
-
- thenify-all@1.6.0:
- resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
- engines: {node: '>=0.8'}
-
- thenify@3.3.1:
- resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
-
- tinyexec@0.3.1:
- resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==}
-
- tinyglobby@0.2.10:
- resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
- engines: {node: '>=12.0.0'}
-
- tr46@1.0.1:
- resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
-
- tree-kill@1.2.2:
- resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
- hasBin: true
-
- ts-interface-checker@0.1.13:
- resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
-
- tsup@8.3.5:
- resolution: {integrity: sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA==}
- engines: {node: '>=18'}
- hasBin: true
- peerDependencies:
- '@microsoft/api-extractor': ^7.36.0
- '@swc/core': ^1
- postcss: ^8.4.12
- typescript: '>=4.5.0'
- peerDependenciesMeta:
- '@microsoft/api-extractor':
- optional: true
- '@swc/core':
- optional: true
- postcss:
- optional: true
- typescript:
- optional: true
-
- undici-types@6.19.8:
- resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
-
- viem@2.21.49:
- resolution: {integrity: sha512-NNItYfTv4+yGE5DDKc+S/g2S7KeJn047GwgEYG60FAJlK0FzwuP6lQKSeQ8k7Y4VasfuKPqiT+XiilcCtTRiDQ==}
- peerDependencies:
- typescript: '>=5.0.4'
- peerDependenciesMeta:
- typescript:
- optional: true
-
- webauthn-p256@0.0.10:
- resolution: {integrity: sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA==}
-
- webidl-conversions@4.0.2:
- resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
-
- whatwg-url@7.1.0:
- resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
-
- which@2.0.2:
- resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
- engines: {node: '>= 8'}
- hasBin: true
-
- wrap-ansi@7.0.0:
- resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
- engines: {node: '>=10'}
-
- wrap-ansi@8.1.0:
- resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
- engines: {node: '>=12'}
-
- ws@8.18.0:
- resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
- engines: {node: '>=10.0.0'}
- peerDependencies:
- bufferutil: ^4.0.1
- utf-8-validate: '>=5.0.2'
- peerDependenciesMeta:
- bufferutil:
- optional: true
- utf-8-validate:
- optional: true
-
- y18n@5.0.8:
- resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
- engines: {node: '>=10'}
-
- yaml@2.6.1:
- resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==}
- engines: {node: '>= 14'}
- hasBin: true
-
- yargs-parser@21.1.1:
- resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
- engines: {node: '>=12'}
-
- yargs@17.7.2:
- resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
- engines: {node: '>=12'}
-
- zod@3.23.8:
- resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
-
-snapshots:
-
- '@adraffy/ens-normalize@1.11.0': {}
-
- '@esbuild/aix-ppc64@0.24.0':
- optional: true
-
- '@esbuild/android-arm64@0.24.0':
- optional: true
-
- '@esbuild/android-arm@0.24.0':
- optional: true
-
- '@esbuild/android-x64@0.24.0':
- optional: true
-
- '@esbuild/darwin-arm64@0.24.0':
- optional: true
-
- '@esbuild/darwin-x64@0.24.0':
- optional: true
-
- '@esbuild/freebsd-arm64@0.24.0':
- optional: true
-
- '@esbuild/freebsd-x64@0.24.0':
- optional: true
-
- '@esbuild/linux-arm64@0.24.0':
- optional: true
-
- '@esbuild/linux-arm@0.24.0':
- optional: true
-
- '@esbuild/linux-ia32@0.24.0':
- optional: true
-
- '@esbuild/linux-loong64@0.24.0':
- optional: true
-
- '@esbuild/linux-mips64el@0.24.0':
- optional: true
-
- '@esbuild/linux-ppc64@0.24.0':
- optional: true
-
- '@esbuild/linux-riscv64@0.24.0':
- optional: true
-
- '@esbuild/linux-s390x@0.24.0':
- optional: true
-
- '@esbuild/linux-x64@0.24.0':
- optional: true
-
- '@esbuild/netbsd-x64@0.24.0':
- optional: true
-
- '@esbuild/openbsd-arm64@0.24.0':
- optional: true
-
- '@esbuild/openbsd-x64@0.24.0':
- optional: true
-
- '@esbuild/sunos-x64@0.24.0':
- optional: true
-
- '@esbuild/win32-arm64@0.24.0':
- optional: true
-
- '@esbuild/win32-ia32@0.24.0':
- optional: true
-
- '@esbuild/win32-x64@0.24.0':
- optional: true
-
- '@faker-js/faker@7.6.0': {}
-
- '@farcaster/core@0.15.6(zod@3.23.8)':
- dependencies:
- '@faker-js/faker': 7.6.0
- '@noble/curves': 1.7.0
- '@noble/hashes': 1.6.0
- bs58: 5.0.0
- neverthrow: 6.2.2
- viem: 2.21.49(zod@3.23.8)
- transitivePeerDependencies:
- - bufferutil
- - typescript
- - utf-8-validate
- - zod
-
- '@farcaster/hub-nodejs@0.12.7(zod@3.23.8)':
- dependencies:
- '@farcaster/core': 0.15.6(zod@3.23.8)
- '@grpc/grpc-js': 1.11.3
- '@noble/hashes': 1.6.0
- neverthrow: 6.2.2
- transitivePeerDependencies:
- - bufferutil
- - typescript
- - utf-8-validate
- - zod
-
- '@grpc/grpc-js@1.11.3':
- dependencies:
- '@grpc/proto-loader': 0.7.13
- '@js-sdsl/ordered-map': 4.4.2
-
- '@grpc/proto-loader@0.7.13':
- dependencies:
- lodash.camelcase: 4.3.0
- long: 5.2.3
- protobufjs: 7.4.0
- yargs: 17.7.2
-
- '@isaacs/cliui@8.0.2':
- dependencies:
- string-width: 5.1.2
- string-width-cjs: string-width@4.2.3
- strip-ansi: 7.1.0
- strip-ansi-cjs: strip-ansi@6.0.1
- wrap-ansi: 8.1.0
- wrap-ansi-cjs: wrap-ansi@7.0.0
-
- '@jridgewell/gen-mapping@0.3.5':
- dependencies:
- '@jridgewell/set-array': 1.2.1
- '@jridgewell/sourcemap-codec': 1.5.0
- '@jridgewell/trace-mapping': 0.3.25
-
- '@jridgewell/resolve-uri@3.1.2': {}
-
- '@jridgewell/set-array@1.2.1': {}
-
- '@jridgewell/sourcemap-codec@1.5.0': {}
-
- '@jridgewell/trace-mapping@0.3.25':
- dependencies:
- '@jridgewell/resolve-uri': 3.1.2
- '@jridgewell/sourcemap-codec': 1.5.0
-
- '@js-sdsl/ordered-map@4.4.2': {}
-
- '@noble/curves@1.6.0':
- dependencies:
- '@noble/hashes': 1.5.0
-
- '@noble/curves@1.7.0':
- dependencies:
- '@noble/hashes': 1.6.0
-
- '@noble/hashes@1.5.0': {}
-
- '@noble/hashes@1.6.0': {}
-
- '@pkgjs/parseargs@0.11.0':
- optional: true
-
- '@protobufjs/aspromise@1.1.2': {}
-
- '@protobufjs/base64@1.1.2': {}
-
- '@protobufjs/codegen@2.0.4': {}
-
- '@protobufjs/eventemitter@1.1.0': {}
-
- '@protobufjs/fetch@1.1.0':
- dependencies:
- '@protobufjs/aspromise': 1.1.2
- '@protobufjs/inquire': 1.1.0
-
- '@protobufjs/float@1.0.2': {}
-
- '@protobufjs/inquire@1.1.0': {}
-
- '@protobufjs/path@1.1.2': {}
-
- '@protobufjs/pool@1.1.0': {}
-
- '@protobufjs/utf8@1.1.0': {}
-
- '@rollup/rollup-android-arm-eabi@4.27.4':
- optional: true
-
- '@rollup/rollup-android-arm64@4.27.4':
- optional: true
-
- '@rollup/rollup-darwin-arm64@4.27.4':
- optional: true
-
- '@rollup/rollup-darwin-x64@4.27.4':
- optional: true
-
- '@rollup/rollup-freebsd-arm64@4.27.4':
- optional: true
-
- '@rollup/rollup-freebsd-x64@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-arm-gnueabihf@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-arm-musleabihf@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-arm64-gnu@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-arm64-musl@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-powerpc64le-gnu@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-riscv64-gnu@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-s390x-gnu@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-x64-gnu@4.27.4':
- optional: true
-
- '@rollup/rollup-linux-x64-musl@4.27.4':
- optional: true
-
- '@rollup/rollup-win32-arm64-msvc@4.27.4':
- optional: true
-
- '@rollup/rollup-win32-ia32-msvc@4.27.4':
- optional: true
-
- '@rollup/rollup-win32-x64-msvc@4.27.4':
- optional: true
-
- '@scure/base@1.1.9': {}
-
- '@scure/bip32@1.5.0':
- dependencies:
- '@noble/curves': 1.6.0
- '@noble/hashes': 1.5.0
- '@scure/base': 1.1.9
-
- '@scure/bip39@1.4.0':
- dependencies:
- '@noble/hashes': 1.5.0
- '@scure/base': 1.1.9
-
- '@types/estree@1.0.6': {}
-
- '@types/node@22.9.3':
- dependencies:
- undici-types: 6.19.8
-
- abitype@1.0.6(zod@3.23.8):
- optionalDependencies:
- zod: 3.23.8
-
- ansi-regex@5.0.1: {}
-
- ansi-regex@6.1.0: {}
-
- ansi-styles@4.3.0:
- dependencies:
- color-convert: 2.0.1
-
- ansi-styles@6.2.1: {}
-
- any-promise@1.3.0: {}
-
- balanced-match@1.0.2: {}
-
- base-x@4.0.0: {}
-
- brace-expansion@2.0.1:
- dependencies:
- balanced-match: 1.0.2
-
- bs58@5.0.0:
- dependencies:
- base-x: 4.0.0
-
- bundle-require@5.0.0(esbuild@0.24.0):
- dependencies:
- esbuild: 0.24.0
- load-tsconfig: 0.2.5
-
- cac@6.7.14: {}
-
- chokidar@4.0.1:
- dependencies:
- readdirp: 4.0.2
-
- cliui@8.0.1:
- dependencies:
- string-width: 4.2.3
- strip-ansi: 6.0.1
- wrap-ansi: 7.0.0
-
- color-convert@2.0.1:
- dependencies:
- color-name: 1.1.4
-
- color-name@1.1.4: {}
-
- commander@4.1.1: {}
-
- consola@3.2.3: {}
-
- cross-spawn@7.0.6:
- dependencies:
- path-key: 3.1.1
- shebang-command: 2.0.0
- which: 2.0.2
-
- debug@4.3.7:
- dependencies:
- ms: 2.1.3
-
- eastasianwidth@0.2.0: {}
-
- emoji-regex@8.0.0: {}
-
- emoji-regex@9.2.2: {}
-
- esbuild@0.24.0:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.24.0
- '@esbuild/android-arm': 0.24.0
- '@esbuild/android-arm64': 0.24.0
- '@esbuild/android-x64': 0.24.0
- '@esbuild/darwin-arm64': 0.24.0
- '@esbuild/darwin-x64': 0.24.0
- '@esbuild/freebsd-arm64': 0.24.0
- '@esbuild/freebsd-x64': 0.24.0
- '@esbuild/linux-arm': 0.24.0
- '@esbuild/linux-arm64': 0.24.0
- '@esbuild/linux-ia32': 0.24.0
- '@esbuild/linux-loong64': 0.24.0
- '@esbuild/linux-mips64el': 0.24.0
- '@esbuild/linux-ppc64': 0.24.0
- '@esbuild/linux-riscv64': 0.24.0
- '@esbuild/linux-s390x': 0.24.0
- '@esbuild/linux-x64': 0.24.0
- '@esbuild/netbsd-x64': 0.24.0
- '@esbuild/openbsd-arm64': 0.24.0
- '@esbuild/openbsd-x64': 0.24.0
- '@esbuild/sunos-x64': 0.24.0
- '@esbuild/win32-arm64': 0.24.0
- '@esbuild/win32-ia32': 0.24.0
- '@esbuild/win32-x64': 0.24.0
-
- escalade@3.2.0: {}
-
- eventemitter3@5.0.1: {}
-
- fdir@6.4.2(picomatch@4.0.2):
- optionalDependencies:
- picomatch: 4.0.2
-
- foreground-child@3.3.0:
- dependencies:
- cross-spawn: 7.0.6
- signal-exit: 4.1.0
-
- fsevents@2.3.3:
- optional: true
-
- get-caller-file@2.0.5: {}
-
- glob@10.4.5:
- dependencies:
- foreground-child: 3.3.0
- jackspeak: 3.4.3
- minimatch: 9.0.5
- minipass: 7.1.2
- package-json-from-dist: 1.0.1
- path-scurry: 1.11.1
-
- is-fullwidth-code-point@3.0.0: {}
-
- isexe@2.0.0: {}
-
- isows@1.0.6(ws@8.18.0):
- dependencies:
- ws: 8.18.0
-
- jackspeak@3.4.3:
- dependencies:
- '@isaacs/cliui': 8.0.2
- optionalDependencies:
- '@pkgjs/parseargs': 0.11.0
-
- joycon@3.1.1: {}
-
- lilconfig@3.1.2: {}
-
- lines-and-columns@1.2.4: {}
-
- load-tsconfig@0.2.5: {}
-
- lodash.camelcase@4.3.0: {}
-
- lodash.sortby@4.7.0: {}
-
- long@5.2.3: {}
-
- lru-cache@10.4.3: {}
-
- minimatch@9.0.5:
- dependencies:
- brace-expansion: 2.0.1
-
- minipass@7.1.2: {}
-
- ms@2.1.3: {}
-
- mz@2.7.0:
- dependencies:
- any-promise: 1.3.0
- object-assign: 4.1.1
- thenify-all: 1.6.0
-
- nanoid@3.3.7:
- optional: true
-
- neverthrow@6.2.2: {}
-
- object-assign@4.1.1: {}
-
- ox@0.1.2(zod@3.23.8):
- dependencies:
- '@adraffy/ens-normalize': 1.11.0
- '@noble/curves': 1.6.0
- '@noble/hashes': 1.5.0
- '@scure/bip32': 1.5.0
- '@scure/bip39': 1.4.0
- abitype: 1.0.6(zod@3.23.8)
- eventemitter3: 5.0.1
- transitivePeerDependencies:
- - zod
-
- package-json-from-dist@1.0.1: {}
-
- path-key@3.1.1: {}
-
- path-scurry@1.11.1:
- dependencies:
- lru-cache: 10.4.3
- minipass: 7.1.2
-
- picocolors@1.1.1: {}
-
- picomatch@4.0.2: {}
-
- pirates@4.0.6: {}
-
- postcss-load-config@6.0.1(postcss@8.4.49)(yaml@2.6.1):
- dependencies:
- lilconfig: 3.1.2
- optionalDependencies:
- postcss: 8.4.49
- yaml: 2.6.1
-
- postcss@8.4.49:
- dependencies:
- nanoid: 3.3.7
- picocolors: 1.1.1
- source-map-js: 1.2.1
- optional: true
-
- protobufjs@7.4.0:
- dependencies:
- '@protobufjs/aspromise': 1.1.2
- '@protobufjs/base64': 1.1.2
- '@protobufjs/codegen': 2.0.4
- '@protobufjs/eventemitter': 1.1.0
- '@protobufjs/fetch': 1.1.0
- '@protobufjs/float': 1.0.2
- '@protobufjs/inquire': 1.1.0
- '@protobufjs/path': 1.1.2
- '@protobufjs/pool': 1.1.0
- '@protobufjs/utf8': 1.1.0
- '@types/node': 22.9.3
- long: 5.2.3
-
- punycode@2.3.1: {}
-
- readdirp@4.0.2: {}
-
- require-directory@2.1.1: {}
-
- resolve-from@5.0.0: {}
-
- rollup@4.27.4:
- dependencies:
- '@types/estree': 1.0.6
- optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.27.4
- '@rollup/rollup-android-arm64': 4.27.4
- '@rollup/rollup-darwin-arm64': 4.27.4
- '@rollup/rollup-darwin-x64': 4.27.4
- '@rollup/rollup-freebsd-arm64': 4.27.4
- '@rollup/rollup-freebsd-x64': 4.27.4
- '@rollup/rollup-linux-arm-gnueabihf': 4.27.4
- '@rollup/rollup-linux-arm-musleabihf': 4.27.4
- '@rollup/rollup-linux-arm64-gnu': 4.27.4
- '@rollup/rollup-linux-arm64-musl': 4.27.4
- '@rollup/rollup-linux-powerpc64le-gnu': 4.27.4
- '@rollup/rollup-linux-riscv64-gnu': 4.27.4
- '@rollup/rollup-linux-s390x-gnu': 4.27.4
- '@rollup/rollup-linux-x64-gnu': 4.27.4
- '@rollup/rollup-linux-x64-musl': 4.27.4
- '@rollup/rollup-win32-arm64-msvc': 4.27.4
- '@rollup/rollup-win32-ia32-msvc': 4.27.4
- '@rollup/rollup-win32-x64-msvc': 4.27.4
- fsevents: 2.3.3
-
- shebang-command@2.0.0:
- dependencies:
- shebang-regex: 3.0.0
-
- shebang-regex@3.0.0: {}
-
- signal-exit@4.1.0: {}
-
- source-map-js@1.2.1:
- optional: true
-
- source-map@0.8.0-beta.0:
- dependencies:
- whatwg-url: 7.1.0
-
- string-width@4.2.3:
- dependencies:
- emoji-regex: 8.0.0
- is-fullwidth-code-point: 3.0.0
- strip-ansi: 6.0.1
-
- string-width@5.1.2:
- dependencies:
- eastasianwidth: 0.2.0
- emoji-regex: 9.2.2
- strip-ansi: 7.1.0
-
- strip-ansi@6.0.1:
- dependencies:
- ansi-regex: 5.0.1
-
- strip-ansi@7.1.0:
- dependencies:
- ansi-regex: 6.1.0
-
- sucrase@3.35.0:
- dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- commander: 4.1.1
- glob: 10.4.5
- lines-and-columns: 1.2.4
- mz: 2.7.0
- pirates: 4.0.6
- ts-interface-checker: 0.1.13
-
- thenify-all@1.6.0:
- dependencies:
- thenify: 3.3.1
-
- thenify@3.3.1:
- dependencies:
- any-promise: 1.3.0
-
- tinyexec@0.3.1: {}
-
- tinyglobby@0.2.10:
- dependencies:
- fdir: 6.4.2(picomatch@4.0.2)
- picomatch: 4.0.2
-
- tr46@1.0.1:
- dependencies:
- punycode: 2.3.1
-
- tree-kill@1.2.2: {}
-
- ts-interface-checker@0.1.13: {}
-
- tsup@8.3.5(postcss@8.4.49)(yaml@2.6.1):
- dependencies:
- bundle-require: 5.0.0(esbuild@0.24.0)
- cac: 6.7.14
- chokidar: 4.0.1
- consola: 3.2.3
- debug: 4.3.7
- esbuild: 0.24.0
- joycon: 3.1.1
- picocolors: 1.1.1
- postcss-load-config: 6.0.1(postcss@8.4.49)(yaml@2.6.1)
- resolve-from: 5.0.0
- rollup: 4.27.4
- source-map: 0.8.0-beta.0
- sucrase: 3.35.0
- tinyexec: 0.3.1
- tinyglobby: 0.2.10
- tree-kill: 1.2.2
- optionalDependencies:
- postcss: 8.4.49
- transitivePeerDependencies:
- - jiti
- - supports-color
- - tsx
- - yaml
-
- undici-types@6.19.8: {}
-
- viem@2.21.49(zod@3.23.8):
- dependencies:
- '@noble/curves': 1.6.0
- '@noble/hashes': 1.5.0
- '@scure/bip32': 1.5.0
- '@scure/bip39': 1.4.0
- abitype: 1.0.6(zod@3.23.8)
- isows: 1.0.6(ws@8.18.0)
- ox: 0.1.2(zod@3.23.8)
- webauthn-p256: 0.0.10
- ws: 8.18.0
- transitivePeerDependencies:
- - bufferutil
- - utf-8-validate
- - zod
-
- webauthn-p256@0.0.10:
- dependencies:
- '@noble/curves': 1.6.0
- '@noble/hashes': 1.5.0
-
- webidl-conversions@4.0.2: {}
-
- whatwg-url@7.1.0:
- dependencies:
- lodash.sortby: 4.7.0
- tr46: 1.0.1
- webidl-conversions: 4.0.2
-
- which@2.0.2:
- dependencies:
- isexe: 2.0.0
-
- wrap-ansi@7.0.0:
- dependencies:
- ansi-styles: 4.3.0
- string-width: 4.2.3
- strip-ansi: 6.0.1
-
- wrap-ansi@8.1.0:
- dependencies:
- ansi-styles: 6.2.1
- string-width: 5.1.2
- strip-ansi: 7.1.0
-
- ws@8.18.0: {}
-
- y18n@5.0.8: {}
-
- yaml@2.6.1:
- optional: true
-
- yargs-parser@21.1.1: {}
-
- yargs@17.7.2:
- dependencies:
- cliui: 8.0.1
- escalade: 3.2.0
- get-caller-file: 2.0.5
- require-directory: 2.1.1
- string-width: 4.2.3
- y18n: 5.0.8
- yargs-parser: 21.1.1
-
- zod@3.23.8:
- optional: true
diff --git a/packages/client-farcaster/src/client.ts b/packages/client-farcaster/src/client.ts
index 9aa780dc4f..663ff5640c 100644
--- a/packages/client-farcaster/src/client.ts
+++ b/packages/client-farcaster/src/client.ts
@@ -1,4 +1,4 @@
-import { IAgentRuntime } from "@ai16z/eliza";
+import { IAgentRuntime, elizaLogger } from "@ai16z/eliza";
import { NeynarAPIClient, isApiErrorResponse } from "@neynar/nodejs-sdk";
import { NeynarCastResponse, Cast, Profile, FidRequest, CastId } from "./types";
@@ -63,11 +63,11 @@ export class FarcasterClient {
}
} catch (err) {
if (isApiErrorResponse(err)) {
- console.log(err.response.data);
+ elizaLogger.error('Neynar error: ', err.response.data);
throw err.response.data;
} else {
+ elizaLogger.error('Error: ', err);
throw err;
- console.log(err);
}
}
}
@@ -83,7 +83,6 @@ export class FarcasterClient {
});
const cast = {
hash: response.cast.hash,
- //parentHash: cast.parent_hash,
authorFid: response.cast.author.fid,
text: response.cast.text,
profile: {
@@ -114,12 +113,10 @@ export class FarcasterClient {
fid: request.fid,
limit: request.pageSize,
});
- //console.log(response);
response.casts.map((cast) => {
this.cache.set(`farcaster/cast/${cast.hash}`, cast);
timeline.push({
hash: cast.hash,
- //parentHash: cast.parent_hash,
authorFid: cast.author.fid,
text: cast.text,
profile: {
@@ -175,9 +172,9 @@ export class FarcasterClient {
const result = await this.neynar.fetchBulkUsers({ fids: [fid] });
if (!result.users || result.users.length < 1) {
- console.log("getUserDataByFid ERROR");
+ elizaLogger.error('Error fetching user by fid');
- throw "getUserDataByFid ERROR";
+ throw "getProfile ERROR";
}
const neynarUserProfile = result.users[0];
diff --git a/packages/client-farcaster/src/interactions.ts b/packages/client-farcaster/src/interactions.ts
index d3a1e0b712..6e71e1f45e 100644
--- a/packages/client-farcaster/src/interactions.ts
+++ b/packages/client-farcaster/src/interactions.ts
@@ -5,6 +5,9 @@ import {
Memory,
ModelClass,
stringToUuid,
+ elizaLogger,
+ HandlerCallback,
+ Content,
type IAgentRuntime,
} from "@ai16z/eliza";
import type { FarcasterClient } from "./client";
@@ -34,14 +37,16 @@ export class FarcasterInteractionManager {
try {
await this.handleInteractions();
} catch (error) {
- console.error(error);
+ elizaLogger.error(error);
return;
}
this.timeout = setTimeout(
handleInteractionsLoop,
- (Math.floor(Math.random() * (5 - 2 + 1)) + 2) * 60 * 1000
- ); // Random interval between 2-5 minutes
+ Number(
+ this.runtime.getSetting("FARCASTER_POLL_INTERVAL") || 120
+ ) * 1000 // Default to 2 minutes
+ );
};
handleInteractionsLoop();
@@ -93,7 +98,7 @@ export class FarcasterInteractionManager {
});
const memory: Memory = {
- content: { text: mention.text },
+ content: { text: mention.text, hash: mention.hash },
agentId: this.runtime.agentId,
userId,
roomId,
@@ -103,7 +108,7 @@ export class FarcasterInteractionManager {
agent,
cast: mention,
memory,
- thread
+ thread,
});
}
@@ -114,20 +119,20 @@ export class FarcasterInteractionManager {
agent,
cast,
memory,
- thread
+ thread,
}: {
agent: Profile;
cast: Cast;
memory: Memory;
- thread: Cast[]
+ thread: Cast[];
}) {
if (cast.profile.fid === agent.fid) {
- console.log("skipping cast from bot itself", cast.hash);
+ elizaLogger.info("skipping cast from bot itself", cast.hash);
return;
}
if (!memory.content.text) {
- console.log("skipping cast with no text", cast.hash);
+ elizaLogger.info("skipping cast with no text", cast.hash);
return { text: "", action: "IGNORE" };
}
@@ -143,10 +148,25 @@ export class FarcasterInteractionManager {
timeline
);
+ const formattedConversation = thread
+ .map(
+ (cast) => `@${cast.profile.username} (${new Date(
+ cast.timestamp
+ ).toLocaleString("en-US", {
+ hour: "2-digit",
+ minute: "2-digit",
+ month: "short",
+ day: "numeric",
+ })}):
+ ${cast.text}`
+ )
+ .join("\n\n");
+
const state = await this.runtime.composeState(memory, {
farcasterUsername: agent.username,
timeline: formattedTimeline,
currentPost,
+ formattedConversation,
});
const shouldRespondContext = composeContext({
@@ -176,15 +196,20 @@ export class FarcasterInteractionManager {
);
}
- const shouldRespond = await generateShouldRespond({
+ const shouldRespondResponse = await generateShouldRespond({
runtime: this.runtime,
context: shouldRespondContext,
modelClass: ModelClass.SMALL,
});
- if (!shouldRespond) {
- console.log("Not responding to message");
- return { text: "", action: "IGNORE" };
+ if (
+ shouldRespondResponse === "IGNORE" ||
+ shouldRespondResponse === "STOP"
+ ) {
+ elizaLogger.info(
+ `Not responding to cast because generated ShouldRespond was ${shouldRespondResponse}`
+ );
+ return;
}
const context = composeContext({
@@ -196,47 +221,65 @@ export class FarcasterInteractionManager {
messageHandlerTemplate,
});
- const response = await generateMessageResponse({
+ const responseContent = await generateMessageResponse({
runtime: this.runtime,
context,
modelClass: ModelClass.LARGE,
});
- response.inReplyTo = memoryId;
-
- if (!response.text) return;
+ responseContent.inReplyTo = memoryId;
- try {
- console.log(`Replying to cast ${cast.hash}.`);
+ if (!responseContent.text) return;
- const results = await sendCast({
- runtime: this.runtime,
- client: this.client,
- signerUuid: this.signerUuid,
- profile: cast.profile,
- content: response,
- roomId: memory.roomId,
- inReplyTo: {
- fid: cast.authorFid,
- hash: cast.hash,
- },
- });
-
- const newState = await this.runtime.updateRecentMessageState(state);
+ if (this.runtime.getSetting("FARCASTER_DRY_RUN") === "true") {
+ elizaLogger.info(
+ `Dry run: would have responded to cast ${cast.hash} with ${responseContent.text}`
+ );
+ return;
+ }
- for (const { memory } of results) {
- await this.runtime.messageManager.createMemory(memory);
+ const callback: HandlerCallback = async (
+ content: Content,
+ files: any[]
+ ) => {
+ try {
+ if (memoryId && !content.inReplyTo) {
+ content.inReplyTo = memoryId;
+ }
+ const results = await sendCast({
+ runtime: this.runtime,
+ client: this.client,
+ signerUuid: this.signerUuid,
+ profile: cast.profile,
+ content: content,
+ roomId: memory.roomId,
+ inReplyTo: {
+ fid: cast.authorFid,
+ hash: cast.hash,
+ },
+ });
+ // sendCast lost response action, so we need to add it back here
+ results[0].memory.content.action = content.action;
+
+ for (const { memory } of results) {
+ await this.runtime.messageManager.createMemory(memory);
+ }
+ return results.map((result) => result.memory);
+ } catch (error) {
+ console.error("Error sending response cast:", error);
+ return [];
}
+ };
- await this.runtime.evaluate(memory, newState);
+ const responseMessages = await callback(responseContent);
- await this.runtime.processActions(
- memory,
- results.map((result) => result.memory),
- newState
- );
- } catch (error) {
- console.error(`Error sending response cast: ${error}`);
- }
+ const newState = await this.runtime.updateRecentMessageState(state);
+
+ await this.runtime.processActions(
+ memory,
+ responseMessages,
+ newState,
+ callback
+ );
}
}
diff --git a/packages/client-farcaster/src/post.ts b/packages/client-farcaster/src/post.ts
index 13b135b529..3cd06605ec 100644
--- a/packages/client-farcaster/src/post.ts
+++ b/packages/client-farcaster/src/post.ts
@@ -48,8 +48,6 @@ export class FarcasterPostManager {
elizaLogger.info("Generating new cast");
try {
const fid = Number(this.runtime.getSetting("FARCASTER_FID")!);
- // const farcasterUserName =
- // this.runtime.getSetting("FARCASTER_USERNAME")!;
const profile = await this.client.getProfile(fid);
await this.runtime.ensureUserExists(
@@ -86,7 +84,7 @@ export class FarcasterPostManager {
}
);
- // Generate new tweet
+ // Generate new cast
const context = composeContext({
state,
template:
@@ -105,6 +103,7 @@ export class FarcasterPostManager {
const contentLength = 240;
let content = slice.slice(0, contentLength);
+
// if its bigger than 280, delete the last line
if (content.length > 280) {
content = content.slice(0, content.lastIndexOf("\n"));
@@ -120,12 +119,18 @@ export class FarcasterPostManager {
content = content.slice(0, content.lastIndexOf("."));
}
+
+ if (this.runtime.getSetting("FARCASTER_DRY_RUN") === "true") {
+ elizaLogger.info(
+ `Dry run: would have cast: ${content}`
+ );
+ return;
+ }
+
try {
- // TODO: handle all the casts?
const [{ cast }] = await sendCast({
client: this.client,
runtime: this.runtime,
- //: this.signer,
signerUuid: this.signerUuid,
roomId: generateRoomId,
content: { text: content },
@@ -144,10 +149,7 @@ export class FarcasterPostManager {
roomId
);
- console.log(
- `%c [Farcaster Neynar Client] Published cast ${cast.hash}`,
- "color: #8565cb;"
- );
+ elizaLogger.info(`[Farcaster Neynar Client] Published cast ${cast.hash}`);
await this.runtime.messageManager.createMemory(
createCastMemory({
diff --git a/packages/client-farcaster/src/prompts.ts b/packages/client-farcaster/src/prompts.ts
index b9e0326d72..fde67d5370 100644
--- a/packages/client-farcaster/src/prompts.ts
+++ b/packages/client-farcaster/src/prompts.ts
@@ -36,7 +36,7 @@ About {{agentName}} (@{{farcasterUsername}}):
{{characterPostExamples}}`;
export const postTemplate =
- headerTemplate +
+headerTemplate +
`
# Task: Generate a post in the voice and style of {{agentName}}, aka @{{farcasterUsername}}
Write a single sentence post that is {{adjective}} about {{topic}} (without mentioning {{topic}} directly), from the perspective of {{agentName}}.
@@ -53,13 +53,17 @@ Recent interactions between {{agentName}} and other users:
Thread of casts You Are Replying To:
{{formattedConversation}}
-# Task: Generate a post in the voice, style and perspective of {{agentName}} (@{{twitterUserName}}):
+# Task: Generate a post in the voice, style and perspective of {{agentName}} (@{{farcasterUsername}}):
{{currentPost}}` +
messageCompletionFooter;
export const shouldRespondTemplate =
//
- `# INSTRUCTIONS: Determine if {{agentName}} (@{{twitterUserName}}) should respond to the message and participate in the conversation. Do not comment. Just respond with "true" or "false".
+ `# Task: Decide if {{agentName}} should respond.
+ About {{agentName}}:
+ {{bio}}
+
+ # INSTRUCTIONS: Determine if {{agentName}} (@{{farcasterUsername}}) should respond to the message and participate in the conversation. Do not comment. Just respond with "RESPOND" or "IGNORE" or "STOP".
Response options are RESPOND, IGNORE and STOP.
@@ -68,15 +72,17 @@ Response options are RESPOND, IGNORE and STOP.
{{agentName}} is in a room with other users and wants to be conversational, but not annoying.
{{agentName}} should RESPOND to messages that are directed at them, or participate in conversations that are interesting or relevant to their background.
If a message is not interesting or relevant, {{agentName}} should IGNORE.
+If a message thread has become repetitive, {{agentName}} should IGNORE.
Unless directly RESPONDing to a user, {{agentName}} should IGNORE messages that are very short or do not contain much information.
If a user asks {{agentName}} to stop talking, {{agentName}} should STOP.
If {{agentName}} concludes a conversation and isn't part of the conversation anymore, {{agentName}} should STOP.
-{{recentPosts}}
+IMPORTANT: {{agentName}} (aka @{{farcasterUsername}}) is particularly sensitive about being annoying, so if there is any doubt, it is better to IGNORE than to RESPOND.
-IMPORTANT: {{agentName}} (aka @{{twitterUserName}}) is particularly sensitive about being annoying, so if there is any doubt, it is better to IGNORE than to RESPOND.
+Thread of messages You Are Replying To:
+{{formattedConversation}}
+Current message:
{{currentPost}}
-# INSTRUCTIONS: Respond with [RESPOND] if {{agentName}} should respond, or [IGNORE] if {{agentName}} should not respond to the last message and [STOP] if {{agentName}} should stop participating in the conversation.
` + shouldRespondFooter;
diff --git a/packages/client-linkedin/README.md b/packages/client-linkedin/README.md
new file mode 100644
index 0000000000..ec43d8c5cc
--- /dev/null
+++ b/packages/client-linkedin/README.md
@@ -0,0 +1,55 @@
+# @ai16z/client-linkedin
+
+LinkedIn client integration for AI16Z agents. This package provides functionality for AI agents to interact with LinkedIn, including:
+
+- Automated post creation and scheduling
+- Professional interaction management
+- Message and comment handling
+- Connection management
+- Activity tracking
+
+## Installation
+
+```bash
+pnpm add @ai16z/client-linkedin
+```
+
+## Configuration
+
+Set the following environment variables:
+
+```env
+LINKEDIN_USERNAME=your.email@example.com
+LINKEDIN_PASSWORD=your_password
+LINKEDIN_DRY_RUN=false
+POST_INTERVAL_MIN=24
+POST_INTERVAL_MAX=72
+```
+
+## Usage
+
+```typescript
+import { LinkedInClientInterface } from '@ai16z/client-linkedin';
+
+// Initialize the client
+const manager = await LinkedInClientInterface.start(runtime);
+
+// The client will automatically:
+// - Generate and schedule posts
+// - Respond to messages and comments
+// - Manage connections
+// - Track activities
+```
+
+## Features
+
+- Professional content generation
+- Rate-limited API interactions
+- Conversation history tracking
+- Connection management
+- Activity monitoring
+- Cache management
+
+## License
+
+MIT
diff --git a/packages/client-linkedin/package.json b/packages/client-linkedin/package.json
new file mode 100644
index 0000000000..eb120b215e
--- /dev/null
+++ b/packages/client-linkedin/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "@ai16z/client-linkedin",
+ "version": "0.1.0-alpha.1",
+ "description": "LinkedIn client integration for AI16Z agents",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "jest"
+ },
+ "dependencies": {
+ "@ai16z/eliza": "workspace:*",
+ "linkedin-api": "0.0.1",
+ "zod": "^3.22.4"
+ },
+ "devDependencies": {
+ "@types/node": "^20.0.0",
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
+ "@typescript-eslint/parser": "^6.0.0",
+ "eslint": "^8.0.0",
+ "jest": "^29.0.0",
+ "typescript": "^5.0.0"
+ },
+ "peerDependencies": {
+ "@ai16z/eliza": "workspace:*"
+ }
+}
diff --git a/packages/client-linkedin/src/base.ts b/packages/client-linkedin/src/base.ts
new file mode 100644
index 0000000000..cbf2f5e162
--- /dev/null
+++ b/packages/client-linkedin/src/base.ts
@@ -0,0 +1,205 @@
+import { EventEmitter } from "events";
+// @ts-ignore
+import { Client as LinkedInClient } from "linkedin-api";
+import { elizaLogger } from "@ai16z/eliza";
+import { stringToUuid, getEmbeddingZeroVector } from "@ai16z/eliza";
+
+class RequestQueue {
+ private queue: (() => Promise)[] = [];
+ private processing = false;
+
+ async add(request: () => Promise): Promise {
+ return new Promise((resolve, reject) => {
+ this.queue.push(async () => {
+ try {
+ const result = await request();
+ resolve(result);
+ } catch (error) {
+ reject(error);
+ }
+ });
+ this.processQueue();
+ });
+ }
+
+ private async processQueue() {
+ if (this.processing || this.queue.length === 0) {
+ return;
+ }
+
+ this.processing = true;
+ while (this.queue.length > 0) {
+ const request = this.queue.shift();
+ if (!request) continue;
+ try {
+ await request();
+ } catch (error) {
+ console.error("Error processing request:", error);
+ this.queue.unshift(request);
+ await this.exponentialBackoff(this.queue.length);
+ }
+ await this.randomDelay();
+ }
+ this.processing = false;
+ }
+
+ private async exponentialBackoff(retryCount: number) {
+ const delay = Math.pow(2, retryCount) * 1000;
+ await new Promise((resolve) => setTimeout(resolve, delay));
+ }
+
+ private async randomDelay() {
+ const delay = Math.floor(Math.random() * 2000) + 1500;
+ await new Promise((resolve) => setTimeout(resolve, delay));
+ }
+}
+
+export class ClientBase extends EventEmitter {
+ private static _linkedInClient: LinkedInClient;
+ protected linkedInClient: LinkedInClient;
+ protected runtime: any;
+ protected profile: any;
+ protected requestQueue: RequestQueue = new RequestQueue();
+
+ constructor(runtime: any) {
+ super();
+ this.runtime = runtime;
+
+ if (ClientBase._linkedInClient) {
+ this.linkedInClient = ClientBase._linkedInClient;
+ } else {
+ this.linkedInClient = new LinkedInClient();
+ ClientBase._linkedInClient = this.linkedInClient;
+ }
+ }
+
+ async init() {
+ const username = this.runtime.getSetting("LINKEDIN_USERNAME");
+ const password = this.runtime.getSetting("LINKEDIN_PASSWORD");
+
+ if (!username || !password) {
+ throw new Error("LinkedIn credentials not configured");
+ }
+
+ elizaLogger.log("Logging into LinkedIn...");
+
+ try {
+ await this.linkedInClient.login(username, password);
+ this.profile = await this.fetchProfile();
+
+ if (this.profile) {
+ elizaLogger.log(
+ "LinkedIn profile loaded:",
+ JSON.stringify(this.profile, null, 2)
+ );
+ this.runtime.character.linkedInProfile = {
+ id: this.profile.id,
+ username: this.profile.username,
+ fullName: this.profile.fullName,
+ headline: this.profile.headline,
+ summary: this.profile.summary,
+ };
+ } else {
+ throw new Error("Failed to load LinkedIn profile");
+ }
+
+ await this.loadInitialState();
+ } catch (error) {
+ elizaLogger.error("LinkedIn login failed:", error);
+ throw error;
+ }
+ }
+
+ async fetchProfile() {
+ const cachedProfile = await this.getCachedProfile();
+ if (cachedProfile) return cachedProfile;
+
+ try {
+ const profile = await this.requestQueue.add(async () => {
+ const profileData = await this.linkedInClient.getProfile();
+ return {
+ id: profileData.id,
+ username: profileData.username,
+ fullName:
+ profileData.firstName + " " + profileData.lastName,
+ headline: profileData.headline,
+ summary: profileData.summary,
+ };
+ });
+
+ await this.cacheProfile(profile);
+ return profile;
+ } catch (error) {
+ console.error("Error fetching LinkedIn profile:", error);
+ return undefined;
+ }
+ }
+
+ async loadInitialState() {
+ await this.populateConnections();
+ await this.populateRecentActivity();
+ }
+
+ async populateConnections() {
+ const connections = await this.requestQueue.add(async () => {
+ return await this.linkedInClient.getConnections();
+ });
+
+ for (const connection of connections) {
+ const roomId = stringToUuid(`linkedin-connection-${connection.id}`);
+ await this.runtime.ensureConnection(
+ stringToUuid(connection.id),
+ roomId,
+ connection.username,
+ connection.fullName,
+ "linkedin"
+ );
+ }
+ }
+
+ async populateRecentActivity() {
+ const activities = await this.requestQueue.add(async () => {
+ return await this.linkedInClient.getFeedPosts();
+ });
+
+ for (const activity of activities) {
+ const roomId = stringToUuid(`linkedin-post-${activity.id}`);
+ await this.saveActivity(activity, roomId);
+ }
+ }
+
+ private async saveActivity(activity: any, roomId: string) {
+ const content = {
+ text: activity.text,
+ url: activity.url,
+ source: "linkedin",
+ type: activity.type,
+ };
+
+ await this.runtime.messageManager.createMemory({
+ id: stringToUuid(`${activity.id}-${this.runtime.agentId}`),
+ userId:
+ activity.authorId === this.profile.id
+ ? this.runtime.agentId
+ : stringToUuid(activity.authorId),
+ content,
+ agentId: this.runtime.agentId,
+ roomId,
+ embedding: getEmbeddingZeroVector(),
+ createdAt: activity.timestamp,
+ });
+ }
+
+ private async getCachedProfile() {
+ return await this.runtime.cacheManager.get(
+ `linkedin/${this.runtime.getSetting("LINKEDIN_USERNAME")}/profile`
+ );
+ }
+
+ private async cacheProfile(profile: any) {
+ await this.runtime.cacheManager.set(
+ `linkedin/${profile.username}/profile`,
+ profile
+ );
+ }
+}
diff --git a/packages/client-linkedin/src/environment.ts b/packages/client-linkedin/src/environment.ts
new file mode 100644
index 0000000000..8386953b80
--- /dev/null
+++ b/packages/client-linkedin/src/environment.ts
@@ -0,0 +1,33 @@
+import { z } from 'zod';
+
+export const linkedInEnvSchema = z.object({
+ LINKEDIN_USERNAME: z.string().min(1, 'LinkedIn username is required'),
+ LINKEDIN_PASSWORD: z.string().min(1, 'LinkedIn password is required'),
+ LINKEDIN_DRY_RUN: z.string().transform(val => val.toLowerCase() === 'true'),
+ POST_INTERVAL_MIN: z.string().optional(),
+ POST_INTERVAL_MAX: z.string().optional()
+});
+
+export async function validateLinkedInConfig(runtime: any) {
+ try {
+ const config = {
+ LINKEDIN_USERNAME: runtime.getSetting('LINKEDIN_USERNAME') || process.env.LINKEDIN_USERNAME,
+ LINKEDIN_PASSWORD: runtime.getSetting('LINKEDIN_PASSWORD') || process.env.LINKEDIN_PASSWORD,
+ LINKEDIN_DRY_RUN: runtime.getSetting('LINKEDIN_DRY_RUN') || process.env.LINKEDIN_DRY_RUN,
+ POST_INTERVAL_MIN: runtime.getSetting('POST_INTERVAL_MIN') || process.env.POST_INTERVAL_MIN,
+ POST_INTERVAL_MAX: runtime.getSetting('POST_INTERVAL_MAX') || process.env.POST_INTERVAL_MAX
+ };
+
+ return linkedInEnvSchema.parse(config);
+ } catch (error) {
+ if (error instanceof z.ZodError) {
+ const errorMessages = error.errors
+ .map(err => `${err.path.join('.')}: ${err.message}`)
+ .join('\n');
+ throw new Error(
+ `LinkedIn configuration validation failed:\n${errorMessages}`
+ );
+ }
+ throw error;
+ }
+}
\ No newline at end of file
diff --git a/packages/client-linkedin/src/index.ts b/packages/client-linkedin/src/index.ts
new file mode 100644
index 0000000000..6e1fe400de
--- /dev/null
+++ b/packages/client-linkedin/src/index.ts
@@ -0,0 +1,37 @@
+import { elizaLogger } from '@ai16z/eliza';
+import { ClientBase } from './base';
+import { LinkedInPostClient } from './post';
+import { LinkedInInteractionClient } from './interactions';
+import { validateLinkedInConfig } from './environment';
+
+class LinkedInManager {
+ client: ClientBase;
+ post: LinkedInPostClient;
+ interaction: LinkedInInteractionClient;
+
+ constructor(runtime: any) {
+ this.client = new ClientBase(runtime);
+ this.post = new LinkedInPostClient(this.client, runtime);
+ this.interaction = new LinkedInInteractionClient(this.client, runtime);
+ }
+}
+
+export const LinkedInClientInterface = {
+ async start(runtime: any) {
+ await validateLinkedInConfig(runtime);
+ elizaLogger.log('LinkedIn client started');
+
+ const manager = new LinkedInManager(runtime);
+ await manager.client.init();
+ await manager.post.start();
+ await manager.interaction.start();
+
+ return manager;
+ },
+
+ async stop(runtime: any) {
+ elizaLogger.warn('LinkedIn client stop not implemented yet');
+ }
+};
+
+export default LinkedInClientInterface;
\ No newline at end of file
diff --git a/packages/client-linkedin/src/interactions.ts b/packages/client-linkedin/src/interactions.ts
new file mode 100644
index 0000000000..1c6b6654bd
--- /dev/null
+++ b/packages/client-linkedin/src/interactions.ts
@@ -0,0 +1,264 @@
+import {
+ composeContext,
+ generateMessageResponse,
+ generateShouldRespond,
+ messageCompletionFooter,
+ shouldRespondFooter,
+ ModelClass,
+ stringToUuid,
+ elizaLogger
+} from '@ai16z/eliza';
+
+const linkedInMessageTemplate = `{{timeline}}
+
+# Knowledge
+{{knowledge}}
+
+About {{agentName}} (LinkedIn Profile):
+{{bio}}
+{{headline}}
+{{summary}}
+{{postDirections}}
+
+{{providers}}
+
+Recent interactions:
+{{recentInteractions}}
+
+# Task: Generate a professional response in the voice and style of {{agentName}}
+Current Message:
+{{currentMessage}}
+
+Conversation History:
+{{conversationHistory}}
+
+{{actions}}
+
+# Task: Generate a response in the voice and style of {{agentName}}. Include an action, if appropriate. {{actionNames}}:` + messageCompletionFooter;
+
+const linkedInShouldRespondTemplate = `# INSTRUCTIONS: Determine if {{agentName}} should respond to the message and participate in the conversation.
+
+Response options are RESPOND, IGNORE and STOP.
+
+{{agentName}} should:
+- RESPOND to messages that are directly addressed to them
+- RESPOND to professional networking opportunities
+- RESPOND to industry-relevant discussions
+- IGNORE messages that are irrelevant to their professional focus
+- IGNORE spam or promotional content
+- STOP if the conversation is concluded
+- STOP if asked to stop
+
+Recent interactions:
+{{recentInteractions}}
+
+Current Message:
+{{currentMessage}}
+
+Conversation History:
+{{conversationHistory}}
+
+# INSTRUCTIONS: Respond with [RESPOND] if {{agentName}} should respond, [IGNORE] if {{agentName}} should not respond, or [STOP] if {{agentName}} should end the conversation.` + shouldRespondFooter;
+
+export class LinkedInInteractionClient {
+ private client: any;
+ private runtime: any;
+
+ constructor(client: any, runtime: any) {
+ this.client = client;
+ this.runtime = runtime;
+ }
+
+ async start() {
+ const handleLinkedInInteractionsLoop = () => {
+ this.handleLinkedInInteractions();
+ setTimeout(
+ handleLinkedInInteractionsLoop,
+ (Math.floor(Math.random() * (15 - 5 + 1)) + 5) * 60 * 1000
+ );
+ };
+
+ handleLinkedInInteractionsLoop();
+ }
+
+ async handleLinkedInInteractions() {
+ elizaLogger.log('Checking LinkedIn interactions');
+
+ try {
+ // Check messages
+ const messages = await this.client.linkedInClient.getMessages();
+ for (const message of messages) {
+ await this.handleMessage(message);
+ }
+
+ // Check post comments
+ const posts = await this.client.linkedInClient.getFeedPosts();
+ for (const post of posts) {
+ if (post.authorId === this.client.profile.id) {
+ const comments = await this.client.linkedInClient.getPostComments(post.id);
+ for (const comment of comments) {
+ await this.handleComment(comment, post);
+ }
+ }
+ }
+ } catch (error) {
+ elizaLogger.error('Error handling LinkedIn interactions:', error);
+ }
+ }
+
+ private async handleMessage(message: any) {
+ if (message.senderId === this.client.profile.id) {
+ return;
+ }
+
+ const roomId = stringToUuid(`linkedin-conversation-${message.conversationId}`);
+ const state = await this.runtime.composeState(
+ {
+ userId: stringToUuid(message.senderId),
+ roomId,
+ agentId: this.runtime.agentId,
+ content: {
+ text: message.text,
+ action: ''
+ }
+ },
+ {
+ currentMessage: message.text,
+ conversationHistory: await this.getConversationHistory(message.conversationId)
+ }
+ );
+
+ const shouldRespondContext = composeContext({
+ state,
+ template: this.runtime.character.templates?.linkedInShouldRespondTemplate || linkedInShouldRespondTemplate
+ });
+
+ const shouldRespond = await generateShouldRespond({
+ runtime: this.runtime,
+ context: shouldRespondContext,
+ modelClass: ModelClass.MEDIUM
+ });
+
+ if (shouldRespond !== 'RESPOND') {
+ elizaLogger.log('Not responding to message');
+ return;
+ }
+
+ const responseContext = composeContext({
+ state,
+ template: this.runtime.character.templates?.linkedInMessageTemplate || linkedInMessageTemplate
+ });
+
+ const response = await generateMessageResponse({
+ runtime: this.runtime,
+ context: responseContext,
+ modelClass: ModelClass.MEDIUM
+ });
+
+ if (response.text) {
+ try {
+ await this.client.linkedInClient.sendMessage(message.conversationId, response.text);
+
+ await this.runtime.messageManager.createMemory({
+ id: stringToUuid(`${Date.now()}-${this.runtime.agentId}`),
+ userId: this.runtime.agentId,
+ content: {
+ text: response.text,
+ source: 'linkedin',
+ action: response.action
+ },
+ agentId: this.runtime.agentId,
+ roomId,
+ createdAt: Date.now()
+ });
+ } catch (error) {
+ elizaLogger.error('Error sending LinkedIn message:', error);
+ }
+ }
+ }
+
+ private async handleComment(comment: any, post: any) {
+ if (comment.authorId === this.client.profile.id) {
+ return;
+ }
+
+ const roomId = stringToUuid(`linkedin-post-${post.id}`);
+ const state = await this.runtime.composeState(
+ {
+ userId: stringToUuid(comment.authorId),
+ roomId,
+ agentId: this.runtime.agentId,
+ content: {
+ text: comment.text,
+ action: ''
+ }
+ },
+ {
+ currentMessage: comment.text,
+ conversationHistory: await this.getPostCommentHistory(post.id)
+ }
+ );
+
+ const shouldRespondContext = composeContext({
+ state,
+ template: this.runtime.character.templates?.linkedInShouldRespondTemplate || linkedInShouldRespondTemplate
+ });
+
+ const shouldRespond = await generateShouldRespond({
+ runtime: this.runtime,
+ context: shouldRespondContext,
+ modelClass: ModelClass.MEDIUM
+ });
+
+ if (shouldRespond !== 'RESPOND') {
+ elizaLogger.log('Not responding to comment');
+ return;
+ }
+
+ const responseContext = composeContext({
+ state,
+ template: this.runtime.character.templates?.linkedInMessageTemplate || linkedInMessageTemplate
+ });
+
+ const response = await generateMessageResponse({
+ runtime: this.runtime,
+ context: responseContext,
+ modelClass: ModelClass.MEDIUM
+ });
+
+ if (response.text) {
+ try {
+ await this.client.linkedInClient.replyToComment(post.id, comment.id, response.text);
+
+ await this.runtime.messageManager.createMemory({
+ id: stringToUuid(`${Date.now()}-${this.runtime.agentId}`),
+ userId: this.runtime.agentId,
+ content: {
+ text: response.text,
+ source: 'linkedin',
+ action: response.action
+ },
+ agentId: this.runtime.agentId,
+ roomId,
+ createdAt: Date.now()
+ });
+ } catch (error) {
+ elizaLogger.error('Error replying to LinkedIn comment:', error);
+ }
+ }
+ }
+
+ private async getConversationHistory(conversationId: string): Promise {
+ const messages = await this.client.linkedInClient.getConversationMessages(conversationId);
+ return messages.map((msg: any) =>
+ `${msg.senderName} (${new Date(msg.timestamp).toLocaleString()}): ${msg.text}`
+ ).join('\n\n');
+ }
+
+ private async getPostCommentHistory(postId: string): Promise {
+ const comments = await this.client.linkedInClient.getPostComments(postId);
+ return comments.map((comment: any) =>
+ `${comment.authorName} (${new Date(comment.timestamp).toLocaleString()}): ${comment.text}`
+ ).join('\n\n');
+ }
+}
\ No newline at end of file
diff --git a/packages/client-linkedin/src/post.ts b/packages/client-linkedin/src/post.ts
new file mode 100644
index 0000000000..f3159faadc
--- /dev/null
+++ b/packages/client-linkedin/src/post.ts
@@ -0,0 +1,161 @@
+import {
+ composeContext,
+ generateText,
+ ModelClass,
+ stringToUuid,
+ elizaLogger
+} from '@ai16z/eliza';
+
+const linkedInPostTemplate = `{{timeline}}
+
+# Knowledge
+{{knowledge}}
+
+About {{agentName}} (LinkedIn Profile):
+{{bio}}
+{{headline}}
+{{summary}}
+{{postDirections}}
+
+{{providers}}
+
+{{recentPosts}}
+
+{{characterPostExamples}}
+
+# Task: Generate a professional LinkedIn post in the voice and style of {{agentName}}
+Write a post that is {{adjective}} about {{topic}}, from the perspective of {{agentName}}.
+The post should be professional and industry-relevant.
+Do not add commentary or acknowledge this request, just write the post.
+Keep the tone professional but engaging.`;
+
+export class LinkedInPostClient {
+ private client: any;
+ private runtime: any;
+
+ constructor(client: any, runtime: any) {
+ this.client = client;
+ this.runtime = runtime;
+ }
+
+ async start(postImmediately = false) {
+ if (!this.client.profile) {
+ await this.client.init();
+ }
+
+ const generateNewPostLoop = async () => {
+ const lastPost = await this.runtime.cacheManager.get(
+ `linkedin/${this.runtime.getSetting('LINKEDIN_USERNAME')}/lastPost`
+ );
+ const lastPostTimestamp = lastPost?.timestamp ?? 0;
+ const minHours = parseInt(this.runtime.getSetting('POST_INTERVAL_MIN')) || 24;
+ const maxHours = parseInt(this.runtime.getSetting('POST_INTERVAL_MAX')) || 72;
+ const randomHours = Math.floor(Math.random() * (maxHours - minHours + 1)) + minHours;
+ const delay = randomHours * 60 * 60 * 1000;
+
+ if (Date.now() > lastPostTimestamp + delay) {
+ await this.generateNewPost();
+ }
+
+ setTimeout(() => {
+ generateNewPostLoop();
+ }, delay);
+
+ elizaLogger.log(`Next LinkedIn post scheduled in ${randomHours} hours`);
+ };
+
+ if (postImmediately) {
+ await this.generateNewPost();
+ }
+
+ generateNewPostLoop();
+ }
+
+ async generateNewPost() {
+ elizaLogger.log('Generating new LinkedIn post');
+
+ try {
+ const recentPosts = await this.client.linkedInClient.getFeedPosts();
+ const formattedPosts = recentPosts.map((post: any) => {
+ return `Post ID: ${post.id}
+Author: ${post.author.name}
+Date: ${new Date(post.timestamp).toDateString()}
+
+${post.text}
+---`;
+ }).join('\n\n');
+
+ const topics = this.runtime.character.topics.join(', ');
+ const state = await this.runtime.composeState(
+ {
+ userId: this.runtime.agentId,
+ roomId: stringToUuid('linkedin_generate_room'),
+ agentId: this.runtime.agentId,
+ content: {
+ text: topics,
+ action: ''
+ }
+ },
+ {
+ timeline: formattedPosts,
+ headline: this.client.profile.headline,
+ summary: this.client.profile.summary
+ }
+ );
+
+ const context = composeContext({
+ state,
+ template: this.runtime.character.templates?.linkedInPostTemplate || linkedInPostTemplate
+ });
+
+ elizaLogger.debug('Generate post prompt:\n' + context);
+
+ const newPostContent = await generateText({
+ runtime: this.runtime,
+ context,
+ modelClass: ModelClass.SMALL
+ });
+
+ if (this.runtime.getSetting('LINKEDIN_DRY_RUN') === 'true') {
+ elizaLogger.info(`Dry run: would have posted: ${newPostContent}`);
+ return;
+ }
+
+ try {
+ elizaLogger.log(`Posting new LinkedIn post:\n${newPostContent}`);
+
+ const result = await this.client.requestQueue.add(
+ async () => await this.client.linkedInClient.createPost(newPostContent)
+ );
+
+ await this.runtime.cacheManager.set(
+ `linkedin/${this.client.profile.username}/lastPost`,
+ {
+ id: result.id,
+ timestamp: Date.now()
+ }
+ );
+
+ const roomId = stringToUuid(`linkedin-post-${result.id}`);
+ await this.runtime.messageManager.createMemory({
+ id: stringToUuid(`${result.id}-${this.runtime.agentId}`),
+ userId: this.runtime.agentId,
+ content: {
+ text: newPostContent,
+ url: result.url,
+ source: 'linkedin'
+ },
+ agentId: this.runtime.agentId,
+ roomId,
+ createdAt: Date.now()
+ });
+
+ elizaLogger.log(`LinkedIn post created: ${result.url}`);
+ } catch (error) {
+ elizaLogger.error('Error creating LinkedIn post:', error);
+ }
+ } catch (error) {
+ elizaLogger.error('Error generating new LinkedIn post:', error);
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/client-linkedin/tsconfig.json b/packages/client-linkedin/tsconfig.json
new file mode 100644
index 0000000000..22414fda08
--- /dev/null
+++ b/packages/client-linkedin/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "CommonJS",
+ "lib": [
+ "ES2020"
+ ],
+ "declaration": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "moduleResolution": "node"
+ },
+ "include": [
+ "src"
+ ],
+ "exclude": [
+ "node_modules",
+ "dist",
+ "test"
+ ]
+}
\ No newline at end of file
diff --git a/packages/client-telegram/src/messageManager.ts b/packages/client-telegram/src/messageManager.ts
index 8eb9154f24..6c400d514e 100644
--- a/packages/client-telegram/src/messageManager.ts
+++ b/packages/client-telegram/src/messageManager.ts
@@ -460,7 +460,7 @@ export class MessageManager {
this.runtime.agentId
),
agentId,
- userId,
+ userId: agentId,
roomId,
content: {
...content,
diff --git a/packages/client-twitter/src/environment.ts b/packages/client-twitter/src/environment.ts
index 52af051fe8..8b22d0dc59 100644
--- a/packages/client-twitter/src/environment.ts
+++ b/packages/client-twitter/src/environment.ts
@@ -1,6 +1,8 @@
import { IAgentRuntime } from "@ai16z/eliza";
import { z } from "zod";
+export const DEFAULT_MAX_TWEET_LENGTH = 280;
+
export const twitterEnvSchema = z.object({
TWITTER_DRY_RUN: z
.string()
@@ -9,6 +11,10 @@ export const twitterEnvSchema = z.object({
TWITTER_PASSWORD: z.string().min(1, "Twitter password is required"),
TWITTER_EMAIL: z.string().email("Valid Twitter email is required"),
TWITTER_COOKIES: z.string().optional(),
+ MAX_TWEET_LENGTH: z
+ .string()
+ .pipe(z.coerce.number().min(0).int())
+ .default(DEFAULT_MAX_TWEET_LENGTH.toString()),
});
export type TwitterConfig = z.infer;
@@ -34,6 +40,10 @@ export async function validateTwitterConfig(
TWITTER_COOKIES:
runtime.getSetting("TWITTER_COOKIES") ||
process.env.TWITTER_COOKIES,
+ MAX_TWEET_LENGTH:
+ runtime.getSetting("MAX_TWEET_LENGTH") ||
+ process.env.MAX_TWEET_LENGTH ||
+ DEFAULT_MAX_TWEET_LENGTH.toString(),
};
return twitterEnvSchema.parse(config);
diff --git a/packages/client-twitter/src/index.ts b/packages/client-twitter/src/index.ts
index c15e994c7e..553d96822c 100644
--- a/packages/client-twitter/src/index.ts
+++ b/packages/client-twitter/src/index.ts
@@ -13,7 +13,7 @@ class TwitterManager {
constructor(runtime: IAgentRuntime) {
this.client = new ClientBase(runtime);
this.post = new TwitterPostClient(this.client, runtime);
- // this.search = new TwitterSearchClient(runtime); // don't start the search client by default
+ //this.search = new TwitterSearchClient(this.client, runtime); // don't start the search client by default
// this searches topics from character file, but kind of violates consent of random users
// burns your rate limit and can get your account banned
// use at your own risk
@@ -35,6 +35,8 @@ export const TwitterClientInterface: Client = {
await manager.interaction.start();
+ //await manager.search.start(); // don't run the search by default
+
return manager;
},
async stop(_runtime: IAgentRuntime) {
diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts
index c25d74bd33..3c7ff1d08e 100644
--- a/packages/client-twitter/src/post.ts
+++ b/packages/client-twitter/src/post.ts
@@ -28,22 +28,23 @@ const twitterPostTemplate = `
# Task: Generate a post in the voice and style and perspective of {{agentName}} @{{twitterUserName}}.
Write a 1-3 sentence post that is {{adjective}} about {{topic}} (without mentioning {{topic}} directly), from the perspective of {{agentName}}. Do not add commentary or acknowledge this request, just write the post.
-Your response should not contain any questions. Brief, concise statements only. The total character count MUST be less than 280. No emojis. Use \\n\\n (double spaces) between statements.`;
-
-const MAX_TWEET_LENGTH = 280;
+Your response should not contain any questions. Brief, concise statements only. The total character count MUST be less than {{maxTweetLength}}. No emojis. Use \\n\\n (double spaces) between statements.`;
/**
* Truncate text to fit within the Twitter character limit, ensuring it ends at a complete sentence.
*/
-function truncateToCompleteSentence(text: string): string {
- if (text.length <= MAX_TWEET_LENGTH) {
+function truncateToCompleteSentence(
+ text: string,
+ maxTweetLength: number
+): string {
+ if (text.length <= maxTweetLength) {
return text;
}
// Attempt to truncate at the last period within the limit
const truncatedAtPeriod = text.slice(
0,
- text.lastIndexOf(".", MAX_TWEET_LENGTH) + 1
+ text.lastIndexOf(".", maxTweetLength) + 1
);
if (truncatedAtPeriod.trim().length > 0) {
return truncatedAtPeriod.trim();
@@ -52,14 +53,14 @@ function truncateToCompleteSentence(text: string): string {
// If no period is found, truncate to the nearest whitespace
const truncatedAtSpace = text.slice(
0,
- text.lastIndexOf(" ", MAX_TWEET_LENGTH)
+ text.lastIndexOf(" ", maxTweetLength)
);
if (truncatedAtSpace.trim().length > 0) {
return truncatedAtSpace.trim() + "...";
}
// Fallback: Hard truncate and add ellipsis
- return text.slice(0, MAX_TWEET_LENGTH - 3).trim() + "...";
+ return text.slice(0, maxTweetLength - 3).trim() + "...";
}
export class TwitterPostClient {
@@ -147,6 +148,7 @@ export class TwitterPostClient {
},
{
twitterUserName: this.client.profile.username,
+ maxTweetLength: this.runtime.getSetting("MAX_TWEET_LENGTH"),
}
);
@@ -171,7 +173,10 @@ export class TwitterPostClient {
.trim();
// Use the helper function to truncate to complete sentence
- const content = truncateToCompleteSentence(formattedTweet);
+ const content = truncateToCompleteSentence(
+ formattedTweet,
+ Number(this.runtime.getSetting("MAX_TWEET_LENGTH"))
+ );
if (this.runtime.getSetting("TWITTER_DRY_RUN") === "true") {
elizaLogger.info(
diff --git a/packages/client-twitter/src/search.ts b/packages/client-twitter/src/search.ts
index b12d1d4bfb..8cfeef8dc7 100644
--- a/packages/client-twitter/src/search.ts
+++ b/packages/client-twitter/src/search.ts
@@ -42,17 +42,17 @@ Your response should not contain any questions. Brief, concise statements only.
` + messageCompletionFooter;
-export class TwitterSearchClient extends ClientBase {
+export class TwitterSearchClient {
+ client: ClientBase;
+ runtime: IAgentRuntime;
private respondedTweets: Set = new Set();
- constructor(runtime: IAgentRuntime) {
- // Initialize the client and pass an optional callback to be called when the client is ready
- super({
- runtime,
- });
+ constructor(client: ClientBase, runtime: IAgentRuntime) {
+ this.client = client;
+ this.runtime = runtime;
}
- async onReady() {
+ async start() {
this.engageWithSearchTermsLoop();
}
@@ -74,16 +74,16 @@ export class TwitterSearchClient extends ClientBase {
console.log("Fetching search tweets");
// TODO: we wait 5 seconds here to avoid getting rate limited on startup, but we should queue
await new Promise((resolve) => setTimeout(resolve, 5000));
- const recentTweets = await this.fetchSearchTweets(
+ const recentTweets = await this.client.fetchSearchTweets(
searchTerm,
20,
SearchMode.Top
);
console.log("Search tweets fetched");
- const homeTimeline = await this.fetchHomeTimeline(50);
+ const homeTimeline = await this.client.fetchHomeTimeline(50);
- await this.cacheTimeline(homeTimeline);
+ await this.client.cacheTimeline(homeTimeline);
const formattedHomeTimeline =
`# ${this.runtime.character.name}'s Home Timeline\n\n` +
@@ -179,7 +179,7 @@ export class TwitterSearchClient extends ClientBase {
);
// crawl additional conversation tweets, if there are any
- await buildConversationThread(selectedTweet, this);
+ await buildConversationThread(selectedTweet, this.client);
const message = {
id: stringToUuid(selectedTweet.id + "-" + this.runtime.agentId),
@@ -218,8 +218,8 @@ export class TwitterSearchClient extends ClientBase {
let tweetBackground = "";
if (selectedTweet.isRetweet) {
- const originalTweet = await this.requestQueue.add(() =>
- this.twitterClient.getTweet(selectedTweet.id)
+ const originalTweet = await this.client.requestQueue.add(() =>
+ this.client.twitterClient.getTweet(selectedTweet.id)
);
tweetBackground = `Retweeting @${originalTweet.username}: ${originalTweet.text}`;
}
@@ -231,13 +231,12 @@ export class TwitterSearchClient extends ClientBase {
.getService(
ServiceType.IMAGE_DESCRIPTION
)
- .getInstance()
.describeImage(photo.url);
imageDescriptions.push(description);
}
let state = await this.runtime.composeState(message, {
- twitterClient: this.twitterClient,
+ twitterClient: this.client.twitterClient,
twitterUserName: this.runtime.getSetting("TWITTER_USERNAME"),
timeline: formattedHomeTimeline,
tweetContext: `${tweetBackground}
@@ -250,7 +249,7 @@ export class TwitterSearchClient extends ClientBase {
`,
});
- await this.saveRequestMessage(message, state as State);
+ await this.client.saveRequestMessage(message, state as State);
const context = composeContext({
state,
@@ -280,7 +279,7 @@ export class TwitterSearchClient extends ClientBase {
try {
const callback: HandlerCallback = async (response: Content) => {
const memories = await sendTweet(
- this,
+ this.client,
response,
message.roomId,
this.runtime.getSetting("TWITTER_USERNAME"),
diff --git a/packages/client-twitter/src/utils.ts b/packages/client-twitter/src/utils.ts
index 469f26627b..c69bf53130 100644
--- a/packages/client-twitter/src/utils.ts
+++ b/packages/client-twitter/src/utils.ts
@@ -4,8 +4,7 @@ import { Content, Memory, UUID } from "@ai16z/eliza";
import { stringToUuid } from "@ai16z/eliza";
import { ClientBase } from "./base";
import { elizaLogger } from "@ai16z/eliza";
-
-const MAX_TWEET_LENGTH = 280; // Updated to Twitter's current character limit
+import { DEFAULT_MAX_TWEET_LENGTH } from "./environment";
export const wait = (minTime: number = 1000, maxTime: number = 3000) => {
const waitTime =
@@ -170,7 +169,11 @@ export async function sendTweet(
twitterUsername: string,
inReplyTo: string
): Promise {
- const tweetChunks = splitTweetContent(content.text);
+ const tweetChunks = splitTweetContent(
+ content.text,
+ Number(client.runtime.getSetting("MAX_TWEET_LENGTH")) ||
+ DEFAULT_MAX_TWEET_LENGTH
+ );
const sentTweets: Tweet[] = [];
let previousTweetId = inReplyTo;
@@ -236,8 +239,7 @@ export async function sendTweet(
return memories;
}
-function splitTweetContent(content: string): string[] {
- const maxLength = MAX_TWEET_LENGTH;
+function splitTweetContent(content: string, maxLength: number): string[] {
const paragraphs = content.split("\n\n").map((p) => p.trim());
const tweets: string[] = [];
let currentTweet = "";
diff --git a/packages/core/src/embedding.ts b/packages/core/src/embedding.ts
index 3116d57f56..49c1a4163c 100644
--- a/packages/core/src/embedding.ts
+++ b/packages/core/src/embedding.ts
@@ -22,7 +22,7 @@ export const getEmbeddingConfig = () => ({
: settings.USE_OLLAMA_EMBEDDING?.toLowerCase() === "true"
? 1024 // Ollama mxbai-embed-large
:settings.USE_GAIANET_EMBEDDING?.toLowerCase() === "true"
- ? 1536 // GaiaNet
+ ? 768 // GaiaNet
: 384, // BGE
model:
settings.USE_OPENAI_EMBEDDING?.toLowerCase() === "true"
@@ -196,7 +196,10 @@ export async function embed(runtime: IAgentRuntime, input: string) {
model: config.model,
endpoint:
runtime.character.modelEndpointOverride ||
- models[ModelProviderName.GAIANET].endpoint,
+ models[ModelProviderName.GAIANET].endpoint ||
+ settings.SMALL_GAIANET_SERVER_URL ||
+ settings.MEDIUM_GAIANET_SERVER_URL ||
+ settings.LARGE_GAIANET_SERVER_URL,
apiKey: settings.GAIANET_API_KEY || runtime.token,
dimensions: config.dimensions,
});
diff --git a/packages/core/src/generation.ts b/packages/core/src/generation.ts
index b3ec542a81..12ef211a65 100644
--- a/packages/core/src/generation.ts
+++ b/packages/core/src/generation.ts
@@ -76,27 +76,49 @@ export async function generateText({
runtime.character.modelEndpointOverride || models[provider].endpoint;
let model = models[provider].model[modelClass];
- // if runtime.getSetting("LLAMACLOUD_MODEL_LARGE") is true and modelProvider is LLAMACLOUD, then use the large model
- if (
- (runtime.getSetting("LLAMACLOUD_MODEL_LARGE") &&
- provider === ModelProviderName.LLAMACLOUD) ||
- (runtime.getSetting("TOGETHER_MODEL_LARGE") &&
- provider === ModelProviderName.TOGETHER)
- ) {
- model =
- runtime.getSetting("LLAMACLOUD_MODEL_LARGE") ||
- runtime.getSetting("TOGETHER_MODEL_LARGE");
- }
-
- if (
- (runtime.getSetting("LLAMACLOUD_MODEL_SMALL") &&
- provider === ModelProviderName.LLAMACLOUD) ||
- (runtime.getSetting("TOGETHER_MODEL_SMALL") &&
- provider === ModelProviderName.TOGETHER)
- ) {
- model =
- runtime.getSetting("LLAMACLOUD_MODEL_SMALL") ||
- runtime.getSetting("TOGETHER_MODEL_SMALL");
+ // allow character.json settings => secrets to override models
+ // FIXME: add MODEL_MEDIUM support
+ switch(provider) {
+ // if runtime.getSetting("LLAMACLOUD_MODEL_LARGE") is true and modelProvider is LLAMACLOUD, then use the large model
+ case ModelProviderName.LLAMACLOUD: {
+ switch(modelClass) {
+ case ModelClass.LARGE: {
+ model = runtime.getSetting("LLAMACLOUD_MODEL_LARGE") || model;
+ }
+ break;
+ case ModelClass.SMALL: {
+ model = runtime.getSetting("LLAMACLOUD_MODEL_SMALL") || model;
+ }
+ break;
+ }
+ }
+ break;
+ case ModelProviderName.TOGETHER: {
+ switch(modelClass) {
+ case ModelClass.LARGE: {
+ model = runtime.getSetting("TOGETHER_MODEL_LARGE") || model;
+ }
+ break;
+ case ModelClass.SMALL: {
+ model = runtime.getSetting("TOGETHER_MODEL_SMALL") || model;
+ }
+ break;
+ }
+ }
+ break;
+ case ModelProviderName.OPENROUTER: {
+ switch(modelClass) {
+ case ModelClass.LARGE: {
+ model = runtime.getSetting("LARGE_OPENROUTER_MODEL") || model;
+ }
+ break;
+ case ModelClass.SMALL: {
+ model = runtime.getSetting("SMALL_OPENROUTER_MODEL") || model;
+ }
+ break;
+ }
+ }
+ break;
}
elizaLogger.info("Selected model:", model);
@@ -129,6 +151,8 @@ export async function generateText({
case ModelProviderName.ALI_BAILIAN:
case ModelProviderName.VOLENGINE:
case ModelProviderName.LLAMACLOUD:
+ case ModelProviderName.NANOGPT:
+ case ModelProviderName.HYPERBOLIC:
case ModelProviderName.TOGETHER: {
elizaLogger.debug("Initializing OpenAI model.");
const openai = createOpenAI({ apiKey, baseURL: endpoint });
@@ -386,6 +410,30 @@ export async function generateText({
}
case ModelProviderName.GAIANET: {
elizaLogger.debug("Initializing GAIANET model.");
+
+ var baseURL = models[provider].endpoint;
+ if (!baseURL) {
+ switch (modelClass) {
+ case ModelClass.SMALL:
+ baseURL =
+ settings.SMALL_GAIANET_SERVER_URL ||
+ "https://llama3b.gaia.domains/v1";
+ break;
+ case ModelClass.MEDIUM:
+ baseURL =
+ settings.MEDIUM_GAIANET_SERVER_URL ||
+ "https://llama8b.gaia.domains/v1";
+ break;
+ case ModelClass.LARGE:
+ baseURL =
+ settings.LARGE_GAIANET_SERVER_URL ||
+ "https://qwen72b.gaia.domains/v1";
+ break;
+ }
+ }
+
+ elizaLogger.debug("Using GAIANET model with baseURL:", baseURL);
+
const openai = createOpenAI({ apiKey, baseURL: endpoint });
const { text: openaiResponse } = await aiGenerateText({
@@ -672,7 +720,7 @@ export async function generateTextArray({
}
}
-export async function generateObject({
+export async function generateObjectDEPRECATED({
runtime,
context,
modelClass,
@@ -682,7 +730,7 @@ export async function generateObject({
modelClass: string;
}): Promise {
if (!context) {
- elizaLogger.error("generateObject context is empty");
+ elizaLogger.error("generateObjectDEPRECATED context is empty");
return null;
}
let retryDelay = 1000;
@@ -1111,7 +1159,7 @@ export const generateObjectV2 = async ({
mode = "json",
}: GenerationOptions): Promise> => {
if (!context) {
- const errorMessage = "generateObject context is empty";
+ const errorMessage = "generateObjectV2 context is empty";
console.error(errorMessage);
throw new Error(errorMessage);
}
@@ -1196,6 +1244,7 @@ export async function handleProvider(
case ModelProviderName.VOLENGINE:
case ModelProviderName.LLAMACLOUD:
case ModelProviderName.TOGETHER:
+ case ModelProviderName.NANOGPT:
return await handleOpenAI(options);
case ModelProviderName.ANTHROPIC:
return await handleAnthropic(options);
@@ -1204,7 +1253,7 @@ export async function handleProvider(
case ModelProviderName.GROQ:
return await handleGroq(options);
case ModelProviderName.LLAMALOCAL:
- return await generateObject({
+ return await generateObjectDEPRECATED({
runtime,
context,
modelClass,
diff --git a/packages/core/src/models.ts b/packages/core/src/models.ts
index f61ce20bfb..a705f4f204 100644
--- a/packages/core/src/models.ts
+++ b/packages/core/src/models.ts
@@ -13,11 +13,11 @@ export const models: Models = {
temperature: 0.6,
},
model: {
- [ModelClass.SMALL]: "gpt-4o-mini",
- [ModelClass.MEDIUM]: "gpt-4o",
- [ModelClass.LARGE]: "gpt-4o",
- [ModelClass.EMBEDDING]: "text-embedding-3-small",
- [ModelClass.IMAGE]: "dall-e-3",
+ [ModelClass.SMALL]: settings.SMALL_OPENAI_MODEL || "gpt-4o-mini",
+ [ModelClass.MEDIUM]: settings.MEDIUM_OPENAI_MODEL || "gpt-4o",
+ [ModelClass.LARGE]: settings.LARGE_OPENAI_MODEL || "gpt-4o",
+ [ModelClass.EMBEDDING]: settings.EMBEDDING_OPENAI_MODEL || "text-embedding-3-small",
+ [ModelClass.IMAGE]: settings.IMAGE_OPENAI_MODEL || "dall-e-3",
},
},
[ModelProviderName.ETERNALAI]: {
@@ -32,10 +32,13 @@ export const models: Models = {
},
model: {
[ModelClass.SMALL]:
+ settings.ETERNALAI_MODEL ||
"neuralmagic/Meta-Llama-3.1-405B-Instruct-quantized.w4a16",
[ModelClass.MEDIUM]:
+ settings.ETERNALAI_MODEL ||
"neuralmagic/Meta-Llama-3.1-405B-Instruct-quantized.w4a16",
[ModelClass.LARGE]:
+ settings.ETERNALAI_MODEL ||
"neuralmagic/Meta-Llama-3.1-405B-Instruct-quantized.w4a16",
[ModelClass.EMBEDDING]: "",
[ModelClass.IMAGE]: "",
@@ -52,9 +55,9 @@ export const models: Models = {
},
endpoint: "https://api.anthropic.com/v1",
model: {
- [ModelClass.SMALL]: "claude-3-haiku-20240307",
- [ModelClass.MEDIUM]: "claude-3-5-sonnet-20241022",
- [ModelClass.LARGE]: "claude-3-5-sonnet-20241022",
+ [ModelClass.SMALL]: settings.SMALL_ANTHROPIC_MODEL || "claude-3-haiku-20240307",
+ [ModelClass.MEDIUM]: settings.MEDIUM_ANTHROPIC_MODEL || "claude-3-5-sonnet-20241022",
+ [ModelClass.LARGE]: settings.LARGE_ANTHROPIC_MODEL || "claude-3-5-sonnet-20241022",
},
},
[ModelProviderName.CLAUDE_VERTEX]: {
@@ -101,10 +104,14 @@ export const models: Models = {
temperature: 0.7,
},
model: {
- [ModelClass.SMALL]: "llama-3.1-8b-instant",
- [ModelClass.MEDIUM]: "llama-3.3-70b-versatile",
- [ModelClass.LARGE]: "llama-3.2-90b-vision-preview",
- [ModelClass.EMBEDDING]: "llama-3.1-8b-instant",
+ [ModelClass.SMALL]:
+ settings.SMALL_GROQ_MODEL || "llama-3.1-8b-instant",
+ [ModelClass.MEDIUM]:
+ settings.MEDIUM_GROQ_MODEL || "llama-3.3-70b-versatile",
+ [ModelClass.LARGE]:
+ settings.LARGE_GROQ_MODEL || "llama-3.2-90b-vision-preview",
+ [ModelClass.EMBEDDING]:
+ settings.EMBEDDING_GROQ_MODEL || "llama-3.1-8b-instant",
},
},
[ModelProviderName.LLAMACLOUD]: {
@@ -118,7 +125,7 @@ export const models: Models = {
imageSettings: {
steps: 4,
},
- endpoint: "https://api.together.ai/v1",
+ endpoint: "https://api.llamacloud.com/v1",
model: {
[ModelClass.SMALL]: "meta-llama/Llama-3.2-3B-Instruct-Turbo",
[ModelClass.MEDIUM]: "meta-llama-3.1-8b-instruct",
@@ -179,16 +186,20 @@ export const models: Models = {
temperature: 0.7,
},
model: {
- [ModelClass.SMALL]: settings.SMALL_GOOGLE_MODEL ||
+ [ModelClass.SMALL]:
+ settings.SMALL_GOOGLE_MODEL ||
settings.GOOGLE_MODEL ||
"gemini-1.5-flash-latest",
- [ModelClass.MEDIUM]: settings.MEDIUM_GOOGLE_MODEL ||
+ [ModelClass.MEDIUM]:
+ settings.MEDIUM_GOOGLE_MODEL ||
settings.GOOGLE_MODEL ||
"gemini-1.5-flash-latest",
- [ModelClass.LARGE]: settings.LARGE_GOOGLE_MODEL ||
+ [ModelClass.LARGE]:
+ settings.LARGE_GOOGLE_MODEL ||
settings.GOOGLE_MODEL ||
"gemini-1.5-pro-latest",
- [ModelClass.EMBEDDING]: settings.EMBEDDING_GOOGLE_MODEL ||
+ [ModelClass.EMBEDDING]:
+ settings.EMBEDDING_GOOGLE_MODEL ||
settings.GOOGLE_MODEL ||
"text-embedding-004",
},
@@ -289,11 +300,17 @@ export const models: Models = {
},
endpoint: "https://llm-gateway.heurist.xyz",
model: {
- [ModelClass.SMALL]: "meta-llama/llama-3-70b-instruct",
- [ModelClass.MEDIUM]: "meta-llama/llama-3-70b-instruct",
- [ModelClass.LARGE]: "meta-llama/llama-3.1-405b-instruct",
+ [ModelClass.SMALL]:
+ settings.SMALL_HEURIST_MODEL ||
+ "meta-llama/llama-3-70b-instruct",
+ [ModelClass.MEDIUM]:
+ settings.MEDIUM_HEURIST_MODEL ||
+ "meta-llama/llama-3-70b-instruct",
+ [ModelClass.LARGE]:
+ settings.LARGE_HEURIST_MODEL ||
+ "meta-llama/llama-3.1-405b-instruct",
[ModelClass.EMBEDDING]: "", //Add later,
- [ModelClass.IMAGE]: "PepeXL",
+ [ModelClass.IMAGE]: settings.HEURIST_IMAGE_MODEL || "PepeXL",
},
},
[ModelProviderName.GALADRIEL]: {
@@ -311,6 +328,7 @@ export const models: Models = {
[ModelClass.MEDIUM]: "llama3.1:70b",
[ModelClass.LARGE]: "llama3.1:405b",
[ModelClass.EMBEDDING]: "gte-large-en-v1.5",
+ [ModelClass.IMAGE]: "stabilityai/stable-diffusion-xl-base-1.0",
},
},
[ModelProviderName.FAL]: {
@@ -341,11 +359,20 @@ export const models: Models = {
repetition_penalty: 0.4,
temperature: 0.7,
},
- endpoint: settings.GAIANET_SERVER_URL || "http://localhost:8080/v1",
+ endpoint: settings.GAIANET_SERVER_URL,
model: {
- [ModelClass.SMALL]: settings.GAIANET_MODEL || "llama3.2",
- [ModelClass.MEDIUM]: settings.GAIANET_MODEL || "llama3.2",
- [ModelClass.LARGE]: settings.GAIANET_MODEL || "llama3.2",
+ [ModelClass.SMALL]:
+ settings.GAIANET_MODEL ||
+ settings.SMALL_GAIANET_MODEL ||
+ "llama3b",
+ [ModelClass.MEDIUM]:
+ settings.GAIANET_MODEL ||
+ settings.MEDIUM_GAIANET_MODEL ||
+ "llama",
+ [ModelClass.LARGE]:
+ settings.GAIANET_MODEL ||
+ settings.LARGE_GAIANET_MODEL ||
+ "qwen72b",
[ModelClass.EMBEDDING]:
settings.GAIANET_EMBEDDING_MODEL || "nomic-embed",
},
@@ -384,6 +411,46 @@ export const models: Models = {
[ModelClass.EMBEDDING]: "doubao-embedding",
},
},
+ [ModelProviderName.NANOGPT]: {
+ endpoint: "https://nano-gpt.com/api/v1",
+ settings: {
+ stop: [],
+ maxInputTokens: 128000,
+ maxOutputTokens: 8192,
+ frequency_penalty: 0.0,
+ presence_penalty: 0.0,
+ temperature: 0.6,
+ },
+ model: {
+ [ModelClass.SMALL]: settings.SMALL_NANOGPT_MODEL || "gpt-4o-mini",
+ [ModelClass.MEDIUM]: settings.MEDIUM_NANOGPT_MODEL || "gpt-4o",
+ [ModelClass.LARGE]: settings.LARGE_NANOGPT_MODEL || "gpt-4o",
+ }
+ },
+ [ModelProviderName.HYPERBOLIC]: {
+ endpoint: "https://api.hyperbolic.xyz/v1",
+ settings: {
+ stop: [],
+ maxInputTokens: 128000,
+ maxOutputTokens: 8192,
+ temperature: 0.6,
+ },
+ model: {
+ [ModelClass.SMALL]:
+ settings.SMALL_HYPERBOLIC_MODEL ||
+ settings.HYPERBOLIC_MODEL ||
+ "meta-llama/Llama-3.2-3B-Instruct",
+ [ModelClass.MEDIUM]:
+ settings.MEDIUM_HYPERBOLIC_MODEL ||
+ settings.HYPERBOLIC_MODEL ||
+ "meta-llama/Meta-Llama-3.1-70B-Instruct",
+ [ModelClass.LARGE]:
+ settings.LARGE_HYPERBOLIC_MODEL ||
+ settings.HYPERBOLIC_MODEL ||
+ "meta-llama/Meta-Llama-3.1-405-Instruct",
+ [ModelClass.IMAGE]: settings.IMAGE_HYPERBOLIC_MODEL || "FLUX.1-dev",
+ },
+ },
};
export function getModel(provider: ModelProviderName, type: ModelClass) {
diff --git a/packages/core/src/runtime.ts b/packages/core/src/runtime.ts
index 36fde717ee..ca6c0b18ab 100644
--- a/packages/core/src/runtime.ts
+++ b/packages/core/src/runtime.ts
@@ -498,67 +498,73 @@ export class AgentRuntime implements IAgentRuntime {
state?: State,
callback?: HandlerCallback
): Promise {
- if (!responses[0].content?.action) {
- elizaLogger.warn("No action found in the response content.");
- return;
- }
-
- const normalizedAction = responses[0].content.action
- .toLowerCase()
- .replace("_", "");
-
- elizaLogger.success(`Normalized action: ${normalizedAction}`);
+ for (const response of responses) {
+ if (!response.content?.action) {
+ elizaLogger.warn("No action found in the response content.");
+ continue;
+ }
- let action = this.actions.find(
- (a: { name: string }) =>
- a.name
- .toLowerCase()
- .replace("_", "")
- .includes(normalizedAction) ||
- normalizedAction.includes(a.name.toLowerCase().replace("_", ""))
- );
+ const normalizedAction = response.content.action
+ .toLowerCase()
+ .replace("_", "");
+
+ elizaLogger.success(`Normalized action: ${normalizedAction}`);
+
+ let action = this.actions.find(
+ (a: { name: string }) =>
+ a.name
+ .toLowerCase()
+ .replace("_", "")
+ .includes(normalizedAction) ||
+ normalizedAction.includes(
+ a.name.toLowerCase().replace("_", "")
+ )
+ );
- if (!action) {
- elizaLogger.info("Attempting to find action in similes.");
- for (const _action of this.actions) {
- const simileAction = _action.similes.find(
- (simile) =>
- simile
- .toLowerCase()
- .replace("_", "")
- .includes(normalizedAction) ||
- normalizedAction.includes(
- simile.toLowerCase().replace("_", "")
- )
- );
- if (simileAction) {
- action = _action;
- elizaLogger.success(
- `Action found in similes: ${action.name}`
+ if (!action) {
+ elizaLogger.info("Attempting to find action in similes.");
+ for (const _action of this.actions) {
+ const simileAction = _action.similes.find(
+ (simile) =>
+ simile
+ .toLowerCase()
+ .replace("_", "")
+ .includes(normalizedAction) ||
+ normalizedAction.includes(
+ simile.toLowerCase().replace("_", "")
+ )
);
- break;
+ if (simileAction) {
+ action = _action;
+ elizaLogger.success(
+ `Action found in similes: ${action.name}`
+ );
+ break;
+ }
}
}
- }
- if (!action) {
- elizaLogger.error(
- "No action found for",
- responses[0].content.action
- );
- return;
- }
+ if (!action) {
+ elizaLogger.error(
+ "No action found for",
+ response.content.action
+ );
+ continue;
+ }
- if (!action.handler) {
- elizaLogger.error(`Action ${action.name} has no handler.`);
- return;
- }
+ if (!action.handler) {
+ elizaLogger.error(`Action ${action.name} has no handler.`);
+ continue;
+ }
- try {
- elizaLogger.info(`Executing handler for action: ${action.name}`);
- await action.handler(this, message, state, {}, callback);
- } catch (error) {
- elizaLogger.error(error);
+ try {
+ elizaLogger.info(
+ `Executing handler for action: ${action.name}`
+ );
+ await action.handler(this, message, state, {}, callback);
+ } catch (error) {
+ elizaLogger.error(error);
+ }
}
}
@@ -566,10 +572,16 @@ export class AgentRuntime implements IAgentRuntime {
* Evaluate the message and state using the registered evaluators.
* @param message The message to evaluate.
* @param state The state of the agent.
- * @param didRespond Whether the agent responded to the message.
+ * @param didRespond Whether the agent responded to the message.~
+ * @param callback The handler callback
* @returns The results of the evaluation.
*/
- async evaluate(message: Memory, state?: State, didRespond?: boolean) {
+ async evaluate(
+ message: Memory,
+ state?: State,
+ didRespond?: boolean,
+ callback?: HandlerCallback
+ ) {
const evaluatorPromises = this.evaluators.map(
async (evaluator: Evaluator) => {
elizaLogger.log("Evaluating", evaluator.name);
@@ -595,17 +607,12 @@ export class AgentRuntime implements IAgentRuntime {
return [];
}
- const evaluators = formatEvaluators(evaluatorsData as Evaluator[]);
- const evaluatorNames = formatEvaluatorNames(
- evaluatorsData as Evaluator[]
- );
-
const context = composeContext({
state: {
...state,
- evaluators,
- evaluatorNames,
- } as State,
+ evaluators: formatEvaluators(evaluatorsData),
+ evaluatorNames: formatEvaluatorNames(evaluatorsData),
+ },
template:
this.character.templates?.evaluationTemplate ||
evaluationTemplate,
@@ -617,21 +624,18 @@ export class AgentRuntime implements IAgentRuntime {
modelClass: ModelClass.SMALL,
});
- const parsedResult = parseJsonArrayFromText(
+ const evaluators = parseJsonArrayFromText(
result
) as unknown as string[];
- this.evaluators
- .filter((evaluator: Evaluator) =>
- parsedResult?.includes(evaluator.name)
- )
- .forEach((evaluator: Evaluator) => {
- if (!evaluator?.handler) return;
+ for (const evaluator of this.evaluators) {
+ if (!evaluators.includes(evaluator.name)) continue;
- evaluator.handler(this, message);
- });
+ if (evaluator.handler)
+ await evaluator.handler(this, message, state, {}, callback);
+ }
- return parsedResult;
+ return evaluators;
}
/**
diff --git a/packages/core/src/tests/parsing.test.ts b/packages/core/src/tests/parsing.test.ts
new file mode 100644
index 0000000000..636f0b00af
--- /dev/null
+++ b/packages/core/src/tests/parsing.test.ts
@@ -0,0 +1,94 @@
+import { describe, it, expect } from 'vitest';
+import {
+ parseShouldRespondFromText,
+ parseBooleanFromText,
+ parseJsonArrayFromText,
+ parseJSONObjectFromText,
+} from '../parsing';
+
+describe('Parsing Module', () => {
+ describe('parseShouldRespondFromText', () => {
+ it('should parse exact matches', () => {
+ expect(parseShouldRespondFromText('[RESPOND]')).toBe('RESPOND');
+ expect(parseShouldRespondFromText('[IGNORE]')).toBe('IGNORE');
+ expect(parseShouldRespondFromText('[STOP]')).toBe('STOP');
+ });
+
+ it('should handle case insensitive input', () => {
+ expect(parseShouldRespondFromText('[respond]')).toBe('RESPOND');
+ expect(parseShouldRespondFromText('[ignore]')).toBe('IGNORE');
+ expect(parseShouldRespondFromText('[stop]')).toBe('STOP');
+ });
+
+ it('should handle text containing keywords', () => {
+ expect(parseShouldRespondFromText('I think we should RESPOND here')).toBe('RESPOND');
+ expect(parseShouldRespondFromText('Better to IGNORE this one')).toBe('IGNORE');
+ expect(parseShouldRespondFromText('We need to STOP now')).toBe('STOP');
+ });
+
+ it('should return null for invalid input', () => {
+ expect(parseShouldRespondFromText('')).toBe(null);
+ expect(parseShouldRespondFromText('invalid')).toBe(null);
+ expect(parseShouldRespondFromText('[INVALID]')).toBe(null);
+ });
+ });
+
+ describe('parseBooleanFromText', () => {
+ it('should parse exact YES/NO matches', () => {
+ expect(parseBooleanFromText('YES')).toBe(true);
+ expect(parseBooleanFromText('NO')).toBe(false);
+ });
+
+ it('should handle case insensitive input', () => {
+ expect(parseBooleanFromText('yes')).toBe(true);
+ expect(parseBooleanFromText('no')).toBe(false);
+ });
+
+ it('should return null for invalid input', () => {
+ expect(parseBooleanFromText('')).toBe(null);
+ expect(parseBooleanFromText('maybe')).toBe(null);
+ expect(parseBooleanFromText('YES NO')).toBe(null);
+ });
+ });
+
+ describe('parseJsonArrayFromText', () => {
+ it('should parse JSON array from code block', () => {
+ const input = '```json\n["item1", "item2", "item3"]\n```';
+ expect(parseJsonArrayFromText(input)).toEqual(['item1', 'item2', 'item3']);
+ });
+
+ it('should handle empty arrays', () => {
+ expect(parseJsonArrayFromText('```json\n[]\n```')).toEqual([]);
+ expect(parseJsonArrayFromText('[]')).toEqual(null);
+ });
+
+ it('should return null for invalid JSON', () => {
+ expect(parseJsonArrayFromText('invalid')).toBe(null);
+ expect(parseJsonArrayFromText('[invalid]')).toBe(null);
+ expect(parseJsonArrayFromText('```json\n[invalid]\n```')).toBe(null);
+ });
+ });
+
+ describe('parseJSONObjectFromText', () => {
+ it('should parse JSON object from code block', () => {
+ const input = '```json\n{"key": "value", "number": 42}\n```';
+ expect(parseJSONObjectFromText(input)).toEqual({ key: 'value', number: 42 });
+ });
+
+ it('should parse JSON object without code block', () => {
+ const input = '{"key": "value", "number": 42}';
+ expect(parseJSONObjectFromText(input)).toEqual({ key: 'value', number: 42 });
+ });
+
+ it('should handle empty objects', () => {
+ expect(parseJSONObjectFromText('```json\n{}\n```')).toEqual({});
+ expect(parseJSONObjectFromText('{}')).toEqual({});
+ });
+
+ it('should return null for invalid JSON', () => {
+ expect(parseJSONObjectFromText('invalid')).toBe(null);
+ expect(parseJSONObjectFromText('{invalid}')).toBe(null);
+ expect(parseJSONObjectFromText('```json\n{invalid}\n```')).toBe(null);
+ });
+ });
+});
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 5ef9ea2f64..1c5be52579 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -205,6 +205,8 @@ export type Models = {
[ModelProviderName.GAIANET]: Model;
[ModelProviderName.ALI_BAILIAN]: Model;
[ModelProviderName.VOLENGINE]: Model;
+ [ModelProviderName.NANOGPT]: Model;
+ [ModelProviderName.HYPERBOLIC]: Model;
};
/**
@@ -230,6 +232,8 @@ export enum ModelProviderName {
GAIANET = "gaianet",
ALI_BAILIAN = "ali_bailian",
VOLENGINE = "volengine",
+ NANOGPT = "nanogpt",
+ HYPERBOLIC = "hyperbolic",
}
/**
@@ -681,7 +685,7 @@ export type Character = {
/** Optional configuration */
settings?: {
secrets?: { [key: string]: string };
- buttplug?: boolean;
+ intiface?: boolean;
voice?: {
model?: string; // For VITS
url?: string; // Legacy VITS support
@@ -1023,7 +1027,8 @@ export interface IAgentRuntime {
evaluate(
message: Memory,
state?: State,
- didRespond?: boolean
+ didRespond?: boolean,
+ callback?: HandlerCallback
): Promise;
ensureParticipantExists(userId: UUID, roomId: UUID): Promise;
@@ -1118,6 +1123,15 @@ export interface IPdfService extends Service {
convertPdfToText(pdfBuffer: Buffer): Promise;
}
+export interface IAwsS3Service extends Service {
+ uploadFile(imagePath: string, useSignedUrl: boolean, expiresIn: number ): Promise<{
+ success: boolean;
+ url?: string;
+ error?: string;
+ }>;
+ generateSignedUrl(fileName: string, expiresIn: number): Promise
+}
+
export type SearchResult = {
title: string;
url: string;
@@ -1143,7 +1157,8 @@ export enum ServiceType {
BROWSER = "browser",
SPEECH_GENERATION = "speech_generation",
PDF = "pdf",
- BUTTPLUG = "buttplug",
+ INTIFACE = "intiface",
+ AWS_S3 = "aws_s3",
}
export enum LoggingLevel {
diff --git a/packages/plugin-aptos/package.json b/packages/plugin-aptos/package.json
index 27fe3761d2..026a58d158 100644
--- a/packages/plugin-aptos/package.json
+++ b/packages/plugin-aptos/package.json
@@ -16,6 +16,7 @@
},
"scripts": {
"build": "tsup --format esm --dts",
+ "dev": "tsup --format esm --dts --watch",
"lint": "eslint . --fix",
"test": "vitest run"
},
diff --git a/packages/plugin-aptos/src/actions/transfer.ts b/packages/plugin-aptos/src/actions/transfer.ts
index 5110c76461..19b3c86fcc 100644
--- a/packages/plugin-aptos/src/actions/transfer.ts
+++ b/packages/plugin-aptos/src/actions/transfer.ts
@@ -10,7 +10,7 @@ import {
type Action,
} from "@ai16z/eliza";
import { composeContext } from "@ai16z/eliza";
-import { generateObject } from "@ai16z/eliza";
+import { generateObjectDEPRECATED } from "@ai16z/eliza";
import {
Account,
Aptos,
@@ -111,7 +111,7 @@ export default {
});
// Generate transfer content
- const content = await generateObject({
+ const content = await generateObjectDEPRECATED({
runtime,
context: transferContext,
modelClass: ModelClass.SMALL,
diff --git a/packages/plugin-coinbase/advanced-sdk-ts b/packages/plugin-coinbase/advanced-sdk-ts
deleted file mode 160000
index 1fba324dc5..0000000000
--- a/packages/plugin-coinbase/advanced-sdk-ts
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1fba324dc5bb2dc527d9cd09cbd0d4946e450252
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/.eslintrc.js b/packages/plugin-coinbase/advanced-sdk-ts/.eslintrc.js
new file mode 100644
index 0000000000..ec00d481ab
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/.eslintrc.js
@@ -0,0 +1,21 @@
+/** @type {import('eslint').Linter.Config} */
+module.exports = {
+ parser: '@typescript-eslint/parser',
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'prettier',
+ 'plugin:prettier/recommended',
+ ],
+ plugins: ['prettier'],
+ rules: {
+ 'prettier/prettier': 'error',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
+ '@typescript-eslint/no-explicit-any': 'off',
+ },
+ ignorePatterns: ['**/dist/**', '**/node_modules/**', '**/*.md'],
+ env: {
+ node: true, // Add this line to recognize Node.js globals
+ es2021: true, // Optionally include modern JavaScript features
+ },
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/.gitignore b/packages/plugin-coinbase/advanced-sdk-ts/.gitignore
new file mode 100644
index 0000000000..722ad2ade5
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/.gitignore
@@ -0,0 +1,6 @@
+.env
+src/rest/main.ts
+dist/
+node_modules/
+.idea/
+package-lock.json
\ No newline at end of file
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/.prettierrc b/packages/plugin-coinbase/advanced-sdk-ts/.prettierrc
new file mode 100644
index 0000000000..c3e03287be
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "semi": true,
+ "singleQuote": true,
+ "trailingComma": "es5"
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/CHANGELOG.md b/packages/plugin-coinbase/advanced-sdk-ts/CHANGELOG.md
new file mode 100644
index 0000000000..eda768580a
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Changelog
+
+## [0.1.0] - 2024-SEP-06
+
+### Added
+
+- Support for all Coinbase Advanced API REST endpoints via central client
+- Custom Request and Response objects for endpoints
+- Custom error types
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/README.md b/packages/plugin-coinbase/advanced-sdk-ts/README.md
new file mode 100644
index 0000000000..73f0bf459d
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/README.md
@@ -0,0 +1,126 @@
+# Coinbase Advanced API TypeScript SDK
+
+Welcome to the Coinbase Advanced API TypeScript SDK. This TypeScript project was created to allow developers to easily plug into the [Coinbase Advanced API](https://docs.cdp.coinbase.com/advanced-trade/docs/welcome).
+
+Coinbase Advanced Trade offers a comprehensive API for traders, providing access to real-time market data, order management, and execution. Elevate your trading strategies and develop sophisticated solutions using our powerful tools and features.
+
+For more information on all the available REST endpoints, see the [API Reference](https://docs.cdp.coinbase.com/advanced-trade/reference/).
+
+---
+
+## Installation
+
+```bash
+npm install
+```
+
+---
+
+## Build and Use
+
+To build the project, run the following command:
+
+```bash
+npm run build
+```
+
+_Note: To avoid potential issues, do not forget to build your project again after making any changes to it._
+
+After building the project, each `.ts` file will have its `.js` counterpart generated.
+
+To run a file, use the following command:
+
+```
+node dist/{INSERT-FILENAME}.js
+```
+
+For example, a `main.ts` file would be run like:
+
+```bash
+node dist/main.js
+```
+
+---
+
+## Coinbase Developer Platform (CDP) API Keys
+
+This SDK uses Cloud Developer Platform (CDP) API keys. To use this SDK, you will need to create a CDP API key and secret by following the instructions [here](https://docs.cdp.coinbase.com/advanced-trade/docs/getting-started).
+Make sure to save your API key and secret in a safe place. You will not be able to retrieve your secret again.
+
+---
+
+## Importing the RESTClient
+
+All the REST endpoints are available directly from the client, therefore it's all you need to import.
+
+```
+import { RESTClient } from './rest';
+```
+
+---
+
+## Authentication
+
+Authentication of CDP API Keys is handled automatically by the SDK when making a REST request.
+
+After creating your CDP API keys, store them using your desired method and simply pass them into the client during initialization like:
+
+```
+const client = new RESTClient(API_KEY, API_SECRET);
+```
+
+---
+
+## Making Requests
+
+Here are a few examples requests:
+
+**[List Accounts](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getaccounts)**
+
+```
+client
+ .listAccounts({})
+ .then((result) => {
+ console.log(result);
+ })
+ .catch((error) => {
+ console.error(error.message);
+ });
+```
+
+**[Get Product](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getproduct)**
+
+```
+client
+ .getProduct({productId: "BTC-USD"})
+ .then((result) => {
+ console.log(result);
+ })
+ .catch((error) => {
+ console.error(error.message);
+ });
+```
+
+**[Create Order](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_postorder)**
+
+_$10 Market Buy on BTC-USD_
+
+```
+client
+ .createOrder({
+ clientOrderId: "00000001",
+ productId: "BTC-USD",
+ side: OrderSide.BUY,
+ orderConfiguration:{
+ market_market_ioc: {
+ quote_size: "10"
+ }
+ }
+ })
+ .then((result) => {
+ console.log(result);
+ })
+ .catch((error) => {
+ console.error(error.message);
+ });
+```
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/package.json b/packages/plugin-coinbase/advanced-sdk-ts/package.json
new file mode 100644
index 0000000000..78480e529e
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "@coinbase-samples/advanced-sdk-ts",
+ "version": "0.1.0",
+ "main": "dist/main.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "tsc",
+ "lint": "eslint . --ext .js,.ts",
+ "format": "prettier --write \"**/*.{js,ts,tsx,json,css,md}\""
+ },
+ "files": [
+ "dist/"
+ ],
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "dependencies": {
+ "jsonwebtoken": "^9.0.2",
+ "node-fetch": "^2.6.1"
+ },
+ "devDependencies": {
+ "@types/jsonwebtoken": "^9.0.7",
+ "@types/node-fetch": "^2.6.11",
+ "@typescript-eslint/eslint-plugin": "^5.59.0",
+ "@typescript-eslint/parser": "^5.59.0",
+ "dotenv": "^16.4.5",
+ "eslint": "^8.35.0",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "prettier": "^2.8.8",
+ "typescript": "^5.5.4"
+ }
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/constants.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/constants.ts
new file mode 100644
index 0000000000..8962365907
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/constants.ts
@@ -0,0 +1,6 @@
+export const BASE_URL = 'api.coinbase.com';
+export const API_PREFIX = '/api/v3/brokerage';
+export const ALGORITHM = 'ES256';
+export const VERSION = '0.1.0';
+export const USER_AGENT = `coinbase-advanced-ts/${VERSION}`;
+export const JWT_ISSUER = 'cdp';
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/jwt-generator.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/jwt-generator.ts
new file mode 100644
index 0000000000..275fc876e6
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/jwt-generator.ts
@@ -0,0 +1,31 @@
+import jwt from 'jsonwebtoken';
+import { BASE_URL, ALGORITHM, JWT_ISSUER } from './constants';
+import crypto from 'crypto';
+
+export function generateToken(
+ requestMethod: string,
+ requestPath: string,
+ apiKey: string,
+ apiSecret: string
+): string {
+ const uri = `${requestMethod} ${BASE_URL}${requestPath}`;
+ const payload = {
+ iss: JWT_ISSUER,
+ nbf: Math.floor(Date.now() / 1000),
+ exp: Math.floor(Date.now() / 1000) + 120,
+ sub: apiKey,
+ uri,
+ };
+
+ const header = {
+ alg: ALGORITHM,
+ kid: apiKey,
+ nonce: crypto.randomBytes(16).toString('hex'),
+ };
+ const options: jwt.SignOptions = {
+ algorithm: ALGORITHM as jwt.Algorithm,
+ header: header,
+ };
+
+ return jwt.sign(payload, apiSecret as string, options);
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/accounts.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/accounts.ts
new file mode 100644
index 0000000000..04cec4b934
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/accounts.ts
@@ -0,0 +1,36 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ GetAccountRequest,
+ GetAccountResponse,
+ ListAccountsRequest,
+ ListAccountsResponse,
+} from './types/accounts-types';
+import { method } from './types/request-types';
+
+// [GET] Get Account
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getaccount
+export function getAccount(
+ this: RESTBase,
+ { accountUuid }: GetAccountRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/accounts/${accountUuid}`,
+ isPublic: false,
+ });
+}
+
+// [GET] List Accounts
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getaccounts
+export function listAccounts(
+ this: RESTBase,
+ requestParams: ListAccountsRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/accounts`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/converts.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/converts.ts
new file mode 100644
index 0000000000..435a807e28
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/converts.ts
@@ -0,0 +1,53 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ CommitConvertTradeRequest,
+ CommitConvertTradeResponse,
+ CreateConvertQuoteRequest,
+ CreateConvertQuoteResponse,
+ GetConvertTradeRequest,
+ GetConvertTradeResponse,
+} from './types/converts-types';
+import { method } from './types/request-types';
+
+// [POST] Create Convert Quote
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_createconvertquote
+export function createConvertQuote(
+ this: RESTBase,
+ requestParams: CreateConvertQuoteRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/convert/quote`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Convert Trade
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getconverttrade
+export function getConvertTrade(
+ this: RESTBase,
+ { tradeId, ...requestParams }: GetConvertTradeRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/convert/trade/${tradeId}`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [POST] Commit Connvert Trade
+// https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_commitconverttrade
+export function commitConvertTrade(
+ this: RESTBase,
+ { tradeId, ...requestParams }: CommitConvertTradeRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/convert/trade/${tradeId}`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/dataAPI.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/dataAPI.ts
new file mode 100644
index 0000000000..2adc646607
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/dataAPI.ts
@@ -0,0 +1,17 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+
+import { method } from './types/request-types';
+import { GetAPIKeyPermissionsResponse } from './types/dataAPI-types';
+
+// [GET] Get API Key Permissions
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getapikeypermissions
+export function getAPIKeyPermissions(
+ this: RESTBase
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/key_permissions`,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/errors.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/errors.ts
new file mode 100644
index 0000000000..5d9695ef67
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/errors.ts
@@ -0,0 +1,36 @@
+import { Response } from 'node-fetch';
+
+class CoinbaseError extends Error {
+ statusCode: number;
+ response: Response;
+
+ constructor(message: string, statusCode: number, response: Response) {
+ super(message);
+ this.name = 'CoinbaseError';
+ this.statusCode = statusCode;
+ this.response = response;
+ }
+}
+
+export function handleException(
+ response: Response,
+ responseText: string,
+ reason: string
+) {
+ let message: string | undefined;
+
+ if (
+ (400 <= response.status && response.status <= 499) ||
+ (500 <= response.status && response.status <= 599)
+ ) {
+ if (
+ response.status == 403 &&
+ responseText.includes('"error_details":"Missing required scopes"')
+ ) {
+ message = `${response.status} Coinbase Error: Missing Required Scopes. Please verify your API keys include the necessary permissions.`;
+ } else
+ message = `${response.status} Coinbase Error: ${reason} ${responseText}`;
+
+ throw new CoinbaseError(message, response.status, response);
+ }
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/fees.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/fees.ts
new file mode 100644
index 0000000000..9f9c08e5cf
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/fees.ts
@@ -0,0 +1,21 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ GetTransactionsSummaryRequest,
+ GetTransactionsSummaryResponse,
+} from './types/fees-types';
+import { method } from './types/request-types';
+
+// [GET] Get Transaction Summary
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_commitconverttrade
+export function getTransactionSummary(
+ this: RESTBase,
+ requestParams: GetTransactionsSummaryRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/transaction_summary`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/futures.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/futures.ts
new file mode 100644
index 0000000000..82412ec4e3
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/futures.ts
@@ -0,0 +1,133 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ CancelPendingFuturesSweep,
+ GetCurrentMarginWindowRequest,
+ GetCurrentMarginWindowResponse,
+ GetFuturesBalanceSummaryResponse,
+ GetFuturesPositionRequest,
+ GetFuturesPositionResponse,
+ GetIntradayMarginSettingResponse,
+ ListFuturesPositionsResponse,
+ ListFuturesSweepsResponse,
+ ScheduleFuturesSweepRequest,
+ ScheduleFuturesSweepResponse,
+ SetIntradayMarginSettingRequest,
+ SetIntradayMarginSettingResponse,
+} from './types/futures-types';
+import { method } from './types/request-types';
+
+// [GET] Get Futures Balance Summary
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getfcmbalancesummary
+export function getFuturesBalanceSummary(
+ this: RESTBase
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/cfm/balance_summary`,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Intraday Margin Setting
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getintradaymarginsetting
+export function getIntradayMarginSetting(
+ this: RESTBase
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/cfm/intraday/margin_setting`,
+ isPublic: false,
+ });
+}
+
+// [POST] Set Intraday Margin Setting
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_setintradaymarginsetting
+export function setIntradayMarginSetting(
+ this: RESTBase,
+ requestParams: SetIntradayMarginSettingRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/cfm/intraday/margin_setting`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Current Margin Window
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getcurrentmarginwindow
+export function getCurrentMarginWindow(
+ this: RESTBase,
+ requestParams: GetCurrentMarginWindowRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/cfm/intraday/current_margin_window`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] List Futures Positions
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getfcmpositions
+export function listFuturesPositions(
+ this: RESTBase
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/cfm/positions`,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Futures Position
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getfcmposition
+export function getFuturesPosition(
+ this: RESTBase,
+ { productId }: GetFuturesPositionRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/cfm/positions/${productId}`,
+ isPublic: false,
+ });
+}
+
+// [POST] Schedule Futures Sweep
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_schedulefcmsweep
+export function scheduleFuturesSweep(
+ this: RESTBase,
+ requestParams: ScheduleFuturesSweepRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/cfm/sweeps/schedule`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] List Futures Sweeps
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getfcmsweeps
+export function listFuturesSweeps(
+ this: RESTBase
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/cfm/sweeps`,
+ isPublic: false,
+ });
+}
+
+// [DELETE] Cancel Pending Futures Sweep
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_cancelfcmsweep
+export function cancelPendingFuturesSweep(
+ this: RESTBase
+): Promise {
+ return this.request({
+ method: method.DELETE,
+ endpoint: `${API_PREFIX}/cfm/sweeps`,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/index.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/index.ts
new file mode 100644
index 0000000000..101223a645
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/index.ts
@@ -0,0 +1,95 @@
+import { RESTBase } from './rest-base';
+import * as Accounts from './accounts';
+import * as Converts from './converts';
+import * as DataAPI from './dataAPI';
+import * as Fees from './fees';
+import * as Futures from './futures';
+import * as Orders from './orders';
+import * as Payments from './payments';
+import * as Perpetuals from './perpetuals';
+import * as Portfolios from './portfolios';
+import * as Products from './products';
+import * as Public from './public';
+
+export class RESTClient extends RESTBase {
+ constructor(key?: string | undefined, secret?: string | undefined) {
+ super(key, secret);
+ }
+
+ // =============== ACCOUNTS endpoints ===============
+ public getAccount = Accounts.getAccount.bind(this);
+ public listAccounts = Accounts.listAccounts.bind(this);
+
+ // =============== CONVERTS endpoints ===============
+ public createConvertQuote = Converts.createConvertQuote.bind(this);
+ public commitConvertTrade = Converts.commitConvertTrade.bind(this);
+ public getConvertTrade = Converts.getConvertTrade.bind(this);
+
+ // =============== DATA API endpoints ===============
+ public getAPIKeyPermissions = DataAPI.getAPIKeyPermissions.bind(this);
+
+ // =============== FEES endpoints ===============
+ public getTransactionSummary = Fees.getTransactionSummary.bind(this);
+
+ // =============== FUTURES endpoints ===============
+ public getFuturesBalanceSummary = Futures.getFuturesBalanceSummary.bind(this);
+ public getIntradayMarginSetting = Futures.getIntradayMarginSetting.bind(this);
+ public setIntradayMarginSetting = Futures.setIntradayMarginSetting.bind(this);
+ public getCurrentMarginWindow = Futures.getCurrentMarginWindow.bind(this);
+ public listFuturesPositions = Futures.listFuturesPositions.bind(this);
+ public getFuturesPosition = Futures.getFuturesPosition.bind(this);
+ public scheduleFuturesSweep = Futures.scheduleFuturesSweep.bind(this);
+ public listFuturesSweeps = Futures.listFuturesSweeps.bind(this);
+ public cancelPendingFuturesSweep =
+ Futures.cancelPendingFuturesSweep.bind(this);
+
+ // =============== ORDERS endpoints ===============
+ public createOrder = Orders.createOrder.bind(this);
+ public cancelOrders = Orders.cancelOrders.bind(this);
+ public editOrder = Orders.editOrder.bind(this);
+ public editOrderPreview = Orders.editOrderPreview.bind(this);
+ public listOrders = Orders.listOrders.bind(this);
+ public listFills = Orders.listFills.bind(this);
+ public getOrder = Orders.getOrder.bind(this);
+ public previewOrder = Orders.previewOrder.bind(this);
+ public closePosition = Orders.closePosition.bind(this);
+
+ // =============== PAYMENTS endpoints ===============
+ public listPaymentMethods = Payments.listPaymentMethods.bind(this);
+ public getPaymentMethod = Payments.getPaymentMethod.bind(this);
+
+ // =============== PERPETUALS endpoints ===============
+ public allocatePortfolio = Perpetuals.allocatePortfolio.bind(this);
+ public getPerpetualsPortfolioSummary =
+ Perpetuals.getPerpetualsPortfolioSummary.bind(this);
+ public listPerpetualsPositions =
+ Perpetuals.listPerpetualsPositions.bind(this);
+ public getPerpetualsPosition = Perpetuals.getPerpertualsPosition.bind(this);
+ public getPortfolioBalances = Perpetuals.getPortfolioBalances.bind(this);
+ public optInOutMultiAssetCollateral =
+ Perpetuals.optInOutMultiAssetCollateral.bind(this);
+
+ // =============== PORTFOLIOS endpoints ===============
+ public listPortfolios = Portfolios.listPortfolios.bind(this);
+ public createPortfolio = Portfolios.createPortfolio.bind(this);
+ public deletePortfolio = Portfolios.deletePortfolio.bind(this);
+ public editPortfolio = Portfolios.editPortfolio.bind(this);
+ public movePortfolioFunds = Portfolios.movePortfolioFunds.bind(this);
+ public getPortfolioBreakdown = Portfolios.getPortfolioBreakdown.bind(this);
+
+ // =============== PRODUCTS endpoints ===============
+ public getBestBidAsk = Products.getBestBidAsk.bind(this);
+ public getProductBook = Products.getProductBook.bind(this);
+ public listProducts = Products.listProducts.bind(this);
+ public getProduct = Products.getProduct.bind(this);
+ public getProductCandles = Products.getProductCandles.bind(this);
+ public getMarketTrades = Products.getMarketTrades.bind(this);
+
+ // =============== PUBLIC endpoints ===============
+ public getServerTime = Public.getServerTime.bind(this);
+ public getPublicProductBook = Public.getPublicProductBook.bind(this);
+ public listPublicProducts = Public.listPublicProducts.bind(this);
+ public getPublicProduct = Public.getPublicProduct.bind(this);
+ public getPublicProductCandles = Public.getPublicProductCandles.bind(this);
+ public getPublicMarketTrades = Public.getPublicMarketTrades.bind(this);
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/orders.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/orders.ts
new file mode 100644
index 0000000000..04bf2aefbc
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/orders.ts
@@ -0,0 +1,149 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ CancelOrdersRequest,
+ CancelOrdersResponse,
+ ClosePositionRequest,
+ ClosePositionResponse,
+ CreateOrderRequest,
+ CreateOrderResponse,
+ EditOrderPreviewRequest,
+ EditOrderPreviewResponse,
+ EditOrderRequest,
+ EditOrderResponse,
+ GetOrderRequest,
+ GetOrderResponse,
+ ListFillsRequest,
+ ListFillsResponse,
+ ListOrdersRequest,
+ ListOrdersResponse,
+ PreviewOrderRequest,
+ PreviewOrderResponse,
+} from './types/orders-types';
+import { method } from './types/request-types';
+
+// [POST] Create Order
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_postorder
+export function createOrder(
+ this: RESTBase,
+ requestParams: CreateOrderRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/orders`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [POST] Cancel Orders
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_cancelorders
+export function cancelOrders(
+ this: RESTBase,
+ requestParams: CancelOrdersRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/orders/batch_cancel`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [POST] Edit Order
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_editorder
+export function editOrder(
+ this: RESTBase,
+ requestParams: EditOrderRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/orders/edit`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [POST] Edit Order Preview
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_previeweditorder
+export function editOrderPreview(
+ this: RESTBase,
+ requestParams: EditOrderPreviewRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/orders/edit_preview`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] List Orders
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_gethistoricalorders
+export function listOrders(
+ this: RESTBase,
+ requestParams: ListOrdersRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/orders/historical/batch`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] List Fills
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getfills
+export function listFills(
+ this: RESTBase,
+ requestParams: ListFillsRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/orders/historical/fills`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Order
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_gethistoricalorder
+export function getOrder(
+ this: RESTBase,
+ { orderId }: GetOrderRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/orders/historical/${orderId}`,
+ isPublic: false,
+ });
+}
+
+// [POST] Preview Order
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_previeworder
+export function previewOrder(
+ this: RESTBase,
+ requestParams: PreviewOrderRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/orders/preview`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [POST] Close Position
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_closeposition
+export function closePosition(
+ this: RESTBase,
+ requestParams: ClosePositionRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/orders/close_position`,
+ queryParams: undefined,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/payments.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/payments.ts
new file mode 100644
index 0000000000..defa3f4dd6
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/payments.ts
@@ -0,0 +1,33 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ GetPaymentMethodRequest,
+ GetPaymentMethodResponse,
+ ListPaymentMethodsResponse,
+} from './types/payments-types';
+import { method } from './types/request-types';
+
+// [GET] List Payment Methods
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpaymentmethods
+export function listPaymentMethods(
+ this: RESTBase
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/payment_methods`,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Payment Method
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpaymentmethod
+export function getPaymentMethod(
+ this: RESTBase,
+ { paymentMethodId }: GetPaymentMethodRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/payment_methods/${paymentMethodId}`,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/perpetuals.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/perpetuals.ts
new file mode 100644
index 0000000000..6e1c568d69
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/perpetuals.ts
@@ -0,0 +1,97 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ AllocatePortfolioRequest,
+ AllocatePortfolioResponse,
+ GetPerpetualsPortfolioSummaryRequest,
+ GetPerpetualsPortfolioSummaryResponse,
+ GetPerpetualsPositionRequest,
+ GetPerpetualsPositionResponse,
+ GetPortfolioBalancesRequest,
+ GetPortfolioBalancesResponse,
+ ListPerpetualsPositionsRequest,
+ ListPerpetualsPositionsResponse,
+ OptInOutMultiAssetCollateralRequest,
+ OptInOutMultiAssetCollateralResponse,
+} from './types/perpetuals-types';
+import { method } from './types/request-types';
+
+// [POST] Allocate Portfolio
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_allocateportfolio
+export function allocatePortfolio(
+ this: RESTBase,
+ requestParams: AllocatePortfolioRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/intx/allocate`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Perpetuals Portfolio Summary
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getintxportfoliosummary
+export function getPerpetualsPortfolioSummary(
+ this: RESTBase,
+ { portfolioUuid }: GetPerpetualsPortfolioSummaryRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/intx/portfolio/${portfolioUuid}`,
+ isPublic: false,
+ });
+}
+
+// [GET] List Perpetuals Positions
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getintxpositions
+export function listPerpetualsPositions(
+ this: RESTBase,
+ { portfolioUuid }: ListPerpetualsPositionsRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/intx/positions/${portfolioUuid}`,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Perpetuals Position
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getintxposition
+export function getPerpertualsPosition(
+ this: RESTBase,
+ { portfolioUuid, symbol }: GetPerpetualsPositionRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/intx/positions/${portfolioUuid}/${symbol}`,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Portfolio Balances
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getintxbalances
+export function getPortfolioBalances(
+ this: RESTBase,
+ { portfolioUuid }: GetPortfolioBalancesRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/intx/balances/${portfolioUuid}`,
+ isPublic: false,
+ });
+}
+
+// [POST] Opt In or Out of Multi Asset Collateral
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_intxmultiassetcollateral
+export function optInOutMultiAssetCollateral(
+ this: RESTBase,
+ requestParams: OptInOutMultiAssetCollateralRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/intx/multi_asset_collateral`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/portfolios.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/portfolios.ts
new file mode 100644
index 0000000000..df5e5791f8
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/portfolios.ts
@@ -0,0 +1,100 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ CreatePortfolioRequest,
+ CreatePortfolioResponse,
+ DeletePortfolioRequest,
+ DeletePortfolioResponse,
+ EditPortfolioRequest,
+ EditPortfolioResponse,
+ GetPortfolioBreakdownRequest,
+ GetPortfolioBreakdownResponse,
+ ListPortfoliosRequest,
+ ListPortfoliosResponse,
+ MovePortfolioFundsRequest,
+ MovePortfolioFundsResponse,
+} from './types/portfolios-types';
+import { method } from './types/request-types';
+
+// [GET] List Portfolios
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getportfolios
+export function listPortfolios(
+ this: RESTBase,
+ requestParams: ListPortfoliosRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/portfolios`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [POST] Create Portfolio
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_createportfolio
+export function createPortfolio(
+ this: RESTBase,
+ requestParams: CreatePortfolioRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/portfolios`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [POST] Move Portfolio Funds
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_moveportfoliofunds
+export function movePortfolioFunds(
+ this: RESTBase,
+ requestParams: MovePortfolioFundsRequest
+): Promise {
+ return this.request({
+ method: method.POST,
+ endpoint: `${API_PREFIX}/portfolios/move_funds`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Portfolio Breakdown
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getportfoliobreakdown
+export function getPortfolioBreakdown(
+ this: RESTBase,
+ { portfolioUuid, ...requestParams }: GetPortfolioBreakdownRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/portfolios/${portfolioUuid}`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [DELETE] Delete Portfolio
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_deleteportfolio
+export function deletePortfolio(
+ this: RESTBase,
+ { portfolioUuid }: DeletePortfolioRequest
+): Promise {
+ return this.request({
+ method: method.DELETE,
+ endpoint: `${API_PREFIX}/portfolios/${portfolioUuid}`,
+ isPublic: false,
+ });
+}
+
+// [PUT] Edit Portfolio
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_editportfolio
+export function editPortfolio(
+ this: RESTBase,
+ { portfolioUuid, ...requestParams }: EditPortfolioRequest
+): Promise {
+ return this.request({
+ method: method.PUT,
+ endpoint: `${API_PREFIX}/portfolios/${portfolioUuid}`,
+ bodyParams: requestParams,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/products.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/products.ts
new file mode 100644
index 0000000000..7cd2ca1f0a
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/products.ts
@@ -0,0 +1,101 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ GetBestBidAskRequest,
+ GetBestBidAskResponse,
+ GetMarketTradesRequest,
+ GetMarketTradesResponse,
+ GetProductBookRequest,
+ GetProductBookResponse,
+ GetProductCandlesRequest,
+ GetProductCandlesResponse,
+ GetProductRequest,
+ GetProductResponse,
+ ListProductsRequest,
+ ListProductsResponse,
+} from './types/products-types';
+import { method } from './types/request-types';
+
+// [GET] Get Best Bid Ask
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getbestbidask
+export function getBestBidAsk(
+ this: RESTBase,
+ requestParams: GetBestBidAskRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/best_bid_ask`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Product Book
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getproductbook
+export function getProductBook(
+ this: RESTBase,
+ requestParams: GetProductBookRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/product_book`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] List Products
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getproducts
+export function listProducts(
+ this: RESTBase,
+ requestParams: ListProductsRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/products`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Product
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getproduct
+export function getProduct(
+ this: RESTBase,
+ { productId, ...requestParams }: GetProductRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/products/${productId}`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Product Candles
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getcandles
+export function getProductCandles(
+ this: RESTBase,
+ { productId, ...requestParams }: GetProductCandlesRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/products/${productId}/candles`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
+
+// [GET] Get Market Trades
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getmarkettrades
+export function getMarketTrades(
+ this: RESTBase,
+ { productId, ...requestParams }: GetMarketTradesRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/products/${productId}/ticker`,
+ queryParams: requestParams,
+ isPublic: false,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/public.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/public.ts
new file mode 100644
index 0000000000..1b9577e514
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/public.ts
@@ -0,0 +1,95 @@
+import { API_PREFIX } from '../constants';
+import { RESTBase } from './rest-base';
+import {
+ GetPublicMarketTradesRequest,
+ GetPublicMarketTradesResponse,
+ GetPublicProductBookRequest,
+ GetPublicProductBookResponse,
+ GetPublicProductCandlesRequest,
+ GetPublicProductCandlesResponse,
+ GetPublicProductRequest,
+ GetPublicProductResponse,
+ GetServerTimeResponse,
+ ListPublicProductsRequest,
+ ListPublicProductsResponse,
+} from './types/public-types';
+import { method } from './types/request-types';
+
+// [GET] Get Server Time
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getservertime
+export function getServerTime(this: RESTBase): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/time`,
+ isPublic: true,
+ });
+}
+
+// [GET] Get Public Product Book
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpublicproductbook
+export function getPublicProductBook(
+ this: RESTBase,
+ requestParams: GetPublicProductBookRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/market/product_book`,
+ queryParams: requestParams,
+ isPublic: true,
+ });
+}
+
+// [GET] List Public Products
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpublicproducts
+export function listPublicProducts(
+ this: RESTBase,
+ requestParams: ListPublicProductsRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/market/products`,
+ queryParams: requestParams,
+ isPublic: true,
+ });
+}
+
+// [GET] Get Public Product
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpublicproduct
+export function getPublicProduct(
+ this: RESTBase,
+ { productId }: GetPublicProductRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/market/products/${productId}`,
+ isPublic: true,
+ });
+}
+
+// [GET] Get Public Product Candles
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpubliccandles
+export function getPublicProductCandles(
+ this: RESTBase,
+ { productId, ...requestParams }: GetPublicProductCandlesRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/market/products/${productId}/candles`,
+ queryParams: requestParams,
+ isPublic: true,
+ });
+}
+
+// [GET] Get Public Market Trades
+// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpublicmarkettrades
+export function getPublicMarketTrades(
+ this: RESTBase,
+ { productId, ...requestParams }: GetPublicMarketTradesRequest
+): Promise {
+ return this.request({
+ method: method.GET,
+ endpoint: `${API_PREFIX}/products/${productId}/ticker`,
+ queryParams: requestParams,
+ isPublic: true,
+ });
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/rest-base.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/rest-base.ts
new file mode 100644
index 0000000000..9084df5612
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/rest-base.ts
@@ -0,0 +1,123 @@
+import { generateToken } from '../jwt-generator';
+import fetch, { Headers, RequestInit, Response } from 'node-fetch';
+import { BASE_URL, USER_AGENT } from '../constants';
+import { RequestOptions } from './types/request-types';
+import { handleException } from './errors';
+
+export class RESTBase {
+ private apiKey: string | undefined;
+ private apiSecret: string | undefined;
+
+ constructor(key?: string, secret?: string) {
+ if (!key || !secret) {
+ console.log('Could not authenticate. Only public endpoints accessible.');
+ }
+ this.apiKey = key;
+ this.apiSecret = secret;
+ }
+
+ request(options: RequestOptions): Promise {
+ const { method, endpoint, isPublic } = options;
+ let { queryParams, bodyParams } = options;
+
+ queryParams = queryParams ? this.filterParams(queryParams) : {};
+
+ if (bodyParams !== undefined)
+ bodyParams = bodyParams ? this.filterParams(bodyParams) : {};
+
+ return this.prepareRequest(
+ method,
+ endpoint,
+ queryParams,
+ bodyParams,
+ isPublic
+ );
+ }
+
+ prepareRequest(
+ httpMethod: string,
+ urlPath: string,
+ queryParams?: Record,
+ bodyParams?: Record,
+ isPublic?: boolean
+ ) {
+ const headers: Headers = this.setHeaders(httpMethod, urlPath, isPublic);
+
+ const requestOptions: RequestInit = {
+ method: httpMethod,
+ headers: headers,
+ body: JSON.stringify(bodyParams),
+ };
+
+ const queryString = this.buildQueryString(queryParams);
+ const url = `https://${BASE_URL}${urlPath}${queryString}`;
+
+ return this.sendRequest(headers, requestOptions, url);
+ }
+
+ async sendRequest(
+ headers: Headers,
+ requestOptions: RequestInit,
+ url: string
+ ) {
+ const response: Response = await fetch(url, requestOptions);
+ const responseText = await response.text();
+ handleException(response, responseText, response.statusText);
+
+ return responseText;
+ }
+
+ setHeaders(httpMethod: string, urlPath: string, isPublic?: boolean) {
+ const headers: Headers = new Headers();
+ headers.append('Content-Type', 'application/json');
+ headers.append('User-Agent', USER_AGENT);
+ if (this.apiKey !== undefined && this.apiSecret !== undefined)
+ headers.append(
+ 'Authorization',
+ `Bearer ${generateToken(
+ httpMethod,
+ urlPath,
+ this.apiKey,
+ this.apiSecret
+ )}`
+ );
+ else if (isPublic == undefined || isPublic == false)
+ throw new Error(
+ 'Attempting to access authenticated endpoint with invalid API_KEY or API_SECRET.'
+ );
+
+ return headers;
+ }
+
+ filterParams(data: Record) {
+ const filteredParams: Record = {};
+
+ for (const key in data) {
+ if (data[key] !== undefined) {
+ filteredParams[key] = data[key];
+ }
+ }
+
+ return filteredParams;
+ }
+
+ buildQueryString(queryParams?: Record): string {
+ if (!queryParams || Object.keys(queryParams).length === 0) {
+ return '';
+ }
+
+ const queryString = Object.entries(queryParams)
+ .flatMap(([key, value]) => {
+ if (Array.isArray(value)) {
+ return value.map(
+ (item) => `${encodeURIComponent(key)}=${encodeURIComponent(item)}`
+ );
+ } else {
+ return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
+ }
+ })
+ .join('&');
+
+ return `?${queryString}`;
+ }
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/accounts-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/accounts-types.ts
new file mode 100644
index 0000000000..ca1301c3f4
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/accounts-types.ts
@@ -0,0 +1,26 @@
+import { Account } from './common-types';
+
+// Get Account
+export type GetAccountRequest = {
+ // Path Params
+ accountUuid: string;
+};
+
+export type GetAccountResponse = {
+ account?: Account;
+};
+
+// List Accounts
+export type ListAccountsRequest = {
+ // Query Params
+ limit?: number;
+ cursor?: string;
+ retailPortfolioId?: string;
+};
+
+export type ListAccountsResponse = {
+ accounts?: Account[];
+ has_next: boolean;
+ cursor?: string;
+ size?: number;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/common-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/common-types.ts
new file mode 100644
index 0000000000..a27d9fb34e
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/common-types.ts
@@ -0,0 +1,447 @@
+// ----- ENUMS -----
+export enum ProductType {
+ UNKNOWN = 'UNKNOWN_PRODUCT_TYPE',
+ SPOT = 'SPOT',
+ FUTURE = 'FUTURE',
+}
+
+export enum ContractExpiryType {
+ UNKNOWN = 'UNKNOWN_CONTRACT_EXPIRY_TYPE',
+ EXPIRING = 'EXPIRING',
+ PERPETUAL = 'PERPETUAL',
+}
+
+export enum ExpiringContractStatus {
+ UNKNOWN = 'UNKNOWN_EXPIRING_CONTRACT_STATUS',
+ UNEXPIRED = 'STATUS_UNEXPIRED',
+ EXPIRED = 'STATUS_EXPIRED',
+ ALL = 'STATUS_ALL',
+}
+
+export enum PortfolioType {
+ UNDEFINED = 'UNDEFINED',
+ DEFAULT = 'DEFAULT',
+ CONSUMER = 'CONSUMER',
+ INTX = 'INTX',
+}
+
+export enum MarginType {
+ CROSS = 'CROSS',
+ ISOLATED = 'ISOLATED',
+}
+
+export enum OrderPlacementSource {
+ UNKNOWN = 'UNKNOWN_PLACEMENT_SOURCE',
+ RETAIL_SIMPLE = 'RETAIL_SIMPLE',
+ RETAIL_ADVANCED = 'RETAIL_ADVANCED',
+}
+
+export enum SortBy {
+ UNKNOWN = 'UNKNOWN_SORT_BY',
+ LIMIT_PRICE = 'LIMIT_PRICE',
+ LAST_FILL_TIME = 'LAST_FILL_TIME',
+}
+
+export enum OrderSide {
+ BUY = 'BUY',
+ SELL = 'SELL',
+}
+
+export enum StopDirection {
+ UP = 'STOP_DIRECTION_STOP_UP',
+ DOWN = 'STOP_DIRECTION_STOP_DOWN',
+}
+
+export enum Granularity {
+ UNKNOWN = 'UNKNOWN_GRANULARITY',
+ ONE_MINUTE = 'ONE_MINUTE',
+ FIVE_MINUTE = 'FIVE_MINUTE',
+ FIFTEEN_MINUTE = 'FIFTEEN_MINUTE',
+ THIRTY_MINUTE = 'THIRTY_MINUTE',
+ ONE_HOUR = 'ONE_HOUR',
+ TWO_HOUR = 'TWO_HOUR',
+ SIX_HOUR = 'SIX_HOUR',
+ ONE_DAY = 'ONE_DAY',
+}
+
+export enum ProductVenue {
+ UNKNOWN = 'UNKNOWN_VENUE_TYPE',
+ CBE = 'CBE',
+ FCM = 'FCM',
+ INTX = 'INTX',
+}
+
+export enum IntradayMarginSetting {
+ UNSPECIFIED = 'INTRADAY_MARGIN_SETTING_UNSPECIFIED',
+ STANDARD = 'INTRADAY_MARGIN_SETTING_STANDARD',
+ INTRADAY = 'INTRADAY_MARGIN_SETTING_INTRADAY',
+}
+
+// ----- TYPES -----
+export type Account = {
+ uuid?: string;
+ name?: string;
+ currency?: string;
+ available_balance?: Record;
+ default?: boolean;
+ active?: boolean;
+ created_at?: string;
+ updated_at?: string;
+ deleted_at?: string;
+ type?: Record;
+ ready?: boolean;
+ hold?: Record;
+ retail_portfolio_id?: string;
+};
+
+export type TradeIncentiveMetadata = {
+ userIncentiveId?: string;
+ codeVal?: string;
+};
+
+export type OrderConfiguration =
+ | { market_market_ioc: MarketMarketIoc }
+ | { sor_limit_ioc: SorLimitIoc }
+ | { limit_limit_gtc: LimitLimitGtc }
+ | { limit_limit_gtd: LimitLimitGtd }
+ | { limit_limit_fok: LimitLimitFok }
+ | { stop_limit_stop_limit_gtc: StopLimitStopLimitGtc }
+ | { stop_limit_stop_limit_gtd: StopLimitStopLimitGtd }
+ | { trigger_bracket_gtc: TriggerBracketGtc }
+ | { trigger_bracket_gtd: TriggerBracketGtd };
+
+export type MarketMarketIoc = { quote_size: string } | { base_size: string };
+
+export type SorLimitIoc = {
+ baseSize: string;
+ limitPrice: string;
+};
+
+export type LimitLimitGtc = {
+ baseSize: string;
+ limitPrice: string;
+ postOnly: boolean;
+};
+
+export type LimitLimitGtd = {
+ baseSize: string;
+ limitPrice: string;
+ endTime: string;
+ postOnly: boolean;
+};
+
+export type LimitLimitFok = {
+ baseSize: string;
+ limitPrice: string;
+};
+
+export type StopLimitStopLimitGtc = {
+ baseSize: string;
+ limitPrice: string;
+ stopPrice: string;
+ stopDirection: StopDirection;
+};
+
+export type StopLimitStopLimitGtd = {
+ baseSize: string;
+ limitPrice: string;
+ stopPrice: string;
+ endTime: string;
+ stopDirection: StopDirection;
+};
+
+export type TriggerBracketGtc = {
+ baseSize: string;
+ limitPrice: string;
+ stopTriggerPrice: string;
+};
+
+export type TriggerBracketGtd = {
+ baseSize: string;
+ limitPrice: string;
+ stopTriggerPrice: string;
+ endTime: string;
+};
+
+export type RatConvertTrade = {
+ id?: string;
+ status?: Record;
+ user_entered_amount?: Record;
+ amount?: Record;
+ subtotal?: Record;
+ total?: Record;
+ fees?: Record;
+ total_fee?: Record;
+ source?: Record;
+ target?: Record;
+ unit_price?: Record;
+ user_warnings?: Record;
+ user_reference?: string;
+ source_curency?: string;
+ cancellation_reason?: Record;
+ source_id?: string;
+ target_id?: string;
+ subscription_info?: Record;
+ exchange_rate?: Record;
+ tax_details?: Record;
+ trade_incentive_info?: Record;
+ total_fee_without_tax?: Record;
+ fiat_denoted_total?: Record;
+};
+
+export type FCMBalanceSummary = {
+ futures_buying_power?: Record;
+ total_usd_balance?: Record;
+ cbi_usd_balance?: Record;
+ cfm_usd_balance?: Record;
+ total_open_orders_hold_amount?: Record;
+ unrealized_pnl?: Record;
+ daily_realized_pnl?: Record;
+ initial_margin?: Record;
+ available_margin?: Record;
+ liquidation_threshold?: Record;
+ liquidation_buffer_amount?: Record;
+ liquidation_buffer_percentage?: string;
+ intraday_margin_window_measure?: Record;
+ overnight_margin_window_measure?: Record;
+};
+
+export type FCMPosition = {
+ product_id?: string;
+ expiration_time?: Record;
+ side?: Record;
+ number_of_contracts?: string;
+ current_price?: string;
+ avg_entry_price?: string;
+ unrealized_pnl?: string;
+ daily_realized_pnl?: string;
+};
+
+export type FCMSweep = {
+ id: string;
+ requested_amount: Record;
+ should_sweep_all: boolean;
+ status: Record;
+ schedule_time: Record;
+};
+
+export type CancelOrderObject = {
+ success: boolean;
+ failure_reason: Record;
+ order_id: string;
+};
+
+export type Order = {
+ order_id: string;
+ product_id: string;
+ user_id: string;
+ order_configuration: OrderConfiguration;
+ side: OrderSide;
+ client_order_id: string;
+ status: Record;
+ time_in_force?: Record;
+ created_time: Record;
+ completion_percentage: string;
+ filled_size?: string;
+ average_filled_price: string;
+ fee?: string;
+ number_of_fills: string;
+ filled_value?: string;
+ pending_cancel: boolean;
+ size_in_quote: boolean;
+ total_fees: string;
+ size_inclusive_of_fees: boolean;
+ total_value_after_fees: string;
+ trigger_status?: Record;
+ order_type?: Record;
+ reject_reason?: Record;
+ settled?: boolean;
+ product_type?: ProductType;
+ reject_message?: string;
+ cancel_message?: string;
+ order_placement_source?: OrderPlacementSource;
+ outstanding_hold_amount?: string;
+ is_liquidation?: boolean;
+ last_fill_time?: Record;
+ edit_history?: Record[];
+ leverage?: string;
+ margin_type?: MarginType;
+ retail_portfolio_id?: string;
+ originating_order_id?: string;
+ attached_order_id?: string;
+};
+
+export type PaymentMethod = {
+ id?: string;
+ type?: string;
+ name?: string;
+ currency?: string;
+ verified?: boolean;
+ allow_buy?: boolean;
+ allow_sell?: boolean;
+ allow_deposit?: boolean;
+ allow_withdraw?: boolean;
+ created_at?: string;
+ updated_at?: string;
+};
+
+export type PerpetualPortfolio = {
+ portfolio_uuid?: string;
+ collateral?: string;
+ position_notional?: string;
+ open_position_notional?: string;
+ pending_fees?: string;
+ borrow?: string;
+ accrued_interest?: string;
+ rolling_debt?: string;
+ portfolio_initial_margin?: string;
+ portfolio_im_notional?: Record;
+ liquidation_percentage?: string;
+ liquidation_buffer?: string;
+ margin_type?: Record;
+ margin_flags?: Record;
+ liquidation_status?: Record;
+ unrealized_pnl?: Record;
+ total_balance?: Record;
+};
+
+export type PortfolioSummary = {
+ unrealized_pnl?: Record;
+ buying_power?: Record;
+ total_balance?: Record;
+ max_withdrawal_amount?: Record;
+};
+
+export type PositionSummary = {
+ aggregated_pnl?: Record;
+};
+
+export type Position = {
+ product_id?: string;
+ product_uuid?: string;
+ portfolio_uuid?: string;
+ symbol?: string;
+ vwap?: Record;
+ entry_vwap?: Record;
+ position_side?: Record;
+ margin_type?: Record;
+ net_size?: string;
+ buy_order_size?: string;
+ sell_order_size?: string;
+ im_contribution?: string;
+ unrealized_pnl?: Record;
+ mark_price?: Record;
+ liquidation_price?: Record;
+ leverage?: string;
+ im_notional?: Record;
+ mm_notional?: Record;
+ position_notional?: Record;
+ aggregated_pnl?: Record;
+};
+
+export type Balance = {
+ asset: Record;
+ quantity: string;
+ hold: string;
+ transfer_hold: string;
+ collateral_value: string;
+ collateral_weight: string;
+ max_withdraw_amount: string;
+ loan: string;
+ loan_collateral_requirement_usd: string;
+ pledged_quantity: string;
+};
+
+export type Portfolio = {
+ name?: string;
+ uuid?: string;
+ type?: string;
+};
+
+export type PortfolioBreakdown = {
+ portfolio?: Portfolio;
+ portfolio_balances?: Record;
+ spot_positions?: Record[];
+ perp_positions?: Record[];
+ futures_positions?: Record[];
+};
+
+export type PriceBook = {
+ product_id: string;
+ bids: Record[];
+ asks: Record[];
+ time?: Record;
+};
+
+export type Products = {
+ products?: Product[];
+ num_products?: number;
+};
+
+export type Product = {
+ product_id: string;
+ price: string;
+ price_percentage_change_24h: string;
+ volume_24h: string;
+ volume_percentage_change_24h: string;
+ base_increment: string;
+ quote_increment: string;
+ quote_min_size: string;
+ quote_max_size: string;
+ base_min_size: string;
+ base_max_size: string;
+ base_name: string;
+ quote_name: string;
+ watched: boolean;
+ is_disabled: boolean;
+ new: boolean;
+ status: string;
+ cancel_only: boolean;
+ limit_only: boolean;
+ post_only: boolean;
+ trading_disabled: boolean;
+ auction_mode: boolean;
+ product_type?: ProductType;
+ quote_currency_id?: string;
+ base_currency_id?: string;
+ fcm_trading_session_details?: Record;
+ mid_market_price?: string;
+ alias?: string;
+ alias_to?: string[];
+ base_display_symbol: string;
+ quote_display_symbol?: string;
+ view_only?: boolean;
+ price_increment?: string;
+ display_name?: string;
+ product_venue?: ProductVenue;
+ approximate_quote_24h_volume?: string;
+ future_product_details?: Record;
+};
+
+export type Candles = {
+ candles?: Candle[];
+};
+
+export type Candle = {
+ start?: string;
+ low?: string;
+ high?: string;
+ open?: string;
+ close?: string;
+ volume?: string;
+};
+
+export type HistoricalMarketTrade = {
+ trade_id?: string;
+ product_id?: string;
+ price?: string;
+ size?: string;
+ time?: string;
+ side?: OrderSide;
+};
+
+export type PortfolioBalance = {
+ portfolio_uuid?: string;
+ balances?: Balance[];
+ is_margin_limit_reached?: boolean;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/converts-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/converts-types.ts
new file mode 100644
index 0000000000..8724b89d19
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/converts-types.ts
@@ -0,0 +1,42 @@
+// Create Convert Quote
+import { RatConvertTrade, TradeIncentiveMetadata } from './common-types';
+
+export type CreateConvertQuoteRequest = {
+ // Body Params
+ fromAccount: string;
+ toAccount: string;
+ amount: string;
+ tradeIncentiveMetadata?: TradeIncentiveMetadata;
+};
+
+export type CreateConvertQuoteResponse = {
+ trade?: RatConvertTrade;
+};
+
+// Get Convert Trade
+export type GetConvertTradeRequest = {
+ // Path Params
+ tradeId: string;
+
+ //Query Params
+ fromAccount: string;
+ toAccount: string;
+};
+
+export type GetConvertTradeResponse = {
+ trade?: RatConvertTrade;
+};
+
+// Commit Convert Trade
+export type CommitConvertTradeRequest = {
+ // Path Params
+ tradeId: string;
+
+ // Body Params
+ fromAccount: string;
+ toAccount: string;
+};
+
+export type CommitConvertTradeResponse = {
+ trade?: RatConvertTrade;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/dataAPI-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/dataAPI-types.ts
new file mode 100644
index 0000000000..7a773a0e8f
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/dataAPI-types.ts
@@ -0,0 +1,10 @@
+import { PortfolioType } from './common-types';
+
+// Get API Key Permissions
+export type GetAPIKeyPermissionsResponse = {
+ can_view?: boolean;
+ can_trade?: boolean;
+ can_transfer?: boolean;
+ portfolio_uuid?: string;
+ portfolio_type?: PortfolioType;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/fees-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/fees-types.ts
new file mode 100644
index 0000000000..e9db653b37
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/fees-types.ts
@@ -0,0 +1,23 @@
+import { ContractExpiryType, ProductType, ProductVenue } from './common-types';
+
+// Get Transactions Summary
+export type GetTransactionsSummaryRequest = {
+ // Query Params
+ productType?: ProductType;
+ contractExpiryType?: ContractExpiryType;
+ productVenue?: ProductVenue;
+};
+
+export type GetTransactionsSummaryResponse = {
+ total_volume: number;
+ total_fees: number;
+ fee_tier: Record;
+ margin_rate?: Record;
+ goods_and_services_tax?: Record;
+ advanced_trade_only_volumes?: number;
+ advanced_trade_only_fees?: number;
+ coinbase_pro_volume?: number; // deprecated
+ coinbase_pro_fees?: number; // deprecated
+ total_balance?: string;
+ has_promo_fee?: boolean;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/futures-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/futures-types.ts
new file mode 100644
index 0000000000..61bbf97b1d
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/futures-types.ts
@@ -0,0 +1,71 @@
+import {
+ FCMBalanceSummary,
+ FCMPosition,
+ FCMSweep,
+ IntradayMarginSetting,
+} from './common-types';
+
+// Get Futures Balance Summary
+export type GetFuturesBalanceSummaryResponse = {
+ balance_summary?: FCMBalanceSummary;
+};
+
+// Get Intraday Margin Setting
+export type GetIntradayMarginSettingResponse = {
+ setting?: IntradayMarginSetting;
+};
+
+// Set Intraday Margin Setting
+export type SetIntradayMarginSettingRequest = {
+ // Body Params
+ setting?: IntradayMarginSetting;
+};
+
+export type SetIntradayMarginSettingResponse = Record;
+
+// Get Current Margin Window
+export type GetCurrentMarginWindowRequest = {
+ // Query Params
+ marginProfileType?: string;
+};
+
+export type GetCurrentMarginWindowResponse = {
+ margin_window?: Record;
+ is_intraday_margin_killswitch_enabled?: boolean;
+ is_intraday_margin_enrollment_killswitch_enabled?: boolean;
+};
+
+// List Futures Positions
+export type ListFuturesPositionsResponse = {
+ positions?: FCMPosition[];
+};
+
+// Get Futures Position
+export type GetFuturesPositionRequest = {
+ // Path Params
+ productId: string;
+};
+
+export type GetFuturesPositionResponse = {
+ position?: FCMPosition;
+};
+
+// Schedule Futures Sweep
+export type ScheduleFuturesSweepRequest = {
+ // Body Params
+ usdAmount?: string;
+};
+
+export type ScheduleFuturesSweepResponse = {
+ success?: boolean;
+};
+
+// List Futures Sweeps
+export type ListFuturesSweepsResponse = {
+ sweeps: FCMSweep[];
+};
+
+// Cancel Pending Futures Sweep = {
+export type CancelPendingFuturesSweep = {
+ success?: boolean;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/orders-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/orders-types.ts
new file mode 100644
index 0000000000..486d93cd16
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/orders-types.ts
@@ -0,0 +1,185 @@
+import {
+ CancelOrderObject,
+ ContractExpiryType,
+ MarginType,
+ Order,
+ OrderConfiguration,
+ OrderPlacementSource,
+ OrderSide,
+ ProductType,
+ SortBy,
+} from './common-types';
+
+// Create Order
+export type CreateOrderRequest = {
+ // Body Params
+ clientOrderId: string;
+ productId: string;
+ side: OrderSide;
+ orderConfiguration: OrderConfiguration;
+ selfTradePreventionId?: string;
+ leverage?: string;
+ marginType?: MarginType;
+ retailPortfolioId?: string;
+};
+
+export type CreateOrderResponse = {
+ success: boolean;
+ failure_reason?: Record; // deprecated
+ order_id?: string; // deprecated
+ response?:
+ | { success_response: Record }
+ | { error_response: Record };
+ order_configuration?: OrderConfiguration;
+};
+
+// Cancel Orders
+export type CancelOrdersRequest = {
+ // Body Params
+ orderIds: string[];
+};
+
+export type CancelOrdersResponse = {
+ results?: CancelOrderObject[];
+};
+
+// Edit Order
+export type EditOrderRequest = {
+ // Body Params
+ orderId: string;
+ price?: string;
+ size?: string;
+};
+
+export type EditOrderResponse = {
+ success: boolean;
+ response?:
+ | { success_response: Record } // deprecated
+ | { error_response: Record }; // deprecated
+ errors?: Record[];
+};
+
+// Edit Order Preview
+export type EditOrderPreviewRequest = {
+ // Body Params
+ orderId: string;
+ price?: string;
+ size?: string;
+};
+
+export type EditOrderPreviewResponse = {
+ errors: Record[];
+ slippage?: string;
+ order_total?: string;
+ commission_total?: string;
+ quote_size?: string;
+ base_size?: string;
+ best_bid?: string;
+ average_filled_price?: string;
+};
+
+// List Orders
+export type ListOrdersRequest = {
+ // Query Params
+ orderIds?: string[];
+ productIds?: string[];
+ orderStatus?: string[];
+ limit?: number;
+ startDate?: string;
+ endDate?: string;
+ orderType?: string;
+ orderSide?: OrderSide;
+ cursor?: string;
+ productType?: ProductType;
+ orderPlacementSource?: OrderPlacementSource;
+ contractExpiryType?: ContractExpiryType;
+ assetFilters?: string[];
+ retailPortfolioId?: string;
+ timeInForces?: string;
+ sortBy?: SortBy;
+};
+
+export type ListOrdersResponse = {
+ orders: Order[];
+ sequence?: number; // deprecated
+ has_next: boolean;
+ cursor?: string;
+};
+
+// List Fills
+export type ListFillsRequest = {
+ // Query Params
+ orderIds?: string[];
+ tradeIds?: string[];
+ productIds?: string[];
+ startSequenceTimestamp?: string;
+ endSequenceTimestamp?: string;
+ retailPortfolioId?: string;
+ limit?: number;
+ cursor?: string;
+ sortBy?: SortBy;
+};
+
+export type ListFillsResponse = {
+ fills?: Record[];
+ cursor?: string;
+};
+
+// Get Order
+export type GetOrderRequest = {
+ // Path Params
+ orderId: string;
+};
+
+export type GetOrderResponse = {
+ order?: Order;
+};
+
+// Preview Order
+export type PreviewOrderRequest = {
+ // Body Params
+ productId: string;
+ side: OrderSide;
+ orderConfiguration: OrderConfiguration;
+ leverage?: string;
+ marginType?: MarginType;
+ retailPortfolioId?: string;
+};
+
+export type PreviewOrderResponse = {
+ order_total: string;
+ commission_total: string;
+ errs: Record[];
+ warning: Record[];
+ quote_size: string;
+ base_size: string;
+ best_bid: string;
+ best_ask: string;
+ is_max: boolean;
+ order_margin_total?: string;
+ leverage?: string;
+ long_leverage?: string;
+ short_leverage?: string;
+ slippage?: string;
+ preview_id?: string;
+ current_liquidation_buffer?: string;
+ projected_liquidation_buffer?: string;
+ max_leverage?: string;
+ pnl_configuration?: Record;
+};
+
+// Close Position
+export type ClosePositionRequest = {
+ // Body Params
+ clientOrderId: string;
+ productId: string;
+ size?: string;
+};
+
+export type ClosePositionResponse = {
+ success: boolean;
+ response?:
+ | { success_response: Record }
+ | { error_response: Record };
+ order_configuration?: OrderConfiguration;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/payments-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/payments-types.ts
new file mode 100644
index 0000000000..064046f85a
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/payments-types.ts
@@ -0,0 +1,16 @@
+import { PaymentMethod } from './common-types';
+
+// List Payment Methods
+export type ListPaymentMethodsResponse = {
+ paymentMethods?: PaymentMethod;
+};
+
+// Get Payment Method
+export type GetPaymentMethodRequest = {
+ // Path Params
+ paymentMethodId: string;
+};
+
+export type GetPaymentMethodResponse = {
+ paymentMethod?: PaymentMethod;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/perpetuals-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/perpetuals-types.ts
new file mode 100644
index 0000000000..d1c1235a1e
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/perpetuals-types.ts
@@ -0,0 +1,72 @@
+import {
+ PerpetualPortfolio,
+ PortfolioBalance,
+ PortfolioSummary,
+ Position,
+ PositionSummary,
+} from './common-types';
+
+// Allocate Portfolio
+export type AllocatePortfolioRequest = {
+ // Body Params
+ portfolioUuid: string;
+ symbol: string;
+ amount: string;
+ currency: string;
+};
+
+export type AllocatePortfolioResponse = Record;
+
+// Get Perpetuals Portfolio Summary
+export type GetPerpetualsPortfolioSummaryRequest = {
+ // Path Params
+ portfolioUuid: string;
+};
+
+export type GetPerpetualsPortfolioSummaryResponse = {
+ portfolios?: PerpetualPortfolio[];
+ summary?: PortfolioSummary;
+};
+
+// List Perpetuals Positions
+export type ListPerpetualsPositionsRequest = {
+ // Path Params
+ portfolioUuid: string;
+};
+
+export type ListPerpetualsPositionsResponse = {
+ positions?: Position[];
+ summary?: PositionSummary;
+};
+
+// Get Perpetuals Position
+export type GetPerpetualsPositionRequest = {
+ // Path Params
+ portfolioUuid: string;
+ symbol: string;
+};
+
+export type GetPerpetualsPositionResponse = {
+ position?: Position;
+};
+
+// Get Portfolio Balances
+export type GetPortfolioBalancesRequest = {
+ // Path Params
+ portfolioUuid: string;
+};
+
+export type GetPortfolioBalancesResponse = {
+ portfolio_balancces?: PortfolioBalance[];
+};
+
+// Opt In or Out of Multi Asset Collateral
+export type OptInOutMultiAssetCollateralRequest = {
+ // Body Params
+ portfolioUuid?: string;
+ multiAssetCollateralEnabled?: boolean;
+};
+
+export type OptInOutMultiAssetCollateralResponse = {
+ cross_collateral_enabled?: boolean;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/portfolios-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/portfolios-types.ts
new file mode 100644
index 0000000000..e26698da0c
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/portfolios-types.ts
@@ -0,0 +1,68 @@
+import { Portfolio, PortfolioBreakdown, PortfolioType } from './common-types';
+
+// List Portfolios
+export type ListPortfoliosRequest = {
+ // Query Params
+ portfolioType?: PortfolioType;
+};
+
+export type ListPortfoliosResponse = {
+ portfolios?: Portfolio[];
+};
+
+// Create Portfolio
+export type CreatePortfolioRequest = {
+ // Body Params
+ name: string;
+};
+
+export type CreatePortfolioResponse = {
+ portfolio?: Portfolio;
+};
+
+// Move Portfolio Funds
+export type MovePortfolioFundsRequest = {
+ // Body Params
+ funds: Record;
+ sourcePortfolioUuid: string;
+ targetPortfolioUuid: string;
+};
+
+export type MovePortfolioFundsResponse = {
+ source_portfolio_uuid?: string;
+ target_portfolio_uuid?: string;
+};
+
+// Get Portfolio Breakdown
+export type GetPortfolioBreakdownRequest = {
+ // Path Params
+ portfolioUuid: string;
+
+ // Query Params
+ currency?: string;
+};
+
+export type GetPortfolioBreakdownResponse = {
+ breakdown?: PortfolioBreakdown;
+};
+
+// Delete Portfolio
+export type DeletePortfolioRequest = {
+ // Path Params
+ portfolioUuid: string;
+};
+
+export type DeletePortfolioResponse = Record;
+
+// Edit Portfolio
+export type EditPortfolioRequest = {
+ // Path Params
+ portfolioUuid: string;
+
+ // Body Params
+ name: string;
+};
+
+export type EditPortfolioResponse = {
+ portfolio?: Portfolio;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/products-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/products-types.ts
new file mode 100644
index 0000000000..9713e617df
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/products-types.ts
@@ -0,0 +1,96 @@
+import {
+ Candles,
+ ContractExpiryType,
+ ExpiringContractStatus,
+ Granularity,
+ HistoricalMarketTrade,
+ PriceBook,
+ Product,
+ Products,
+ ProductType,
+} from './common-types';
+
+// Get Best Bid Ask
+export type GetBestBidAskRequest = {
+ // Query Params
+ productIds?: string[];
+};
+
+export type GetBestBidAskResponse = {
+ pricebooks: PriceBook[];
+};
+
+// Get Product Book
+export type GetProductBookRequest = {
+ // Query Params
+ productId: string;
+ limit?: number;
+ aggregationPriceIncrement?: number;
+};
+
+export type GetProductBookResponse = {
+ pricebook: PriceBook;
+};
+
+// List Products
+export type ListProductsRequest = {
+ // Query Params
+ limit?: number;
+ offset?: number;
+ productType?: ProductType;
+ productIds?: string[];
+ contractExpiryType?: ContractExpiryType;
+ expiringContractStatus?: ExpiringContractStatus;
+ getTradabilityStatus?: boolean;
+ getAllProducts?: boolean;
+};
+
+export type ListProductsResponse = {
+ body?: Products;
+};
+
+// Get Product
+export type GetProductRequest = {
+ // Path Params
+ productId: string;
+
+ // Query Params
+ getTradabilityStatus?: boolean;
+};
+
+export type GetProductResponse = {
+ body?: Product;
+};
+
+// Get Product Candles
+export type GetProductCandlesRequest = {
+ // Path Params
+ productId: string;
+
+ // Query Params
+ start: string;
+ end: string;
+ granularity: Granularity;
+ limit?: number;
+};
+
+export type GetProductCandlesResponse = {
+ body?: Candles;
+};
+
+// Get Market Trades
+export type GetMarketTradesRequest = {
+ // Path Params
+ productId: string;
+
+ // Query Params
+ limit: number;
+ start?: string;
+ end?: string;
+};
+
+export type GetMarketTradesResponse = {
+ trades?: HistoricalMarketTrade[];
+ best_bid?: string;
+ best_ask?: string;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/public-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/public-types.ts
new file mode 100644
index 0000000000..4f5bbe4a4e
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/public-types.ts
@@ -0,0 +1,88 @@
+import {
+ Candles,
+ ContractExpiryType,
+ ExpiringContractStatus,
+ HistoricalMarketTrade,
+ PriceBook,
+ Product,
+ Products,
+ ProductType,
+} from './common-types';
+
+// Get Server Time
+export type GetServerTimeResponse = {
+ iso?: string;
+ epochSeconds?: number;
+ epochMillis?: number;
+};
+
+// Get Public Product Book
+export type GetPublicProductBookRequest = {
+ // Query Params
+ productId: string;
+ limit?: number;
+ aggregationPriceIncrement?: number;
+};
+
+export type GetPublicProductBookResponse = {
+ pricebook: PriceBook;
+};
+
+// List Public Products
+export type ListPublicProductsRequest = {
+ // Query Params
+ limit?: number;
+ offset?: number;
+ productType?: ProductType;
+ productIds?: string[];
+ contractExpiryType?: ContractExpiryType;
+ expiringContractStatus?: ExpiringContractStatus;
+ getAllProducts?: boolean;
+};
+
+export type ListPublicProductsResponse = {
+ body?: Products;
+};
+
+// Get Public Product
+export type GetPublicProductRequest = {
+ // Path Params
+ productId: string;
+};
+
+export type GetPublicProductResponse = {
+ body?: Product;
+};
+
+//Get Public Product Candles
+export type GetPublicProductCandlesRequest = {
+ // Path Params
+ productId: string;
+
+ // Query Params
+ start: string;
+ end: string;
+ granularity: string;
+ limit?: number;
+};
+
+export type GetPublicProductCandlesResponse = {
+ body?: Candles;
+};
+
+// Get Public Market Trades
+export type GetPublicMarketTradesRequest = {
+ // Path Params
+ productId: string;
+
+ // Query Params
+ limit: number;
+ start?: string;
+ end?: string;
+};
+
+export type GetPublicMarketTradesResponse = {
+ trades?: HistoricalMarketTrade[];
+ best_bid?: string;
+ best_ask?: string;
+};
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/request-types.ts b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/request-types.ts
new file mode 100644
index 0000000000..56c7a926a9
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/src/rest/types/request-types.ts
@@ -0,0 +1,14 @@
+export enum method {
+ GET = 'GET',
+ POST = 'POST',
+ PUT = 'PUT',
+ DELETE = 'DELETE',
+}
+
+export interface RequestOptions {
+ method: method;
+ endpoint: string;
+ queryParams?: Record;
+ bodyParams?: Record;
+ isPublic: boolean;
+}
diff --git a/packages/plugin-coinbase/advanced-sdk-ts/tsconfig.json b/packages/plugin-coinbase/advanced-sdk-ts/tsconfig.json
new file mode 100644
index 0000000000..9a8f3ef63b
--- /dev/null
+++ b/packages/plugin-coinbase/advanced-sdk-ts/tsconfig.json
@@ -0,0 +1,106 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+ /* Language and Environment */
+ "target": "es6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+ /* Modules */
+ "module": "commonjs" /* Specify what module code is generated. */,
+ "rootDir": "./src" /* Specify the root folder within your source files. */,
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ "outDir": "./dist" /* Specify an output folder for all emitted files. */,
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
+ /* Type Checking */
+ "strict": true /* Enable all strict type-checking options. */,
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ "include": [
+ "src/**/*.ts"
+ ], // Include all .ts files in the src directory and subdirectories
+ "exclude": [
+ "node_modules"
+ ]
+}
\ No newline at end of file
diff --git a/packages/plugin-coinbase/package.json b/packages/plugin-coinbase/package.json
index 492ae8e6d8..01b4bd3a1a 100644
--- a/packages/plugin-coinbase/package.json
+++ b/packages/plugin-coinbase/package.json
@@ -6,10 +6,15 @@
"types": "dist/index.d.ts",
"dependencies": {
"@ai16z/eliza": "workspace:*",
- "coinbase-api": "1.0.5"
+ "coinbase-api": "1.0.5",
+ "coinbase-advanced-sdk": "file:../../packages/plugin-coinbase/advanced-sdk-ts",
+ "jsonwebtoken": "^9.0.2",
+ "@types/jsonwebtoken": "^9.0.7",
+ "node-fetch": "^2.6.1"
},
"devDependencies": {
- "tsup": "8.3.5"
+ "tsup": "8.3.5",
+ "@types/node": "^20.0.0"
},
"scripts": {
"build": "tsup --format esm --dts",
diff --git a/packages/plugin-coinbase/src/constants.ts b/packages/plugin-coinbase/src/constants.ts
new file mode 100644
index 0000000000..1524455822
--- /dev/null
+++ b/packages/plugin-coinbase/src/constants.ts
@@ -0,0 +1,224 @@
+export const ABI = [
+ {
+ inputs: [],
+ name: "name",
+ outputs: [
+ {
+ name: "",
+ type: "string",
+ internalType: "string"
+ }
+ ],
+ stateMutability: "view",
+ type: "function"
+ },
+ {
+ inputs: [
+ {
+ name: "spender",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ name: "amount",
+ type: "uint256",
+ internalType: "uint256"
+ }
+ ],
+ name: "approve",
+ outputs: [
+ {
+ name: "",
+ type: "bool",
+ internalType: "bool"
+ }
+ ],
+ stateMutability: "nonpayable",
+ type: "function"
+ },
+ {
+ inputs: [],
+ name: "totalSupply",
+ outputs: [
+ {
+ name: "",
+ type: "uint256",
+ internalType: "uint256"
+ }
+ ],
+ stateMutability: "view",
+ type: "function"
+ },
+ {
+ inputs: [
+ {
+ name: "from",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ name: "to",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ name: "amount",
+ type: "uint256",
+ internalType: "uint256"
+ }
+ ],
+ name: "transferFrom",
+ outputs: [
+ {
+ name: "",
+ type: "bool",
+ internalType: "bool"
+ }
+ ],
+ stateMutability: "nonpayable",
+ type: "function"
+ },
+ {
+ inputs: [],
+ name: "decimals",
+ outputs: [
+ {
+ name: "",
+ type: "uint8",
+ internalType: "uint8"
+ }
+ ],
+ stateMutability: "view",
+ type: "function"
+ },
+ {
+ inputs: [
+ {
+ name: "account",
+ type: "address",
+ internalType: "address"
+ }
+ ],
+ name: "balanceOf",
+ outputs: [
+ {
+ name: "",
+ type: "uint256",
+ internalType: "uint256"
+ }
+ ],
+ stateMutability: "view",
+ type: "function"
+ },
+ {
+ inputs: [],
+ name: "symbol",
+ outputs: [
+ {
+ name: "",
+ type: "string",
+ internalType: "string"
+ }
+ ],
+ stateMutability: "view",
+ type: "function"
+ },
+ {
+ inputs: [
+ {
+ name: "to",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ name: "amount",
+ type: "uint256",
+ internalType: "uint256"
+ }
+ ],
+ name: "transfer",
+ outputs: [
+ {
+ name: "",
+ type: "bool",
+ internalType: "bool"
+ }
+ ],
+ stateMutability: "nonpayable",
+ type: "function"
+ },
+ {
+ inputs: [
+ {
+ name: "owner",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ name: "spender",
+ type: "address",
+ internalType: "address"
+ }
+ ],
+ name: "allowance",
+ outputs: [
+ {
+ name: "",
+ type: "uint256",
+ internalType: "uint256"
+ }
+ ],
+ stateMutability: "view",
+ type: "function"
+ },
+ {
+ inputs: [
+ {
+ indexed: true,
+ name: "owner",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ indexed: true,
+ name: "spender",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ indexed: false,
+ name: "value",
+ type: "uint256",
+ internalType: "uint256"
+ }
+ ],
+ name: "Approval",
+ type: "event",
+ anonymous: false
+ },
+ {
+ inputs: [
+ {
+ indexed: true,
+ name: "from",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ indexed: true,
+ name: "to",
+ type: "address",
+ internalType: "address"
+ },
+ {
+ indexed: false,
+ name: "value",
+ type: "uint256",
+ internalType: "uint256"
+ }
+ ],
+ name: "Transfer",
+ type: "event",
+ anonymous: false
+ }
+];
\ No newline at end of file
diff --git a/packages/plugin-coinbase/src/index.ts b/packages/plugin-coinbase/src/index.ts
index 280f8f4b71..69d4c83826 100644
--- a/packages/plugin-coinbase/src/index.ts
+++ b/packages/plugin-coinbase/src/index.ts
@@ -3,6 +3,7 @@ import { coinbaseCommercePlugin } from "./plugins/commerce";
import { tradePlugin } from "./plugins/trade";
import { tokenContractPlugin } from "./plugins/tokenContract";
import { webhookPlugin } from "./plugins/webhooks";
+import { advancedTradePlugin } from "./plugins/advancedTrade";
export const plugins = {
coinbaseMassPaymentsPlugin,
@@ -10,6 +11,7 @@ export const plugins = {
tradePlugin,
tokenContractPlugin,
webhookPlugin,
+ advancedTradePlugin,
};
export * from "./plugins/massPayments";
@@ -17,3 +19,4 @@ export * from "./plugins/commerce";
export * from "./plugins/trade";
export * from "./plugins/tokenContract";
export * from "./plugins/webhooks";
+export * from "./plugins/advancedTrade";
diff --git a/packages/plugin-coinbase/src/plugins/advancedTrade.ts b/packages/plugin-coinbase/src/plugins/advancedTrade.ts
new file mode 100644
index 0000000000..472616e2d1
--- /dev/null
+++ b/packages/plugin-coinbase/src/plugins/advancedTrade.ts
@@ -0,0 +1,372 @@
+import { RESTClient } from '../../advanced-sdk-ts/src/rest';
+import {
+ Action,
+ Plugin,
+ elizaLogger,
+ IAgentRuntime,
+ Memory,
+ HandlerCallback,
+ State,
+ composeContext,
+ generateObjectV2,
+ ModelClass,
+ Provider,
+} from "@ai16z/eliza";
+import { advancedTradeTemplate } from "../templates";
+import { isAdvancedTradeContent, AdvancedTradeSchema } from "../types";
+import { readFile } from "fs/promises";
+import { parse } from "csv-parse/sync";
+import path from "path";
+import { fileURLToPath } from "url";
+import fs from "fs";
+import { createArrayCsvWriter } from "csv-writer";
+import { OrderSide, OrderConfiguration } from '../../advanced-sdk-ts/src/rest/types/common-types';
+import { CreateOrderResponse } from '../../advanced-sdk-ts/src/rest/types/orders-types';
+
+// File path setup remains the same
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const baseDir = path.resolve(__dirname, "../../plugin-coinbase/src/plugins");
+const tradeCsvFilePath = path.join(baseDir, "advanced_trades.csv");
+
+const tradeProvider: Provider = {
+ get: async (runtime: IAgentRuntime, _message: Memory) => {
+ try {
+ const client = new RESTClient(
+ runtime.getSetting("COINBASE_API_KEY") ?? process.env.COINBASE_API_KEY,
+ runtime.getSetting("COINBASE_PRIVATE_KEY") ?? process.env.COINBASE_PRIVATE_KEY
+ );
+
+ // Get accounts and products information
+ let accounts, products;
+ try {
+ accounts = await client.listAccounts({});
+ } catch (error) {
+ elizaLogger.error("Error fetching accounts:", error);
+ return [];
+ }
+
+ try {
+ products = await client.listProducts({});
+ } catch (error) {
+ elizaLogger.error("Error fetching products:", error);
+ return [];
+ }
+
+ // Read CSV file logic remains the same
+ if (!fs.existsSync(tradeCsvFilePath)) {
+ const csvWriter = createArrayCsvWriter({
+ path: tradeCsvFilePath,
+ header: [
+ "Order ID",
+ "Success",
+ "Order Configuration",
+ "Response",
+ ],
+ });
+ await csvWriter.writeRecords([]);
+ }
+
+ let csvData, records;
+ try {
+ csvData = await readFile(tradeCsvFilePath, "utf-8");
+ } catch (error) {
+ elizaLogger.error("Error reading CSV file:", error);
+ return [];
+ }
+
+ try {
+ records = parse(csvData, {
+ columns: true,
+ skip_empty_lines: true,
+ });
+ } catch (error) {
+ elizaLogger.error("Error parsing CSV data:", error);
+ return [];
+ }
+
+ return {
+ accounts: accounts.accounts,
+ products: products.products,
+ trades: records
+ };
+ } catch (error) {
+ elizaLogger.error("Error in tradeProvider:", error);
+ return [];
+ }
+ },
+};
+
+export async function appendTradeToCsv(tradeResult: any) {
+ try {
+ const csvWriter = createArrayCsvWriter({
+ path: tradeCsvFilePath,
+ header: [
+ "Order ID",
+ "Success",
+ "Order Configuration",
+ "Response",
+ ],
+ append: true,
+ });
+ elizaLogger.info("Trade result:", tradeResult);
+
+
+ // Format trade data based on success/failure
+ const formattedTrade = [
+ tradeResult.success_response?.order_id || tradeResult.failure_response?.order_id || '',
+ tradeResult.success,
+ // JSON.stringify(tradeResult.order_configuration || {}),
+ // JSON.stringify(tradeResult.success_response || tradeResult.failure_response || {})
+ ];
+
+ elizaLogger.info("Formatted trade for CSV:", formattedTrade);
+ await csvWriter.writeRecords([formattedTrade]);
+ elizaLogger.info("Trade written to CSV successfully");
+ } catch (error) {
+ elizaLogger.error("Error writing trade to CSV:", error);
+ // Log the actual error for debugging
+ if (error instanceof Error) {
+ elizaLogger.error("Error details:", error.message);
+ }
+ }
+}
+
+async function hasEnoughBalance(client: RESTClient, currency: string, amount: number, side: string): Promise {
+ try {
+ const response = await client.listAccounts({});
+ const accounts = JSON.parse(response);
+ elizaLogger.info("Accounts:", accounts);
+ const checkCurrency = side === "BUY" ? "USD" : currency;
+ elizaLogger.info(`Checking balance for ${side} order of ${amount} ${checkCurrency}`);
+
+ // Find account with exact currency match
+ const account = accounts?.accounts.find(acc =>
+ acc.currency === checkCurrency &&
+ (checkCurrency === "USD" ? acc.type === "ACCOUNT_TYPE_FIAT" : acc.type === "ACCOUNT_TYPE_CRYPTO")
+ );
+
+ if (!account) {
+ elizaLogger.error(`No ${checkCurrency} account found`);
+ return false;
+ }
+
+ const available = parseFloat(account.available_balance.value);
+ // Add buffer for fees only on USD purchases
+ const requiredAmount = side === "BUY" ? amount * 1.01 : amount;
+ elizaLogger.info(`Required amount (including buffer): ${requiredAmount} ${checkCurrency}`);
+
+ const hasBalance = available >= requiredAmount;
+ elizaLogger.info(`Has sufficient balance: ${hasBalance}`);
+
+ return hasBalance;
+ } catch (error) {
+ elizaLogger.error("Balance check failed with error:", {
+ error: error instanceof Error ? error.message : 'Unknown error',
+ currency,
+ amount,
+ side
+ });
+ return false;
+ }
+}
+
+export const executeAdvancedTradeAction: Action = {
+ name: "EXECUTE_ADVANCED_TRADE",
+ description: "Execute a trade using Coinbase Advanced Trading API",
+ validate: async (runtime: IAgentRuntime) => {
+ return !!(runtime.getSetting("COINBASE_API_KEY") || process.env.COINBASE_API_KEY) &&
+ !!(runtime.getSetting("COINBASE_PRIVATE_KEY") || process.env.COINBASE_PRIVATE_KEY);
+ },
+ similes: [
+ "EXECUTE_ADVANCED_TRADE",
+ "ADVANCED_MARKET_ORDER",
+ "ADVANCED_LIMIT_ORDER",
+ "COINBASE_PRO_TRADE",
+ "PROFESSIONAL_TRADE"
+ ],
+ handler: async (
+ runtime: IAgentRuntime,
+ _message: Memory,
+ state: State,
+ _options: any,
+ callback: HandlerCallback
+ ) => {
+ let client: RESTClient;
+
+ // Initialize client
+ try {
+ client = new RESTClient(
+ runtime.getSetting("COINBASE_API_KEY") ?? process.env.COINBASE_API_KEY,
+ runtime.getSetting("COINBASE_PRIVATE_KEY") ?? process.env.COINBASE_PRIVATE_KEY
+ );
+ elizaLogger.info("Advanced trade client initialized");
+ } catch (error) {
+ elizaLogger.error("Client initialization failed:", error);
+ callback({
+ text: "Failed to initialize trading client. Please check your API credentials."
+ }, []);
+ return;
+ }
+
+ // Generate trade details
+ let tradeDetails;
+ try {
+ tradeDetails = await generateObjectV2({
+ runtime,
+ context: composeContext({ state, template: advancedTradeTemplate }),
+ modelClass: ModelClass.LARGE,
+ schema: AdvancedTradeSchema,
+ });
+ elizaLogger.info("Trade details generated:", tradeDetails.object);
+ } catch (error) {
+ elizaLogger.error("Trade details generation failed:", error);
+ callback({
+ text: "Failed to generate trade details. Please provide valid trading parameters."
+ }, []);
+ return;
+ }
+
+ // Validate trade content
+ if (!isAdvancedTradeContent(tradeDetails.object)) {
+ elizaLogger.error("Invalid trade content:", tradeDetails.object);
+ callback({
+ text: "Invalid trade details. Please check your input parameters."
+ }, []);
+ return;
+ }
+
+ const { productId, amount, side, orderType, limitPrice } = tradeDetails.object;
+
+ // Configure order
+ let orderConfiguration: OrderConfiguration;
+ try {
+ if (orderType === "MARKET") {
+ orderConfiguration = side === "BUY" ? {
+ market_market_ioc: {
+ quote_size: amount.toString()
+ }
+ } : {
+ market_market_ioc: {
+ base_size: amount.toString()
+ }
+ };
+ } else {
+ if (!limitPrice) {
+ throw new Error("Limit price is required for limit orders");
+ }
+ orderConfiguration = {
+ limit_limit_gtc: {
+ baseSize: amount.toString(),
+ limitPrice: limitPrice.toString(),
+ postOnly: false
+ }
+ };
+ }
+ elizaLogger.info("Order configuration created:", orderConfiguration);
+ } catch (error) {
+ elizaLogger.error("Order configuration failed:", error);
+ callback({
+ text: error instanceof Error ? error.message : "Failed to configure order parameters."
+ }, []);
+ return;
+ }
+
+ // Execute trade
+ let order: CreateOrderResponse;
+ try {
+ if (!(await hasEnoughBalance(client, productId.split('-')[0], amount, side))) {
+ callback({
+ text: `Insufficient ${side === "BUY" ? "USD" : productId.split('-')[0]} balance to execute this trade`
+ }, []);
+ return;
+ }
+
+ order = await client.createOrder({
+ clientOrderId: crypto.randomUUID(),
+ productId,
+ side: side === "BUY" ? OrderSide.BUY : OrderSide.SELL,
+ orderConfiguration
+ });
+
+ elizaLogger.info("Trade executed successfully:", order);
+ } catch (error) {
+ elizaLogger.error("Trade execution failed:", error?.message);
+ callback({
+ text: `Failed to execute trade: ${error instanceof Error ? error.message : "Unknown error occurred"}`
+ }, []);
+ return;
+ }
+ // Log trade to CSV
+ try {
+ // await appendTradeToCsv(order);
+ elizaLogger.info("Trade logged to CSV");
+ } catch (csvError) {
+ elizaLogger.warn("Failed to log trade to CSV:", csvError);
+ // Continue execution as this is non-critical
+ }
+
+ callback({
+ text: `Advanced Trade executed successfully:
+- Product: ${productId}
+- Type: ${orderType} Order
+- Side: ${side}
+- Amount: ${amount}
+- ${orderType === "LIMIT" ? `- Limit Price: ${limitPrice}\n` : ""}- Order ID: ${order.order_id}
+- Status: ${order.success}
+- Order Id: ${order.order_id}
+- Response: ${JSON.stringify(order.response)}
+- Order Configuration: ${JSON.stringify(order.order_configuration)}`
+ }, []);
+ },
+ examples: [
+ [
+ {
+ user: "{{user1}}",
+ content: { text: "Place an advanced market order to buy $1 worth of BTC" }
+ },
+ {
+ user: "{{agentName}}",
+ content: {
+ text: `Advanced Trade executed successfully:
+- Product: BTC-USD
+- Type: Market Order
+- Side: BUY
+- Amount: 1000
+- Order ID: CB-ADV-12345
+- Success: true
+- Response: {"success_response":{}}
+- Order Configuration: {"market_market_ioc":{"quote_size":"1000"}}`
+ }
+ }
+ ],
+ [
+ {
+ user: "{{user1}}",
+ content: { text: "Set a limit order to sell 0.5 ETH at $2000" }
+ },
+ {
+ user: "{{agentName}}",
+ content: {
+ text: `Advanced Trade executed successfully:
+- Product: ETH-USD
+- Type: Limit Order
+- Side: SELL
+- Amount: 0.5
+- Limit Price: 2000
+- Order ID: CB-ADV-67890
+- Success: true
+- Response: {"success_response":{}}
+- Order Configuration: {"limit_limit_gtc":{"baseSize":"0.5","limitPrice":"2000","postOnly":false}}`
+ }
+ }
+ ]
+ ]
+};
+
+export const advancedTradePlugin: Plugin = {
+ name: "advancedTradePlugin",
+ description: "Enables advanced trading using Coinbase Advanced Trading API",
+ actions: [executeAdvancedTradeAction],
+ providers: [tradeProvider],
+};
\ No newline at end of file
diff --git a/packages/plugin-coinbase/src/plugins/commerce.ts b/packages/plugin-coinbase/src/plugins/commerce.ts
index c89b48c430..95d651df6d 100644
--- a/packages/plugin-coinbase/src/plugins/commerce.ts
+++ b/packages/plugin-coinbase/src/plugins/commerce.ts
@@ -506,8 +506,14 @@ export const chargeProvider: Provider = {
elizaLogger.log("Current Balances:", balances);
elizaLogger.log("Last Transactions:", transactions);
}
- elizaLogger.log("Charges:", charges);
- return { charges: charges.data, balances, transactions };
+ const formattedCharges = charges.map(charge => ({
+ id: charge.id,
+ name: charge.name,
+ description: charge.description,
+ pricing: charge.pricing,
+ }));
+ elizaLogger.log("Charges:", formattedCharges);
+ return { charges: formattedCharges, balances, transactions };
},
};
diff --git a/packages/plugin-coinbase/src/plugins/tokenContract.ts b/packages/plugin-coinbase/src/plugins/tokenContract.ts
index f5a0612c56..5eaf10b632 100644
--- a/packages/plugin-coinbase/src/plugins/tokenContract.ts
+++ b/packages/plugin-coinbase/src/plugins/tokenContract.ts
@@ -1,4 +1,4 @@
-import { Coinbase, SmartContract } from "@coinbase/coinbase-sdk";
+import { Coinbase, readContract, SmartContract } from "@coinbase/coinbase-sdk";
import {
Action,
Plugin,
@@ -15,17 +15,21 @@ import { initializeWallet } from "../utils";
import {
contractInvocationTemplate,
tokenContractTemplate,
+ readContractTemplate,
} from "../templates";
import {
ContractInvocationSchema,
TokenContractSchema,
isContractInvocationContent,
isTokenContractContent,
+ ReadContractSchema,
+ isReadContractContent,
} from "../types";
import path from "path";
import { fileURLToPath } from "url";
import { createArrayCsvWriter } from "csv-writer";
import fs from "fs";
+import { ABI } from "../constants";
// Dynamically resolve the file path to the src/plugins directory
const __filename = fileURLToPath(import.meta.url);
@@ -33,6 +37,22 @@ const __dirname = path.dirname(__filename);
const baseDir = path.resolve(__dirname, "../../plugin-coinbase/src/plugins");
const contractsCsvFilePath = path.join(baseDir, "contracts.csv");
+// Add this helper at the top level
+const serializeBigInt = (value: any): any => {
+ if (typeof value === 'bigint') {
+ return value.toString();
+ }
+ if (Array.isArray(value)) {
+ return value.map(serializeBigInt);
+ }
+ if (typeof value === 'object' && value !== null) {
+ return Object.fromEntries(
+ Object.entries(value).map(([k, v]) => [k, serializeBigInt(v)])
+ );
+ }
+ return value;
+};
+
export const deployTokenContractAction: Action = {
name: "DEPLOY_TOKEN_CONTRACT",
description:
@@ -320,239 +340,21 @@ export const invokeContractAction: Action = {
return;
}
- const { contractAddress, method, args, amount, assetId, network } =
+ const { contractAddress, method, args, amount, assetId, networkId } =
invocationDetails.object;
-
- const wallet = await initializeWallet(runtime, network);
+ const wallet = await initializeWallet(runtime, networkId);
// Prepare invocation options
const invocationOptions = {
contractAddress,
method,
- abi: [
- {
- constant: true,
- inputs: [],
- name: "name",
- outputs: [
- {
- name: "",
- type: "string",
- },
- ],
- payable: false,
- stateMutability: "view",
- type: "function",
- },
- {
- constant: false,
- inputs: [
- {
- name: "_spender",
- type: "address",
- },
- {
- name: "_value",
- type: "uint256",
- },
- ],
- name: "approve",
- outputs: [
- {
- name: "",
- type: "bool",
- },
- ],
- payable: false,
- stateMutability: "nonpayable",
- type: "function",
- },
- {
- constant: true,
- inputs: [],
- name: "totalSupply",
- outputs: [
- {
- name: "",
- type: "uint256",
- },
- ],
- payable: false,
- stateMutability: "view",
- type: "function",
- },
- {
- constant: false,
- inputs: [
- {
- name: "_from",
- type: "address",
- },
- {
- name: "_to",
- type: "address",
- },
- {
- name: "_value",
- type: "uint256",
- },
- ],
- name: "transferFrom",
- outputs: [
- {
- name: "",
- type: "bool",
- },
- ],
- payable: false,
- stateMutability: "nonpayable",
- type: "function",
- },
- {
- constant: true,
- inputs: [],
- name: "decimals",
- outputs: [
- {
- name: "",
- type: "uint8",
- },
- ],
- payable: false,
- stateMutability: "view",
- type: "function",
- },
- {
- constant: true,
- inputs: [
- {
- name: "_owner",
- type: "address",
- },
- ],
- name: "balanceOf",
- outputs: [
- {
- name: "balance",
- type: "uint256",
- },
- ],
- payable: false,
- stateMutability: "view",
- type: "function",
- },
- {
- constant: true,
- inputs: [],
- name: "symbol",
- outputs: [
- {
- name: "",
- type: "string",
- },
- ],
- payable: false,
- stateMutability: "view",
- type: "function",
- },
- {
- constant: false,
- inputs: [
- {
- name: "_to",
- type: "address",
- },
- {
- name: "_value",
- type: "uint256",
- },
- ],
- name: "transfer",
- outputs: [
- {
- name: "",
- type: "bool",
- },
- ],
- payable: false,
- stateMutability: "nonpayable",
- type: "function",
- },
- {
- constant: true,
- inputs: [
- {
- name: "_owner",
- type: "address",
- },
- {
- name: "_spender",
- type: "address",
- },
- ],
- name: "allowance",
- outputs: [
- {
- name: "",
- type: "uint256",
- },
- ],
- payable: false,
- stateMutability: "view",
- type: "function",
- },
- {
- payable: true,
- stateMutability: "payable",
- type: "fallback",
- },
- {
- anonymous: false,
- inputs: [
- {
- indexed: true,
- name: "owner",
- type: "address",
- },
- {
- indexed: true,
- name: "spender",
- type: "address",
- },
- {
- indexed: false,
- name: "value",
- type: "uint256",
- },
- ],
- name: "Approval",
- type: "event",
- },
- {
- anonymous: false,
- inputs: [
- {
- indexed: true,
- name: "from",
- type: "address",
- },
- {
- indexed: true,
- name: "to",
- type: "address",
- },
- {
- indexed: false,
- name: "value",
- type: "uint256",
- },
- ],
- name: "Transfer",
- type: "event",
- },
- ],
- args,
- ...(amount && assetId ? { amount, assetId } : {}),
+ abi: ABI,
+ args: {
+ ...args,
+ amount: args.amount || amount // Ensure amount is passed in args
+ },
+ networkId,
+ assetId
};
elizaLogger.log("Invocation options:", invocationOptions);
// Invoke the contract
@@ -580,7 +382,7 @@ export const invokeContractAction: Action = {
[
contractAddress,
method,
- network,
+ networkId,
invocation.getStatus(),
invocation.getTransactionLink() || "",
amount || "",
@@ -593,7 +395,7 @@ export const invokeContractAction: Action = {
text: `Contract method invoked successfully:
- Contract Address: ${contractAddress}
- Method: ${method}
-- Network: ${network}
+- Network: ${networkId}
- Status: ${invocation.getStatus()}
- Transaction URL: ${invocation.getTransactionLink() || "N/A"}
${amount ? `- Amount: ${amount}` : ""}
@@ -618,7 +420,7 @@ Contract invocation has been logged to the CSV file.`,
{
user: "{{user1}}",
content: {
- text: " Call the 'transfer' method on my ERC20 token contract at 0x37f2131ebbc8f97717edc3456879ef56b9f4b97b with amount 100 to recepient 0xbcF7C64B880FA89a015970dC104E848d485f99A3",
+ text: "Call the 'transfer' method on my ERC20 token contract at 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 with amount 100 to recepient 0xbcF7C64B880FA89a015970dC104E848d485f99A3",
},
},
{
@@ -641,9 +443,112 @@ Contract invocation has been logged to the CSV file.`,
similes: ["CALL_CONTRACT", "EXECUTE_CONTRACT", "INTERACT_WITH_CONTRACT"],
};
+export const readContractAction: Action = {
+ name: "READ_CONTRACT",
+ description: "Read data from a deployed smart contract using the Coinbase SDK",
+ validate: async (runtime: IAgentRuntime, _message: Memory) => {
+ elizaLogger.log("Validating runtime for READ_CONTRACT...");
+ return !!(
+ runtime.character.settings.secrets?.COINBASE_API_KEY ||
+ process.env.COINBASE_API_KEY
+ ) && !!(
+ runtime.character.settings.secrets?.COINBASE_PRIVATE_KEY ||
+ process.env.COINBASE_PRIVATE_KEY
+ );
+ },
+ handler: async (
+ runtime: IAgentRuntime,
+ _message: Memory,
+ state: State,
+ _options: any,
+ callback: HandlerCallback
+ ) => {
+ elizaLogger.log("Starting READ_CONTRACT handler...");
+
+ try {
+ Coinbase.configure({
+ apiKeyName: runtime.getSetting("COINBASE_API_KEY") ?? process.env.COINBASE_API_KEY,
+ privateKey: runtime.getSetting("COINBASE_PRIVATE_KEY") ?? process.env.COINBASE_PRIVATE_KEY,
+ });
+
+ const context = composeContext({
+ state,
+ template: readContractTemplate,
+ });
+
+ const readDetails = await generateObjectV2({
+ runtime,
+ context,
+ modelClass: ModelClass.SMALL,
+ schema: ReadContractSchema,
+ });
+
+ if (!isReadContractContent(readDetails.object)) {
+ callback(
+ {
+ text: "Invalid contract read details. Please check the inputs.",
+ },
+ []
+ );
+ return;
+ }
+
+ const { contractAddress, method, args, networkId, abi } = readDetails.object;
+ elizaLogger.log("Reading contract:", { contractAddress, method, args, networkId, abi });
+
+ const result = await readContract({
+ networkId,
+ contractAddress,
+ method,
+ args,
+ abi: ABI as any,
+ });
+
+ // Serialize the result before using it
+ const serializedResult = serializeBigInt(result);
+
+ elizaLogger.info("Contract read result:", serializedResult);
+
+ callback({
+ text: `Contract read successful:
+- Contract Address: ${contractAddress}
+- Method: ${method}
+- Network: ${networkId}
+- Result: ${JSON.stringify(serializedResult, null, 2)}`
+ }, []);
+
+ } catch (error) {
+ elizaLogger.error("Error reading contract:", error);
+ callback({
+ text: `Failed to read contract: ${error instanceof Error ? error.message : "Unknown error"}`
+ }, []);
+ }
+ },
+ examples: [
+ [
+ {
+ user: "{{user1}}",
+ content: {
+ text: "Read the balance of address 0xbcF7C64B880FA89a015970dC104E848d485f99A3 from the ERC20 contract at 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 on eth",
+ },
+ },
+ {
+ user: "{{agentName}}",
+ content: {
+ text: `Contract read successful:
+- Contract Address: 0x37f2131ebbc8f97717edc3456879ef56b9f4b97b
+- Method: balanceOf
+- Network: eth
+- Result: "1000000"`,
+ },
+ },
+ ],
+ ],
+ similes: ["READ_CONTRACT", "GET_CONTRACT_DATA", "QUERY_CONTRACT"],
+};
+
export const tokenContractPlugin: Plugin = {
name: "tokenContract",
- description:
- "Enables deployment and invoking of ERC20, ERC721, and ERC1155 token contracts using the Coinbase SDK",
- actions: [deployTokenContractAction],
+ description: "Enables deployment, invocation, and reading of ERC20, ERC721, and ERC1155 token contracts using the Coinbase SDK",
+ actions: [deployTokenContractAction, invokeContractAction, readContractAction],
};
diff --git a/packages/plugin-coinbase/src/plugins/webhooks.ts b/packages/plugin-coinbase/src/plugins/webhooks.ts
index 5a722833d9..33be86c496 100644
--- a/packages/plugin-coinbase/src/plugins/webhooks.ts
+++ b/packages/plugin-coinbase/src/plugins/webhooks.ts
@@ -15,6 +15,7 @@ import {
import { WebhookSchema, isWebhookContent, WebhookContent } from "../types";
import { webhookTemplate } from "../templates";
import { appendWebhooksToCsv } from "../utils";
+
export const webhookProvider: Provider = {
get: async (runtime: IAgentRuntime, _message: Memory) => {
try {
@@ -95,7 +96,7 @@ export const createWebhookAction: Action = {
const webhookDetails = await generateObjectV2({
runtime,
context,
- modelClass: ModelClass.SMALL,
+ modelClass: ModelClass.LARGE,
schema: WebhookSchema,
});
diff --git a/packages/plugin-coinbase/src/templates.ts b/packages/plugin-coinbase/src/templates.ts
index a5546a1492..7dbf26d1f5 100644
--- a/packages/plugin-coinbase/src/templates.ts
+++ b/packages/plugin-coinbase/src/templates.ts
@@ -84,11 +84,13 @@ Extract the following details for processing a trade using the Coinbase SDK:
- **amount** (number): The amount to trade.
- **sourceAsset** (string): The asset ID to trade from (must be one of: ETH, SOL, USDC, WETH, GWEI, LAMPORT).
- **targetAsset** (string): The asset ID to trade to (must be one of: ETH, SOL, USDC, WETH, GWEI, LAMPORT).
+- **side** (string): The side of the trade (must be either "BUY" or "SELL").
Ensure that:
1. **network** is one of the supported networks: "base", "sol", "eth", "arb", or "pol".
2. **sourceAsset** and **targetAsset** are valid assets from the provided list.
3. **amount** is a positive number.
+4. **side** is either "BUY" or "SELL".
Provide the details in the following JSON format:
@@ -97,7 +99,8 @@ Provide the details in the following JSON format:
"network": "",
"amount": ,
"sourceAsset": "",
- "targetAsset": ""
+ "targetAsset": "",
+ "side": ""
}
\`\`\`
@@ -105,6 +108,38 @@ Here are the recent user messages for context:
{{recentMessages}}
`;
+export const advancedTradeTemplate = `
+Extract the following details for processing an advanced trade using the Coinbase Advanced Trading API:
+- **productId** (string): The trading pair ID (e.g., "BTC-USD", "ETH-USD", "SOL-USD")
+- **side** (string): The side of the trade (must be either "BUY" or "SELL")
+- **amount** (number): The amount to trade
+- **orderType** (string): The type of order (must be either "MARKET" or "LIMIT")
+- **limitPrice** (number, optional): The limit price for limit orders
+
+Ensure that:
+1. **productId** follows the format "ASSET-USD" (e.g., "BTC-USD")
+2. **side** is either "BUY" or "SELL"
+3. **amount** is a positive number
+4. **orderType** is either "MARKET" or "LIMIT"
+5. **limitPrice** is provided when orderType is "LIMIT"
+
+Provide the details in the following JSON format:
+
+\`\`\`json
+{
+ "productId": "",
+ "side": "",
+ "amount": ,
+ "orderType": "",
+ "limitPrice":
+}
+\`\`\`
+
+Here are the recent user messages for context:
+{{recentMessages}}
+`;
+
+
export const tokenContractTemplate = `
Extract the following details for deploying a token contract using the Coinbase SDK:
- **contractType** (string): The type of token contract to deploy (ERC20, ERC721, or ERC1155)
@@ -138,9 +173,19 @@ Extract the following details for invoking a smart contract using the Coinbase S
- **method** (string): The method to invoke on the contract
- **abi** (array): The ABI of the contract
- **args** (object, optional): The arguments to pass to the contract method
-- **amount** (number, optional): The amount of the asset to send to a payable contract method
-- **assetId** (string, optional): The ID of the asset to send to a payable contract method
-- **network** (string): The blockchain network to use (e.g., base, eth, arb, pol)
+- **amount** (string, optional): The amount of the asset to send (as string to handle large numbers)
+- **assetId** (string, required): The ID of the asset to send (e.g., 'USDC')
+- **networkId** (string, required): The network ID to use in format "chain-network".
+ static networks: {
+ readonly BaseSepolia: "base-sepolia";
+ readonly BaseMainnet: "base-mainnet";
+ readonly EthereumHolesky: "ethereum-holesky";
+ readonly EthereumMainnet: "ethereum-mainnet";
+ readonly PolygonMainnet: "polygon-mainnet";
+ readonly SolanaDevnet: "solana-devnet";
+ readonly SolanaMainnet: "solana-mainnet";
+ readonly ArbitrumMainnet: "arbitrum-mainnet";
+ };
Provide the details in the following JSON format:
@@ -152,17 +197,17 @@ Provide the details in the following JSON format:
"args": {
"": ""
},
- "amount": ,
+ "amount": "",
"assetId": "",
- "network": ""
+ "networkId": ""
}
\`\`\`
-Example for invoking a transfer method on an ERC20 token contract:
+Example for invoking a transfer method on the USDC contract:
\`\`\`json
{
- "contractAddress": "0x37f2131ebbc8f97717edc3456879ef56b9f4b97b",
+ "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"method": "transfer",
"abi": [
{
@@ -173,7 +218,7 @@ Example for invoking a transfer method on an ERC20 token contract:
"type": "address"
},
{
- "name": "value",
+ "name": "amount",
"type": "uint256"
}
],
@@ -190,10 +235,11 @@ Example for invoking a transfer method on an ERC20 token contract:
}
],
"args": {
- "to": "0xRecipientAddressHere",
- "value": 1000
+ "to": "0xbcF7C64B880FA89a015970dC104E848d485f99A3",
+ "amount": "1000000" // 1 USDC (6 decimals)
},
- "network": "eth"
+ "networkId": "ethereum-mainnet",
+ "assetId": "USDC"
}
\`\`\`
@@ -285,6 +331,58 @@ Example for creating a webhook on the Sepolia testnet for ERC20 transfers origin
});
\`\`\`
+Here are the recent user messages for context:
+{{recentMessages}}
+`;
+
+export const readContractTemplate = `
+Extract the following details for reading from a smart contract using the Coinbase SDK:
+- **contractAddress** (string): The address of the contract to read from (must start with 0x)
+- **method** (string): The view/pure method to call on the contract
+- **networkId** (string): The network ID based on networks configured in Coinbase SDK
+Allowed values are:
+ static networks: {
+ readonly BaseSepolia: "base-sepolia";
+ readonly BaseMainnet: "base-mainnet";
+ readonly EthereumHolesky: "ethereum-holesky";
+ readonly EthereumMainnet: "ethereum-mainnet";
+ readonly PolygonMainnet: "polygon-mainnet";
+ readonly SolanaDevnet: "solana-devnet";
+ readonly SolanaMainnet: "solana-mainnet";
+ readonly ArbitrumMainnet: "arbitrum-mainnet";
+ };
+- **args** (object): The arguments to pass to the contract method
+- **abi** (array, optional): The contract ABI if needed for complex interactions
+
+Provide the details in the following JSON format:
+
+\`\`\`json
+{
+ "contractAddress": "<0x-prefixed-address>",
+ "method": "",
+ "networkId": "",
+ "args": {
+ "": ""
+ },
+ "abi": [
+ // Optional ABI array
+ ]
+}
+\`\`\`
+
+Example for reading the balance of an ERC20 token:
+
+\`\`\`json
+{
+ "contractAddress": "0x37f2131ebbc8f97717edc3456879ef56b9f4b97b",
+ "method": "balanceOf",
+ "networkId": "eth-mainnet",
+ "args": {
+ "account": "0xbcF7C64B880FA89a015970dC104E848d485f99A3"
+ }
+}
+\`\`\`
+
Here are the recent user messages for context:
{{recentMessages}}
`;
\ No newline at end of file
diff --git a/packages/plugin-coinbase/src/types.ts b/packages/plugin-coinbase/src/types.ts
index 5e070c1be8..5644fa9040 100644
--- a/packages/plugin-coinbase/src/types.ts
+++ b/packages/plugin-coinbase/src/types.ts
@@ -59,7 +59,7 @@ export const TradeSchema = z.object({
amount: z.number(),
sourceAsset: z.enum(assetValues),
targetAsset: z.enum(assetValues),
- leverage: z.number().optional(), // Optional leverage for leveraged trades
+ side: z.enum(["BUY", "SELL"]),
});
export interface TradeContent {
@@ -67,6 +67,8 @@ export interface TradeContent {
amount: number;
sourceAsset: string;
targetAsset: string;
+ side: "BUY" | "SELL";
+
}
export const isTradeContent = (object: any): object is TradeContent => {
@@ -122,9 +124,9 @@ export interface ContractInvocationContent {
method: string;
abi: any[];
args?: Record;
- amount?: number;
- assetId?: string;
- network: string;
+ amount?: string;
+ assetId: string;
+ networkId: string;
}
export const ContractInvocationSchema = z.object({
@@ -132,9 +134,9 @@ export const ContractInvocationSchema = z.object({
method: z.string().describe("The method to invoke on the contract"),
abi: z.array(z.any()).describe("The ABI of the contract"),
args: z.record(z.string(), z.any()).optional().describe("The arguments to pass to the contract method"),
- amount: z.number().optional().describe("The amount of the asset to send to a payable contract method"),
- assetId: z.string().optional().describe("The ID of the asset to send to a payable contract method"),
- network: z.string().describe("The blockchain network to use"),
+ amount: z.string().optional().describe("The amount of the asset to send (as string to handle large numbers)"),
+ assetId: z.string().describe("The ID of the asset to send (e.g., 'USDC')"),
+ networkId: z.string().describe("The network ID to use (e.g., 'ethereum-mainnet')")
});
export const isContractInvocationContent = (obj: any): obj is ContractInvocationContent => {
@@ -153,4 +155,44 @@ export type WebhookContent = z.infer;
export const isWebhookContent = (object: any): object is WebhookContent => {
return WebhookSchema.safeParse(object).success;
+};
+
+export const AdvancedTradeSchema = z.object({
+ productId: z.string(),
+ side: z.enum(["BUY", "SELL"]),
+ amount: z.number(),
+ orderType: z.enum(["MARKET", "LIMIT"]),
+ limitPrice: z.number().optional(),
+});
+
+export interface AdvancedTradeContent {
+ productId: string;
+ side: "BUY" | "SELL";
+ amount: number;
+ orderType: "MARKET" | "LIMIT";
+ limitPrice?: number;
+}
+
+export const isAdvancedTradeContent = (object: any): object is AdvancedTradeContent => {
+ return AdvancedTradeSchema.safeParse(object).success;
+};
+
+export interface ReadContractContent {
+ contractAddress: `0x${string}`;
+ method: string;
+ networkId: string;
+ args: Record;
+ abi?: any[];
+}
+
+export const ReadContractSchema = z.object({
+ contractAddress: z.string().describe("The address of the contract to read from"),
+ method: z.string().describe("The view/pure method to call on the contract"),
+ networkId: z.string().describe("The network ID to use"),
+ args: z.record(z.string(), z.any()).describe("The arguments to pass to the contract method"),
+ abi: z.array(z.any()).optional().describe("The contract ABI (optional)")
+});
+
+export const isReadContractContent = (obj: any): obj is ReadContractContent => {
+ return ReadContractSchema.safeParse(obj).success;
};
\ No newline at end of file
diff --git a/packages/plugin-coinbase/src/utils.ts b/packages/plugin-coinbase/src/utils.ts
index 9b96db423f..c3e801a45f 100644
--- a/packages/plugin-coinbase/src/utils.ts
+++ b/packages/plugin-coinbase/src/utils.ts
@@ -356,9 +356,8 @@ export async function getWalletDetails(
}));
// Fetch the wallet's recent transactions
- const walletAddress = await wallet.getDefaultAddress();
- const transactions = await walletAddress.listTransactions();
- const transactionsData = transactions?.data ?? [];
+
+ const transactionsData = [];
const formattedTransactions = transactionsData.map((transaction) => {
const content = transaction.content();
return {
diff --git a/packages/plugin-coinbase/tsconfig.json b/packages/plugin-coinbase/tsconfig.json
index 834c4dce26..4751d6174f 100644
--- a/packages/plugin-coinbase/tsconfig.json
+++ b/packages/plugin-coinbase/tsconfig.json
@@ -2,12 +2,17 @@
"extends": "../core/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
- "rootDir": "src",
+ "rootDir": ".",
+ "rootDirs": [
+ "src",
+ "advanced-sdk-ts"
+ ],
"types": [
"node"
]
},
"include": [
- "src/**/*.ts"
+ "src/**/*.ts",
+ "advanced-sdk-ts/src/**/*.ts",
]
}
\ No newline at end of file
diff --git a/packages/plugin-coinbase/tsup.config.ts b/packages/plugin-coinbase/tsup.config.ts
index 91b5946d0a..42a69c634f 100644
--- a/packages/plugin-coinbase/tsup.config.ts
+++ b/packages/plugin-coinbase/tsup.config.ts
@@ -5,22 +5,44 @@ export default defineConfig({
outDir: "dist",
sourcemap: true,
clean: true,
- format: ["esm"], // Ensure you're targeting CommonJS
+ format: ["cjs", "esm"],
+ dts: true,
+ splitting: false,
+ bundle: true,
+ minify: false,
external: [
- "dotenv", // Externalize dotenv to prevent bundling
- "fs", // Externalize fs to use Node.js built-in module
- "path", // Externalize other built-ins if necessary
+ "@coinbase/coinbase-sdk",
+ "form-data",
+ "combined-stream",
+ "axios",
+ "util",
+ "stream",
+ "http",
+ "https",
+ "events",
+ "crypto",
+ "buffer",
+ "url",
+ "zlib",
+ "querystring",
+ "os",
"@reflink/reflink",
"@node-llama-cpp",
- "https",
- "http",
"agentkeepalive",
- "@coinbase/coinbase-sdk",
"fs/promises",
"csv-writer",
"csv-parse/sync",
- "path",
- "url",
- // Add other modules you want to externalize
+ "dotenv",
+ "coinbase-advanced-sdk",
+ "advanced-sdk-ts",
+ "jsonwebtoken",
+ "whatwg-url"
],
+ platform: 'node',
+ target: 'node18',
+ esbuildOptions(options) {
+ options.bundle = true;
+ options.platform = 'node';
+ options.target = 'node18';
+ }
});
diff --git a/packages/plugin-evm/src/actions/bridge.ts b/packages/plugin-evm/src/actions/bridge.ts
index 3d0a38582d..4d92018e96 100644
--- a/packages/plugin-evm/src/actions/bridge.ts
+++ b/packages/plugin-evm/src/actions/bridge.ts
@@ -6,7 +6,8 @@ import {
ExtendedChain,
getRoutes,
} from "@lifi/sdk";
-import { getChainConfigs, WalletProvider } from "../providers/wallet";
+import { WalletProvider } from "../providers/wallet";
+import { getChainConfigs } from "../providers/chainConfigs";
import { bridgeTemplate } from "../templates";
import type { BridgeParams, Transaction } from "../types";
diff --git a/packages/plugin-evm/src/actions/swap.ts b/packages/plugin-evm/src/actions/swap.ts
index 4bc2308094..3b22916cb3 100644
--- a/packages/plugin-evm/src/actions/swap.ts
+++ b/packages/plugin-evm/src/actions/swap.ts
@@ -6,7 +6,8 @@ import {
ExtendedChain,
getRoutes,
} from "@lifi/sdk";
-import { getChainConfigs, WalletProvider } from "../providers/wallet";
+import { WalletProvider } from "../providers/wallet";
+import { getChainConfigs } from "../providers/chainConfigs";
import { swapTemplate } from "../templates";
import type { SwapParams, Transaction } from "../types";
diff --git a/packages/plugin-evm/src/providers/chainConfigs.ts b/packages/plugin-evm/src/providers/chainConfigs.ts
new file mode 100644
index 0000000000..a410c56b96
--- /dev/null
+++ b/packages/plugin-evm/src/providers/chainConfigs.ts
@@ -0,0 +1,339 @@
+import {
+ mainnet,
+ base,
+ sepolia,
+ bsc,
+ arbitrum,
+ avalanche,
+ polygon,
+ optimism,
+ cronos,
+ gnosis,
+ fantom,
+ klaytn,
+ celo,
+ moonbeam,
+ aurora,
+ harmonyOne,
+ moonriver,
+ arbitrumNova,
+ mantle,
+ linea,
+ scroll,
+ filecoin,
+ taiko,
+ zksync,
+ canto,
+} from "viem/chains";
+import type { ChainMetadata, SupportedChain } from "../types";
+import type { IAgentRuntime } from "@ai16z/eliza";
+
+export const DEFAULT_CHAIN_CONFIGS: Record = {
+ ethereum: {
+ chainId: 1,
+ name: "Ethereum",
+ chain: mainnet,
+ rpcUrl: "https://eth.llamarpc.com",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://etherscan.io",
+ },
+ base: {
+ chainId: 8453,
+ name: "Base",
+ chain: base,
+ rpcUrl: "https://base.llamarpc.com",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://basescan.org",
+ },
+ sepolia: {
+ chainId: 11155111,
+ name: "Sepolia",
+ chain: sepolia,
+ rpcUrl: "https://rpc.sepolia.org",
+ nativeCurrency: {
+ name: "Sepolia Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://sepolia.etherscan.io",
+ },
+ bsc: {
+ chainId: 56,
+ name: "BNB Smart Chain",
+ chain: bsc,
+ rpcUrl: "https://bsc-dataseed1.binance.org/",
+ nativeCurrency: {
+ name: "Binance Coin",
+ symbol: "BNB",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://bscscan.com",
+ },
+ arbitrum: {
+ chainId: 42161,
+ name: "Arbitrum One",
+ chain: arbitrum,
+ rpcUrl: "https://arb1.arbitrum.io/rpc",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://arbiscan.io",
+ },
+ avalanche: {
+ chainId: 43114,
+ name: "Avalanche C-Chain",
+ chain: avalanche,
+ rpcUrl: "https://api.avax.network/ext/bc/C/rpc",
+ nativeCurrency: {
+ name: "Avalanche",
+ symbol: "AVAX",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://snowtrace.io",
+ },
+ polygon: {
+ chainId: 137,
+ name: "Polygon",
+ chain: polygon,
+ rpcUrl: "https://polygon-rpc.com",
+ nativeCurrency: {
+ name: "MATIC",
+ symbol: "MATIC",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://polygonscan.com",
+ },
+ optimism: {
+ chainId: 10,
+ name: "Optimism",
+ chain: optimism,
+ rpcUrl: "https://mainnet.optimism.io",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://optimistic.etherscan.io",
+ },
+ cronos: {
+ chainId: 25,
+ name: "Cronos",
+ chain: cronos,
+ rpcUrl: "https://evm.cronos.org",
+ nativeCurrency: {
+ name: "Cronos",
+ symbol: "CRO",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://cronoscan.com",
+ },
+ gnosis: {
+ chainId: 100,
+ name: "Gnosis",
+ chain: gnosis,
+ rpcUrl: "https://rpc.gnosischain.com",
+ nativeCurrency: {
+ name: "xDAI",
+ symbol: "XDAI",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://gnosisscan.io",
+ },
+ fantom: {
+ chainId: 250,
+ name: "Fantom",
+ chain: fantom,
+ rpcUrl: "https://rpc.ftm.tools",
+ nativeCurrency: {
+ name: "Fantom",
+ symbol: "FTM",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://ftmscan.com",
+ },
+ klaytn: {
+ chainId: 8217,
+ name: "Klaytn",
+ chain: klaytn,
+ rpcUrl: "https://public-node-api.klaytnapi.com/v1/cypress",
+ nativeCurrency: {
+ name: "KLAY",
+ symbol: "KLAY",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://scope.klaytn.com",
+ },
+ celo: {
+ chainId: 42220,
+ name: "Celo",
+ chain: celo,
+ rpcUrl: "https://forno.celo.org",
+ nativeCurrency: {
+ name: "Celo",
+ symbol: "CELO",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://celoscan.io",
+ },
+ moonbeam: {
+ chainId: 1284,
+ name: "Moonbeam",
+ chain: moonbeam,
+ rpcUrl: "https://rpc.api.moonbeam.network",
+ nativeCurrency: {
+ name: "Glimmer",
+ symbol: "GLMR",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://moonscan.io",
+ },
+ aurora: {
+ chainId: 1313161554,
+ name: "Aurora",
+ chain: aurora,
+ rpcUrl: "https://mainnet.aurora.dev",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://aurorascan.dev",
+ },
+ harmonyOne: {
+ chainId: 1666600000,
+ name: "harmonyOne",
+ chain: harmonyOne,
+ rpcUrl: "https://api.harmonyOne.one",
+ nativeCurrency: {
+ name: "ONE",
+ symbol: "ONE",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://explorer.harmonyOne.one",
+ },
+ moonriver: {
+ chainId: 1285,
+ name: "Moonriver",
+ chain: moonriver,
+ rpcUrl: "https://rpc.api.moonriver.moonbeam.network",
+ nativeCurrency: {
+ name: "Moonriver",
+ symbol: "MOVR",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://moonriver.moonscan.io",
+ },
+ arbitrumNova: {
+ chainId: 42170,
+ name: "Arbitrum Nova",
+ chain: arbitrumNova,
+ rpcUrl: "https://nova.arbitrum.io/rpc",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://nova-explorer.arbitrum.io",
+ },
+ mantle: {
+ chainId: 5000,
+ name: "Mantle",
+ chain: mantle,
+ rpcUrl: "https://rpc.mantle.xyz",
+ nativeCurrency: {
+ name: "Mantle",
+ symbol: "MNT",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://explorer.mantle.xyz",
+ },
+ linea: {
+ chainId: 59144,
+ name: "Linea",
+ chain: linea,
+ rpcUrl: "https://linea-mainnet.rpc.build",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://lineascan.build",
+ },
+ scroll: {
+ chainId: 534353,
+ name: "Scroll Alpha Testnet",
+ chain: scroll,
+ rpcUrl: "https://alpha-rpc.scroll.io/l2",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://blockscout.scroll.io",
+ },
+ filecoin: {
+ chainId: 314,
+ name: "Filecoin",
+ chain: filecoin,
+ rpcUrl: "https://api.node.glif.io/rpc/v1",
+ nativeCurrency: {
+ name: "Filecoin",
+ symbol: "FIL",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://filfox.info/en",
+ },
+ taiko: {
+ chainId: 167005,
+ name: "Taiko (Alpha-3) Testnet",
+ chain: taiko,
+ rpcUrl: "https://rpc.a3.taiko.xyz",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://explorer.a3.taiko.xyz",
+ },
+ zksync: {
+ chainId: 324,
+ name: "zksync Era",
+ chain: zksync,
+ rpcUrl: "https://mainnet.era.zksync.io",
+ nativeCurrency: {
+ name: "Ether",
+ symbol: "ETH",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://explorer.zksync.io",
+ },
+ canto: {
+ chainId: 7700,
+ name: "Canto",
+ chain: canto,
+ rpcUrl: "https://canto.slingshot.finance",
+ nativeCurrency: {
+ name: "CANTO",
+ symbol: "CANTO",
+ decimals: 18,
+ },
+ blockExplorerUrl: "https://tuber.build",
+ },
+} as const;
+
+export const getChainConfigs = (runtime: IAgentRuntime) => {
+ return (
+ (runtime.character.settings.chains?.evm as ChainMetadata[]) ||
+ DEFAULT_CHAIN_CONFIGS
+ );
+};
diff --git a/packages/plugin-evm/src/providers/chainUtils.ts b/packages/plugin-evm/src/providers/chainUtils.ts
new file mode 100644
index 0000000000..377aa3f363
--- /dev/null
+++ b/packages/plugin-evm/src/providers/chainUtils.ts
@@ -0,0 +1,51 @@
+import { createPublicClient, createWalletClient, http } from "viem";
+import type { IAgentRuntime } from "@ai16z/eliza";
+import type {
+ Account,
+ Chain,
+ HttpTransport,
+ PublicClient,
+ WalletClient,
+} from "viem";
+import type { SupportedChain, ChainConfig } from "../types";
+import { DEFAULT_CHAIN_CONFIGS } from "./chainConfigs";
+
+export const createChainClients = (
+ chain: SupportedChain,
+ runtime: IAgentRuntime,
+ account: Account
+): ChainConfig => {
+ const chainConfig = DEFAULT_CHAIN_CONFIGS[chain];
+ const transport = http(chainConfig.rpcUrl);
+
+ return {
+ chain: chainConfig.chain,
+ publicClient: createPublicClient({
+ chain: chainConfig.chain,
+ transport,
+ }) as PublicClient,
+ walletClient: createWalletClient({
+ chain: chainConfig.chain,
+ transport,
+ account,
+ }),
+ };
+};
+
+export const initializeChainConfigs = (
+ runtime: IAgentRuntime,
+ account: Account
+): Record => {
+ return Object.keys(DEFAULT_CHAIN_CONFIGS).reduce(
+ (configs, chain) => {
+ const supportedChain = chain as SupportedChain;
+ configs[supportedChain] = createChainClients(
+ supportedChain,
+ runtime,
+ account
+ );
+ return configs;
+ },
+ {} as Record
+ );
+};
diff --git a/packages/plugin-evm/src/providers/wallet.ts b/packages/plugin-evm/src/providers/wallet.ts
index 01b934300d..a317a86187 100644
--- a/packages/plugin-evm/src/providers/wallet.ts
+++ b/packages/plugin-evm/src/providers/wallet.ts
@@ -1,53 +1,17 @@
+import { formatUnits } from "viem";
+import { privateKeyToAccount } from "viem/accounts";
import type { IAgentRuntime, Provider, Memory, State } from "@ai16z/eliza";
-import {
- createPublicClient,
- createWalletClient,
- http,
- formatUnits,
- type PublicClient,
- type WalletClient,
- type Chain,
- type HttpTransport,
- type Address,
+import type {
+ Address,
+ WalletClient,
+ PublicClient,
+ Chain,
+ HttpTransport,
Account,
} from "viem";
-import { mainnet, base } from "viem/chains";
-import type { SupportedChain, ChainConfig, ChainMetadata } from "../types";
-import { privateKeyToAccount } from "viem/accounts";
-
-export const DEFAULT_CHAIN_CONFIGS: Record = {
- ethereum: {
- chainId: 1,
- name: "Ethereum",
- chain: mainnet,
- rpcUrl: "https://eth.llamarpc.com",
- nativeCurrency: {
- name: "Ether",
- symbol: "ETH",
- decimals: 18,
- },
- blockExplorerUrl: "https://etherscan.io",
- },
- base: {
- chainId: 8453,
- name: "Base",
- chain: base,
- rpcUrl: "https://base.llamarpc.com",
- nativeCurrency: {
- name: "Ether",
- symbol: "ETH",
- decimals: 18,
- },
- blockExplorerUrl: "https://basescan.org",
- },
-} as const;
-
-export const getChainConfigs = (runtime: IAgentRuntime) => {
- return (
- (runtime.character.settings.chains?.evm as ChainConfig[]) ||
- DEFAULT_CHAIN_CONFIGS
- );
-};
+import type { SupportedChain, ChainConfig } from "../types";
+import { getChainConfigs } from "./chainConfigs";
+import { initializeChainConfigs } from "./chainUtils";
export class WalletProvider {
private chainConfigs: Record;
@@ -60,30 +24,11 @@ export class WalletProvider {
if (!privateKey) throw new Error("EVM_PRIVATE_KEY not configured");
this.runtime = runtime;
-
const account = privateKeyToAccount(privateKey as `0x${string}`);
this.address = account.address;
- const createClients = (chain: SupportedChain): ChainConfig => {
- const transport = http(getChainConfigs(runtime)[chain].rpcUrl);
- return {
- chain: getChainConfigs(runtime)[chain].chain,
- publicClient: createPublicClient({
- chain: getChainConfigs(runtime)[chain].chain,
- transport,
- }) as PublicClient,
- walletClient: createWalletClient({
- chain: getChainConfigs(runtime)[chain].chain,
- transport,
- account,
- }),
- };
- };
-
- this.chainConfigs = {
- ethereum: createClients("ethereum"),
- base: createClients("base"),
- };
+ // Initialize all chain configs at once
+ this.chainConfigs = initializeChainConfigs(runtime, account);
}
getAddress(): Address {
@@ -175,7 +120,6 @@ export const evmWalletProvider: Provider = {
message: Memory,
state?: State
): Promise {
- // Check if the user has an EVM wallet
if (!runtime.getSetting("EVM_PRIVATE_KEY")) {
return null;
}
diff --git a/packages/plugin-evm/src/templates/index.ts b/packages/plugin-evm/src/templates/index.ts
index d8bccf17d3..a8c7f1fcc3 100644
--- a/packages/plugin-evm/src/templates/index.ts
+++ b/packages/plugin-evm/src/templates/index.ts
@@ -5,7 +5,7 @@ export const transferTemplate = `Given the recent messages and wallet informatio
{{walletInfo}}
Extract the following information about the requested transfer:
-- Chain to execute on (ethereum or base)
+- Chain to execute on
- Amount to transfer
- Recipient address
- Token symbol or address (if not native token)
@@ -14,7 +14,7 @@ Respond with a JSON markdown block containing only the extracted values:
\`\`\`json
{
- "chain": "ethereum" | "base" | null,
+ "chain": "ethereum" | "base" | "sepolia" | "bsc" | "arbitrum" | "avalanche" | "polygon" | "optimism" | "cronos" | "gnosis" | "fantom" | "klaytn" | "celo" | "moonbeam" | "aurora" | "harmonyOne" | "moonriver" | "arbitrumNova" | "mantle" | "linea" | "scroll" | "filecoin" | "taiko" | "zksync" | "canto" | null,
"amount": string | null,
"toAddress": string | null,
"token": string | null
@@ -30,8 +30,8 @@ export const bridgeTemplate = `Given the recent messages and wallet information
Extract the following information about the requested token bridge:
- Token symbol or address to bridge
-- Source chain (ethereum or base)
-- Destination chain (ethereum or base)
+- Source chain
+- Destination chain
- Amount to bridge
- Destination address (if specified)
@@ -40,8 +40,8 @@ Respond with a JSON markdown block containing only the extracted values:
\`\`\`json
{
"token": string | null,
- "fromChain": "ethereum" | "base" | null,
- "toChain": "ethereum" | "base" | null,
+ "fromChain": "ethereum" | "base" | "sepolia" | "bsc" | "arbitrum" | "avalanche" | "polygon" | "optimism" | "cronos" | "gnosis" | "fantom" | "klaytn" | "celo" | "moonbeam" | "aurora" | "harmonyOne" | "moonriver" | "arbitrumNova" | "mantle" | "linea" | "scroll" | "filecoin" | "taiko" | "zksync" | "canto" | null,
+ "toChain": "ethereum" | "base" | "sepolia" | "bsc" | "arbitrum" | "avalanche" | "polygon" | "optimism" | "cronos" | "gnosis" | "fantom" | "klaytn" | "celo" | "moonbeam" | "aurora" | "harmonyOne" | "moonriver" | "arbitrumNova" | "mantle" | "linea" | "scroll" | "filecoin" | "taiko" | "zksync" | "canto" | null,
"amount": string | null,
"toAddress": string | null
}
@@ -58,7 +58,7 @@ Extract the following information about the requested token swap:
- Input token symbol or address (the token being sold)
- Output token symbol or address (the token being bought)
- Amount to swap
-- Chain to execute on (ethereum or base)
+- Chain to execute on
Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined:
@@ -67,7 +67,7 @@ Respond with a JSON markdown block containing only the extracted values. Use nul
"inputToken": string | null,
"outputToken": string | null,
"amount": string | null,
- "chain": "ethereum" | "base" | null,
+ "chain": "ethereum" | "base" | "sepolia" | "bsc" | "arbitrum" | "avalanche" | "polygon" | "optimism" | "cronos" | "gnosis" | "fantom" | "klaytn" | "celo" | "moonbeam" | "aurora" | "harmonyOne" | "moonriver" | "arbitrumNova" | "mantle" | "linea" | "scroll" | "filecoin" | "taiko" | "zksync" | "canto" | null,
"slippage": number | null
}
\`\`\`
diff --git a/packages/plugin-evm/src/types/index.ts b/packages/plugin-evm/src/types/index.ts
index d2bfbca0ed..885f3994fc 100644
--- a/packages/plugin-evm/src/types/index.ts
+++ b/packages/plugin-evm/src/types/index.ts
@@ -9,7 +9,32 @@ import type {
WalletClient,
} from "viem";
-export type SupportedChain = "ethereum" | "base";
+export type SupportedChain =
+ | "ethereum"
+ | "base"
+ | "sepolia"
+ | "bsc"
+ | "arbitrum"
+ | "avalanche"
+ | "polygon"
+ | "optimism"
+ | "cronos"
+ | "gnosis"
+ | "fantom"
+ | "klaytn"
+ | "celo"
+ | "moonbeam"
+ | "aurora"
+ | "harmonyOne"
+ | "moonriver"
+ | "arbitrumNova"
+ | "mantle"
+ | "linea"
+ | "scroll"
+ | "filecoin"
+ | "taiko"
+ | "zksync"
+ | "canto";
// Transaction types
export interface Transaction {
@@ -87,6 +112,29 @@ export interface EvmPluginConfig {
rpcUrl?: {
ethereum?: string;
base?: string;
+ sepolia?: string;
+ bsc?: string;
+ arbitrum?: string;
+ avalanche?: string;
+ polygon?: string;
+ optimism?: string;
+ cronos?: string;
+ gnosis?: string;
+ fantom?: string;
+ klaytn?: string;
+ celo?: string;
+ moonbeam?: string;
+ aurora?: string;
+ harmonyOne?: string;
+ moonriver?: string;
+ arbitrumNova?: string;
+ mantle?: string;
+ linea?: string;
+ scroll?: string;
+ filecoin?: string;
+ taiko?: string;
+ zksync?: string;
+ canto?: string;
};
secrets?: {
EVM_PRIVATE_KEY: string;
diff --git a/packages/plugin-flow/src/actions/transfer.ts b/packages/plugin-flow/src/actions/transfer.ts
index a717a9c4a2..bc04dcfec3 100644
--- a/packages/plugin-flow/src/actions/transfer.ts
+++ b/packages/plugin-flow/src/actions/transfer.ts
@@ -2,7 +2,7 @@ import {
composeContext,
Content,
elizaLogger,
- generateObject,
+ generateObjectArray,
ModelClass,
type Action,
type ActionExample,
@@ -87,12 +87,17 @@ export class TransferAction {
});
// Generate transfer content
- const content = await generateObject({
+ const recommendations = await generateObjectArray({
runtime,
context: transferContext,
- modelClass: ModelClass.SMALL,
+ modelClass: ModelClass.MEDIUM,
});
+ elizaLogger.debug("Recommendations", recommendations);
+
+ // Convert array to object
+ const content = recommendations[recommendations.length - 1];
+
// Validate transfer content
if (!isTransferContent(runtime, content)) {
elizaLogger.error("Invalid content for SEND_COIN action.");
diff --git a/packages/plugin-flow/src/assets/cadence/transactions/evm/call.cdc b/packages/plugin-flow/src/assets/cadence/transactions/evm/call.cdc
index 1f7c60143b..44fffbcf7f 100644
--- a/packages/plugin-flow/src/assets/cadence/transactions/evm/call.cdc
+++ b/packages/plugin-flow/src/assets/cadence/transactions/evm/call.cdc
@@ -10,7 +10,20 @@ transaction(evmContractAddressHex: String, calldata: String, gasLimit: UInt64, v
prepare(signer: auth(BorrowValue) &Account) {
self.evmAddress = EVM.addressFromString(evmContractAddressHex)
- self.coa = signer.storage.borrow(from: /storage/evm)
+ let storagePath = StoragePath(identifier: "evm")!
+ let publicPath = PublicPath(identifier: "evm")!
+
+ // Reference signer's COA if one exists
+ let coa = signer.storage.borrow(from: storagePath)
+ if coa == nil {
+ let coa <- EVM.createCadenceOwnedAccount()
+ signer.storage.save<@EVM.CadenceOwnedAccount>(<-coa, to: storagePath)
+ let addressableCap = signer.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(storagePath)
+ signer.capabilities.unpublish(publicPath)
+ signer.capabilities.publish(addressableCap, at: publicPath)
+ }
+
+ self.coa = signer.storage.borrow(from: storagePath)
?? panic("Could not borrow COA from provided gateway address")
}
diff --git a/packages/plugin-flow/src/providers/connector.provider.ts b/packages/plugin-flow/src/providers/connector.provider.ts
index f0a7e3913d..7d81fe3c87 100644
--- a/packages/plugin-flow/src/providers/connector.provider.ts
+++ b/packages/plugin-flow/src/providers/connector.provider.ts
@@ -72,7 +72,7 @@ export class FlowConnectorProvider {
constructor(private readonly instance: FlowConnector) {}
getConnectorStatus(runtime: IAgentRuntime): string {
- let output = `${runtime.character.name}[${runtime.character.id ?? 0}] Connected to\n`;
+ let output = `Now user<${runtime.character.name}> connected to\n`;
output += `Flow network: ${this.instance.network}\n`;
output += `Flow Endpoint: ${this.instance.rpcEndpoint}\n`;
return output;
diff --git a/packages/plugin-flow/src/providers/wallet.provider.ts b/packages/plugin-flow/src/providers/wallet.provider.ts
index 7e6ba65bf0..1d300bafa7 100644
--- a/packages/plugin-flow/src/providers/wallet.provider.ts
+++ b/packages/plugin-flow/src/providers/wallet.provider.ts
@@ -232,7 +232,12 @@ const flowWalletProvider: Provider = {
elizaLogger.error("Invalid account info");
return null;
}
- return `Flow Wallet Address: ${walletProvider.address}\nBalance: ${info.balance} FLOW\nFlow COA(EVM) Address: ${info.coaAddress || "unknown"}\nFLOW COA(EVM) Balance: ${info.coaBalance ?? 0} FLOW`;
+ let output = `Here is user<${runtime.character.name}>'s wallet status:\n`;
+ output += `Flow wallet address: ${walletProvider.address}\n`;
+ output += `FLOW balance: ${info.balance} FLOW\n`;
+ output += `Flow wallet's COA(EVM) address: ${info.coaAddress || "unknown"}\n`;
+ output += `FLOW balance in COA(EVM) address: ${info.coaBalance ?? 0} FLOW`;
+ return output;
} catch (error) {
elizaLogger.error("Error in Flow wallet provider:", error.message);
return null;
diff --git a/packages/plugin-flow/src/templates/index.ts b/packages/plugin-flow/src/templates/index.ts
index 1f6a3878e0..66b27b1854 100644
--- a/packages/plugin-flow/src/templates/index.ts
+++ b/packages/plugin-flow/src/templates/index.ts
@@ -8,7 +8,9 @@ Extract the following information about the requested transfer:
- Field "token": Cadence Resource Identifier or ERC20 contract address (if not native token). this field should be null if the token is native token: $FLOW or FLOW. Examples for this field:
1. For Cadence resource identifier, the field should be "A.1654653399040a61.ContractName"
2. For ERC20 contract address, the field should be "0xe6ffc15a5bde7dd33c127670ba2b9fcb82db971a"
-- Field "amount": Amount to transfer
+- Field "amount": Amount to transfer, it should be a number or a string. Examples for this field:
+ 1. "1000"
+ 2. 1000
- Field "to": Recipient wallet address, can be EVM address or Cadence address. Examples for this field:
1. Cadence address: "0x1654653399040a61"
2. EVM address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
@@ -21,7 +23,7 @@ Respond with a JSON markdown block containing only the extracted values. Use nul
\`\`\`json
{
"token": string | null
- "amount": string | null,
+ "amount": number | string | null,
"to": string | null,
"matched": boolean
}
diff --git a/packages/plugin-goat/README.md b/packages/plugin-goat/README.md
index fcac78c573..4baa1bd150 100644
--- a/packages/plugin-goat/README.md
+++ b/packages/plugin-goat/README.md
@@ -1,12 +1,39 @@
-# Goat Plugin
-Example plugin setup of how you can integrate [Goat](https://ohmygoat.dev/) tools and plugins with Eliza.
-
-Adds onchain capabilities to your agent to send and check balances of ETH and USDC. Add all the capabilities you need by adding more plugins!
-
-## Setup
-1. Configure your wallet (key pair, smart wallet, etc. see all available wallets at [https://ohmygoat.dev/wallets](https://ohmygoat.dev/wallets))
-2. Add the plugins you need (uniswap, zora, polymarket, etc. see all available plugins at [https://ohmygoat.dev/chains-wallets-plugins](https://ohmygoat.dev/chains-wallets-plugins))
-3. Select a chain (see all available chains at [https://ohmygoat.dev/chains](https://ohmygoat.dev/chains))
-4. Import and add the plugin to your Eliza agent
-5. Build the project
-6. Add the necessary environment variables to set up your wallet and plugins
+# GOAT Plugin
+[GOAT](https://ohmygoat.dev/) 🐐 (Great Onchain Agent Toolkit) is an open-source framework for adding blockchain tools such as wallets, being able to hold or trade tokens, or interacting with blockchain smart contracts, to your AI agent.
+
+This plugin integrates GOAT with Eliza, giving your agent the ability to interact with many different protocols. The current setup adds onchain capabilities to your agent to send and check balances of ETH and USDC. Add all the capabilities you need by adding more plugins (read below for more information)!
+
+## Configure GOAT for your use case
+1. Configure the chain you want to use by updating the `wallet.ts` file (see all available chains at [https://ohmygoat.dev/chains](https://ohmygoat.dev/chains))
+2. Add the plugins you need to your `getOnChainActions` function (uniswap, polymarket, etc. see all available plugins at [https://ohmygoat.dev/chains-wallets-plugins](https://ohmygoat.dev/chains-wallets-plugins))
+3. Build the project running `pnpm build`
+4. Add the necessary environment variables to set up your wallet and plugins
+5. Run your agent!
+
+## Common Issues
+1. **Agent not executing an action**:
+ - If you are also using the EVM Plugin, sometimes the agent might confuse the action name with an EVM Plugin action name instead of the GOAT Plugin action. Removing the EVM Plugin should fix this issue. There is no need for you to use both plugins at the same time.
+ - If you are using Trump as a character it might be tricky to get them to perform any action since the character is full of prompts that aim to change the topic of the conversation. To fix this try using a different character or create your own with prompts that are more suitable to what the agent is supposed to do.
+
+## Plugins
+GOAT itself has several plugins for interacting with different protocols such as Polymarket, Uniswap, and more. (see all available plugins at [https://ohmygoat.dev/chains-wallets-plugins](https://ohmygoat.dev/chains-wallets-plugins))
+
+You can easily add them by installing them and adding them to the `getOnChainActions` function:
+
+```typescript
+const tools = getOnChainActions({
+ wallet: walletClient,
+ plugins: [
+ sendETH(),
+ erc20({ tokens: [USDC, PEPE] }),
+ polymarket(),
+ uniswap(),
+ // ...
+ ],
+})
+```
+
+## Wallets
+GOAT supports many different wallets from key pairs to [Crossmint Smart Wallets](https://docs.crossmint.com/wallets/smart-wallets/overview) and Coinbase.
+
+Read more about wallets at [https://ohmygoat.dev/wallets](https://ohmygoat.dev/wallets).
diff --git a/packages/plugin-goat/src/actions.ts b/packages/plugin-goat/src/actions.ts
index d58f124926..df4f1c906b 100644
--- a/packages/plugin-goat/src/actions.ts
+++ b/packages/plugin-goat/src/actions.ts
@@ -1,10 +1,9 @@
import {
type WalletClient,
type Plugin,
- getDeferredTools,
addParametersToDescription,
- type ChainForWalletClient,
- type DeferredTool,
+ type Tool,
+ getTools,
} from "@goat-sdk/core";
import {
type Action,
@@ -19,10 +18,8 @@ import {
} from "@ai16z/eliza";
type GetOnChainActionsParams = {
- chain: ChainForWalletClient;
- getWalletClient: (runtime: IAgentRuntime) => Promise;
+ wallet: TWalletClient;
plugins: Plugin[];
- supportsSmartWallets?: boolean;
};
/**
@@ -32,16 +29,13 @@ type GetOnChainActionsParams = {
* @returns
*/
export async function getOnChainActions({
- getWalletClient,
+ wallet,
plugins,
- chain,
- supportsSmartWallets,
}: GetOnChainActionsParams): Promise {
- const tools = await getDeferredTools({
+ const tools = await getTools({
+ wallet,
plugins,
wordForTool: "action",
- chain,
- supportsSmartWallets,
});
return tools
@@ -49,13 +43,10 @@ export async function getOnChainActions({
...action,
name: action.name.toUpperCase(),
}))
- .map((tool) => createAction(tool, getWalletClient));
+ .map((tool) => createAction(tool));
}
-function createAction(
- tool: DeferredTool,
- getWalletClient: (runtime: IAgentRuntime) => Promise
-): Action {
+function createAction(tool: Tool): Action {
return {
name: tool.name,
similes: [],
@@ -69,7 +60,6 @@ function createAction(
callback?: HandlerCallback
): Promise => {
try {
- const walletClient = await getWalletClient(runtime);
let currentState =
state ?? (await runtime.composeState(message));
currentState =
@@ -94,10 +84,7 @@ function createAction(
return false;
}
- const result = await tool.method(
- walletClient,
- parsedParameters.data
- );
+ const result = await tool.method(parsedParameters.data);
const responseContext = composeResponseContext(
tool,
result,
@@ -124,10 +111,7 @@ function createAction(
};
}
-function composeParameterContext(
- tool: DeferredTool,
- state: State
-): string {
+function composeParameterContext(tool: Tool, state: State): string {
const contextTemplate = `{{recentMessages}}
Given the recent messages, extract the following information for the action "${tool.name}":
@@ -136,10 +120,10 @@ ${addParametersToDescription("", tool.parameters)}
return composeContext({ state, template: contextTemplate });
}
-async function generateParameters(
+async function generateParameters(
runtime: IAgentRuntime,
context: string,
- tool: DeferredTool
+ tool: Tool
): Promise {
const { object } = await generateObjectV2({
runtime,
@@ -151,8 +135,8 @@ async function generateParameters(
return object;
}
-function composeResponseContext(
- tool: DeferredTool,
+function composeResponseContext(
+ tool: Tool,
result: unknown,
state: State
): string {
diff --git a/packages/plugin-goat/src/index.ts b/packages/plugin-goat/src/index.ts
index 281d95a162..10e8583c3f 100644
--- a/packages/plugin-goat/src/index.ts
+++ b/packages/plugin-goat/src/index.ts
@@ -1,27 +1,31 @@
-import type { Plugin } from '@ai16z/eliza'
-import { getOnChainActions } from './actions';
-import { erc20, USDC } from '@goat-sdk/plugin-erc20';
-import { chain, getWalletClient, walletProvider } from './provider';
-import { sendETH } from '@goat-sdk/core';
+import type { Plugin } from "@ai16z/eliza";
+import { getOnChainActions } from "./actions";
+import { erc20, USDC } from "@goat-sdk/plugin-erc20";
+import { sendETH } from "@goat-sdk/core";
+import { getWalletClient, getWalletProvider } from "./wallet";
-export const goatPlugin: Plugin = {
- name: "[GOAT] Onchain Actions",
- description: "Base integration plugin",
- providers: [walletProvider],
- evaluators: [],
- services: [],
- actions: [
- ...(await getOnChainActions({
- getWalletClient,
- // Add plugins here based on what actions you want to use
- // See all available plugins at https://ohmygoat.dev/chains-wallets-plugins#plugins
- plugins: [sendETH(), erc20({ tokens: [USDC] })],
- chain: {
- type: "evm",
- id: chain.id,
- },
- })),
- ],
-};
+async function createGoatPlugin(
+ getSetting: (key: string) => string | undefined
+): Promise {
+ const walletClient = getWalletClient(getSetting);
+ const actions = await getOnChainActions({
+ wallet: walletClient,
+ // Add plugins here based on what actions you want to use
+ // See all available plugins at https://ohmygoat.dev/chains-wallets-plugins#plugins
+ plugins: [
+ sendETH(),
+ erc20({ tokens: [USDC] }),
+ ],
+ });
-export default goatPlugin
+ return {
+ name: "[GOAT] Onchain Actions",
+ description: "Base integration plugin",
+ providers: [getWalletProvider(walletClient)],
+ evaluators: [],
+ services: [],
+ actions: actions,
+ };
+}
+
+export default createGoatPlugin;
diff --git a/packages/plugin-goat/src/provider.ts b/packages/plugin-goat/src/provider.ts
deleted file mode 100644
index 20b4356b6f..0000000000
--- a/packages/plugin-goat/src/provider.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { createWalletClient, http } from "viem";
-import { privateKeyToAccount } from "viem/accounts";
-import { base } from "viem/chains";
-
-import { Memory, Provider, State, type IAgentRuntime } from "@ai16z/eliza";
-import { viem } from "@goat-sdk/wallet-viem";
-
-
-// Add the chain you want to use, remember to update also
-// the EVM_PROVIDER_URL to the correct one for the chain
-export const chain = base;
-
-/**
- * Create a wallet client for the given runtime.
- *
- * You can change it to use a different wallet client such as Crossmint smart wallets or others.
- *
- * See all available wallet clients at https://ohmygoat.dev/wallets
- *
- * @param runtime
- * @returns Wallet client
- */
-export async function getWalletClient(runtime: IAgentRuntime) {
- const privateKey = runtime.getSetting("EVM_PRIVATE_KEY");
- if (!privateKey) throw new Error("EVM_PRIVATE_KEY not configured");
-
- const provider = runtime.getSetting("EVM_PROVIDER_URL");
- if (!provider) throw new Error("EVM_PROVIDER_URL not configured");
-
- const walletClient = createWalletClient({
- account: privateKeyToAccount(privateKey as `0x${string}`),
- chain: chain,
- transport: http(provider),
- });
- return viem(walletClient);
-}
-
-export const walletProvider: Provider = {
- async get(
- runtime: IAgentRuntime,
- message: Memory,
- state?: State
- ): Promise {
- try {
- const walletClient = await getWalletClient(runtime);
- const address = walletClient.getAddress();
- const balance = await walletClient.balanceOf(address);
- return `EVM Wallet Address: ${address}\nBalance: ${balance} ETH`;
- } catch (error) {
- console.error("Error in EVM wallet provider:", error);
- return null;
- }
- },
-};
diff --git a/packages/plugin-goat/src/wallet.ts b/packages/plugin-goat/src/wallet.ts
new file mode 100644
index 0000000000..6492c95af7
--- /dev/null
+++ b/packages/plugin-goat/src/wallet.ts
@@ -0,0 +1,42 @@
+import { WalletClient } from "@goat-sdk/core";
+import { viem } from "@goat-sdk/wallet-viem";
+import { createWalletClient, http } from "viem";
+import { privateKeyToAccount } from "viem/accounts";
+import { base } from "viem/chains";
+
+// Add the chain you want to use, remember to update also
+// the EVM_PROVIDER_URL to the correct one for the chain
+export const chain = base;
+
+export function getWalletClient(
+ getSetting: (key: string) => string | undefined
+) {
+ const privateKey = getSetting("EVM_PRIVATE_KEY");
+ if (!privateKey) return null;
+
+ const provider = getSetting("EVM_PROVIDER_URL");
+ if (!provider) throw new Error("EVM_PROVIDER_URL not configured");
+
+ const wallet = createWalletClient({
+ account: privateKeyToAccount(privateKey as `0x${string}`),
+ chain: chain,
+ transport: http(provider),
+ });
+
+ return viem(wallet);
+}
+
+export function getWalletProvider(walletClient: WalletClient) {
+ return {
+ async get(): Promise {
+ try {
+ const address = walletClient.getAddress();
+ const balance = await walletClient.balanceOf(address);
+ return `EVM Wallet Address: ${address}\nBalance: ${balance} ETH`;
+ } catch (error) {
+ console.error("Error in EVM wallet provider:", error);
+ return null;
+ }
+ },
+ };
+}
diff --git a/packages/plugin-icp/src/actions/createToken.ts b/packages/plugin-icp/src/actions/createToken.ts
index 1aacf72cd6..b33c0f302f 100644
--- a/packages/plugin-icp/src/actions/createToken.ts
+++ b/packages/plugin-icp/src/actions/createToken.ts
@@ -2,7 +2,7 @@ import {
composeContext,
generateImage,
generateText,
- generateObject,
+ generateObjectDEPRECATED,
} from "@ai16z/eliza";
import {
ActionExample,
@@ -148,7 +148,7 @@ export const executeCreateToken: Action = {
template: createTokenTemplate,
});
- const response = await generateObject({
+ const response = await generateObjectDEPRECATED({
runtime,
context: createTokenContext,
modelClass: ModelClass.LARGE,
diff --git a/packages/plugin-buttplug/.npmignore b/packages/plugin-intiface/.npmignore
similarity index 100%
rename from packages/plugin-buttplug/.npmignore
rename to packages/plugin-intiface/.npmignore
diff --git a/packages/plugin-buttplug/intiface-engine/CHANGELOG.md b/packages/plugin-intiface/intiface-engine/CHANGELOG.md
similarity index 100%
rename from packages/plugin-buttplug/intiface-engine/CHANGELOG.md
rename to packages/plugin-intiface/intiface-engine/CHANGELOG.md
diff --git a/packages/plugin-buttplug/intiface-engine/README.md b/packages/plugin-intiface/intiface-engine/README.md
similarity index 100%
rename from packages/plugin-buttplug/intiface-engine/README.md
rename to packages/plugin-intiface/intiface-engine/README.md
diff --git a/packages/plugin-buttplug/intiface-engine/intiface-engine b/packages/plugin-intiface/intiface-engine/intiface-engine
similarity index 100%
rename from packages/plugin-buttplug/intiface-engine/intiface-engine
rename to packages/plugin-intiface/intiface-engine/intiface-engine
diff --git a/packages/plugin-buttplug/package.json b/packages/plugin-intiface/package.json
similarity index 92%
rename from packages/plugin-buttplug/package.json
rename to packages/plugin-intiface/package.json
index e00b348aad..16cc574f3e 100644
--- a/packages/plugin-buttplug/package.json
+++ b/packages/plugin-intiface/package.json
@@ -1,5 +1,5 @@
{
- "name": "@ai16z/plugin-buttplug",
+ "name": "@ai16z/plugin-intiface",
"version": "0.1.5-alpha.5",
"main": "dist/index.js",
"type": "module",
diff --git a/packages/plugin-buttplug/src/environment.ts b/packages/plugin-intiface/src/environment.ts
similarity index 78%
rename from packages/plugin-buttplug/src/environment.ts
rename to packages/plugin-intiface/src/environment.ts
index 47ba5c0cd5..9359952df7 100644
--- a/packages/plugin-buttplug/src/environment.ts
+++ b/packages/plugin-intiface/src/environment.ts
@@ -1,10 +1,10 @@
import { IAgentRuntime } from "@ai16z/eliza";
import { z } from "zod";
-export const buttplugEnvSchema = z
+export const intifaceEnvSchema = z
.object({
INTIFACE_URL: z.string().default("ws://localhost:12345"),
- INTIFACE_NAME: z.string().default("Eliza Buttplug Client"),
+ INTIFACE_NAME: z.string().default("Eliza Intiface Client"),
DEVICE_NAME: z.string().default("Lovense Nora"),
})
.refine(
@@ -20,11 +20,11 @@ export const buttplugEnvSchema = z
}
);
-export type ButtplugConfig = z.infer;
+export type IntifaceConfig = z.infer;
-export async function validateButtplugConfig(
+export async function validateIntifaceConfig(
runtime: IAgentRuntime
-): Promise {
+): Promise {
try {
const config = {
INTIFACE_URL:
@@ -36,14 +36,14 @@ export async function validateButtplugConfig(
runtime.getSetting("DEVICE_NAME") || process.env.DEVICE_NAME,
};
- return buttplugEnvSchema.parse(config);
+ return intifaceEnvSchema.parse(config);
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessages = error.errors
.map((err) => `${err.path.join(".")}: ${err.message}`)
.join("\n");
throw new Error(
- `Buttplug configuration validation failed:\n${errorMessages}`
+ `Intiface configuration validation failed:\n${errorMessages}`
);
}
throw error;
diff --git a/packages/plugin-buttplug/src/index.ts b/packages/plugin-intiface/src/index.ts
similarity index 93%
rename from packages/plugin-buttplug/src/index.ts
rename to packages/plugin-intiface/src/index.ts
index b9da82b015..3cf09276e9 100644
--- a/packages/plugin-buttplug/src/index.ts
+++ b/packages/plugin-intiface/src/index.ts
@@ -1,5 +1,5 @@
import { ButtplugClient, ButtplugNodeWebsocketClientConnector } from "buttplug";
-import { validateButtplugConfig, type ButtplugConfig } from "./environment";
+import { validateIntifaceConfig, type IntifaceConfig } from "./environment";
import type {
Action,
HandlerCallback,
@@ -15,7 +15,7 @@ import {
shutdownIntifaceEngine,
} from "./utils";
-export interface IButtplugService extends Service {
+export interface IIntifaceService extends Service {
vibrate(strength: number, duration: number): Promise;
rotate?(strength: number, duration: number): Promise