@hybrd/xmtp 1.4.5 β 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -319
- package/dist/index.cjs +138 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +131 -78
- package/dist/index.js.map +1 -1
- package/package.json +10 -6
- package/src/client.ts +19 -39
- package/src/index.ts +8 -5
- package/src/lib/jwt.ts +16 -23
- package/src/lib/secret.ts +33 -0
- package/src/plugin.ts +98 -39
- package/src/index.ts.old +0 -145
- package/src/lib/message-listener.test.ts.old +0 -369
- package/src/lib/message-listener.ts.old +0 -343
- package/src/localStorage.ts.old +0 -203
- package/src/plugin.filters.test.ts +0 -158
- package/src/service-client.ts.old +0 -309
- package/src/transactionMonitor.ts.old +0 -275
- package/src/types.ts.old +0 -157
package/README.md
CHANGED
|
@@ -1,414 +1,304 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @hybrd/xmtp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
XMTP integration library for Hybrid AI agents. Provides client creation with R2 database persistence, an `XMTPPlugin` for the agent framework, ENS/Basename/address resolvers, and JWT utilities.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Website & docs: [hybrid.dev](https://hybrid.dev)
|
|
7
|
+
This package handles all XMTP network connectivity for Hybrid agents:
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
- **Client creation** with installation revocation, retry logic, and Cloudflare R2 database sync
|
|
10
|
+
- **XMTPPlugin** integrating XMTP into the `Agent` + `Plugin` framework architecture
|
|
11
|
+
- **Resolvers** for XMTP inbox IDs, ENS names, and Base network Basenames
|
|
12
|
+
- **JWT utilities** for securing XMTP tool API endpoints
|
|
13
|
+
- **Scripts** for wallet registration and installation management
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
## OpenClaw Compatibility
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
import { XMTPPlugin } from "@hybrd/xmtp"
|
|
16
|
-
import { filter } from "hybrid"
|
|
17
|
-
|
|
18
|
-
// As a standalone plugin instance
|
|
19
|
-
const xmtp = XMTPPlugin({
|
|
20
|
-
filters: [
|
|
21
|
-
filter.isText,
|
|
22
|
-
filter.not(filter.isFromSelf),
|
|
23
|
-
filter.startsWith("@agent")
|
|
24
|
-
]
|
|
25
|
-
})
|
|
26
|
-
```
|
|
17
|
+
This package is a Hybrid-native XMTP integration, not derived from the OpenClaw SDK. It wraps `@xmtp/node-sdk` and `@xmtp/agent-sdk` with production-ready reliability features.
|
|
27
18
|
|
|
28
|
-
|
|
19
|
+
## Architecture
|
|
29
20
|
|
|
30
|
-
```typescript
|
|
31
|
-
await agent.listen({
|
|
32
|
-
port: process.env.PORT || "8454",
|
|
33
|
-
filters: [filter.isText, filter.startsWith("@agent")]
|
|
34
|
-
})
|
|
35
21
|
```
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
β
|
|
54
|
-
β
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
22
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
23
|
+
β @hybrd/xmtp β
|
|
24
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
|
|
25
|
+
β β
|
|
26
|
+
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
|
27
|
+
β β XMTPPlugin (plugin.ts) β β
|
|
28
|
+
β β ββββββββββββββββββββββ ββββββββββββββββββββββββββββββ β β
|
|
29
|
+
β β β XmtpAgent β β XmtpClient (low-level) β β β
|
|
30
|
+
β β β (event listener) β β (resolvers, direct API) β β β
|
|
31
|
+
β β ββββββββββββββββββββββ ββββββββββββββββββββββββββββββ β β
|
|
32
|
+
β β β β
|
|
33
|
+
β β Behavior chain: executeBefore β agent.generate β send β β
|
|
34
|
+
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
|
35
|
+
β β
|
|
36
|
+
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
|
37
|
+
β β Client (client.ts) β β
|
|
38
|
+
β β createXMTPClient() β retry loop: β β
|
|
39
|
+
β β 1. Download .db3 from R2 (if XMTP_STORAGE bound) β β
|
|
40
|
+
β β 2. Create Client with codecs β β
|
|
41
|
+
β β 3. On install limit: revoke old β retry β β
|
|
42
|
+
β β 4. On identity error: refresh identity β retry β β
|
|
43
|
+
β β 5. Upload .db3 to R2 after connect β β
|
|
44
|
+
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
|
45
|
+
β β
|
|
46
|
+
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
|
47
|
+
β β Resolvers (resolver/) β β
|
|
48
|
+
β β Resolver β AddressResolver, ENSResolver, BasenameResolver β β
|
|
49
|
+
β β XmtpResolver (inbox IDs + message chains) β β
|
|
50
|
+
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
|
51
|
+
β β
|
|
52
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
62
53
|
```
|
|
63
54
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
1. **QStash Automatic Retries**: Built-in exponential backoff
|
|
67
|
-
2. **No Persistent Connections**: Eliminates connection drop issues
|
|
68
|
-
3. **Horizontal Scalability**: Multiple instances supported
|
|
69
|
-
4. **Dead Letter Queues**: Failed messages are preserved
|
|
70
|
-
5. **At-least-once Delivery**: Messages guaranteed to be processed
|
|
55
|
+
## Features
|
|
71
56
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
### Basic Usage
|
|
57
|
+
### Client Creation
|
|
75
58
|
|
|
76
59
|
```typescript
|
|
77
|
-
import {
|
|
78
|
-
createSigner,
|
|
79
|
-
createXMTPConnectionManager,
|
|
80
|
-
type XMTPConnectionConfig
|
|
81
|
-
} from "@hybrd/xmtp"
|
|
60
|
+
import { createXMTPClient } from "@hybrd/xmtp"
|
|
82
61
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
retryDelayMs: 1000, // Base retry delay
|
|
89
|
-
healthCheckIntervalMs: 30000, // Health check interval
|
|
90
|
-
connectionTimeoutMs: 15000, // Connection timeout
|
|
91
|
-
reconnectOnFailure: true // Auto-reconnect
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const connectionManager = await createXMTPConnectionManager(
|
|
95
|
-
signer,
|
|
96
|
-
connectionConfig
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
// Get health metrics
|
|
100
|
-
const health = connectionManager.getHealth()
|
|
101
|
-
console.log('Connection Health:', health)
|
|
62
|
+
const client = await createXMTPClient(process.env.AGENT_WALLET_KEY!, {
|
|
63
|
+
persist: true, // Persist conversations locally
|
|
64
|
+
maxRetries: 3, // Installation revocation retry limit
|
|
65
|
+
storagePath: ".xmtp" // Custom DB storage path
|
|
66
|
+
})
|
|
102
67
|
```
|
|
103
68
|
|
|
104
|
-
|
|
69
|
+
**Automatic resilience:**
|
|
70
|
+
- If the installation limit is reached, revokes old installations and retries
|
|
71
|
+
- If an identity association error occurs, refreshes identity and retries
|
|
72
|
+
- If `XMTP_STORAGE` (Cloudflare R2) is in `globalThis`, downloads the `.db3` database before connecting and uploads it after β enabling stateless containers to persist XMTP state
|
|
105
73
|
|
|
106
|
-
|
|
107
|
-
import { RobustXMTPService } from "@hybrd/xmtp/scripts/enhanced-connection-example"
|
|
108
|
-
|
|
109
|
-
const service = new RobustXMTPService()
|
|
110
|
-
await service.start()
|
|
111
|
-
|
|
112
|
-
// Process messages with automatic retry/reconnection
|
|
113
|
-
await service.processMessage(conversationId, "Hello!")
|
|
114
|
-
|
|
115
|
-
// Monitor health
|
|
116
|
-
const health = service.getConnectionHealth()
|
|
117
|
-
if (!health?.isConnected) {
|
|
118
|
-
console.warn("XMTP connection unhealthy")
|
|
119
|
-
}
|
|
120
|
-
```
|
|
74
|
+
Registered content type codecs: `Text`, `Reply`, `Reaction`, `WalletSendCalls`, `TransactionReference`
|
|
121
75
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
The enhanced client provides real-time health metrics:
|
|
76
|
+
### User/Signer Creation
|
|
125
77
|
|
|
126
78
|
```typescript
|
|
127
|
-
|
|
128
|
-
isConnected: boolean // Current connection status
|
|
129
|
-
lastHealthCheck: Date // Last health check timestamp
|
|
130
|
-
consecutiveFailures: number // Failed health checks in a row
|
|
131
|
-
totalReconnects: number // Total reconnection attempts
|
|
132
|
-
avgResponseTime: number // Average response time in ms
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## π Quick Start
|
|
79
|
+
import { createUser, createSigner } from "@hybrd/xmtp"
|
|
137
80
|
|
|
138
|
-
|
|
81
|
+
const user = createUser(process.env.AGENT_WALLET_KEY!)
|
|
82
|
+
// { key, account, wallet } β viem wallet client on Sepolia
|
|
139
83
|
|
|
140
|
-
|
|
141
|
-
|
|
84
|
+
const signer = createSigner(process.env.AGENT_WALLET_KEY!)
|
|
85
|
+
// XMTP Signer: { getIdentifier(), signMessage() }
|
|
142
86
|
```
|
|
143
87
|
|
|
144
|
-
###
|
|
88
|
+
### Connection Manager
|
|
145
89
|
|
|
146
|
-
|
|
147
|
-
pnpm with-env pnpm --filter @hybrd/xmtp enhanced:demo
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### 3. Integrate in Your Code
|
|
90
|
+
For long-running services that need health monitoring and auto-reconnection:
|
|
151
91
|
|
|
152
92
|
```typescript
|
|
153
|
-
|
|
154
|
-
// OLD:
|
|
155
|
-
const client = await createXMTPClient(signer)
|
|
93
|
+
import { XMTPConnectionManager } from "@hybrd/xmtp"
|
|
156
94
|
|
|
157
|
-
|
|
158
|
-
const connectionManager = await createXMTPConnectionManager(signer, {
|
|
95
|
+
const manager = new XMTPConnectionManager(process.env.AGENT_WALLET_KEY!, {
|
|
159
96
|
maxRetries: 5,
|
|
97
|
+
retryDelayMs: 1000,
|
|
160
98
|
healthCheckIntervalMs: 30000,
|
|
99
|
+
connectionTimeoutMs: 15000,
|
|
161
100
|
reconnectOnFailure: true
|
|
162
101
|
})
|
|
163
|
-
const client = connectionManager.getClient()
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
## π Why Your Current Architecture is Already Robust
|
|
167
|
-
|
|
168
|
-
Your **QStash-based webhook system** already provides superior reliability compared to traditional streaming:
|
|
169
|
-
|
|
170
|
-
### Traditional Streaming Issues β
|
|
171
|
-
- Connection drops require manual reconnection
|
|
172
|
-
- Memory leaks from long-running connections
|
|
173
|
-
- Difficult to scale horizontally
|
|
174
|
-
- Complex heartbeat/keepalive management
|
|
175
|
-
- Single point of failure
|
|
176
102
|
|
|
177
|
-
|
|
178
|
-
- **Automatic Retries**: QStash handles exponential backoff
|
|
179
|
-
- **No Connection Drops**: Stateless webhook calls
|
|
180
|
-
- **Horizontal Scaling**: Multiple app instances supported
|
|
181
|
-
- **Built-in Monitoring**: QStash provides delivery metrics
|
|
182
|
-
- **Dead Letter Queues**: Failed messages preserved
|
|
183
|
-
- **At-least-once Delivery**: Guaranteed message processing
|
|
103
|
+
const client = await manager.connect()
|
|
184
104
|
|
|
185
|
-
|
|
105
|
+
const health = manager.getHealth()
|
|
106
|
+
// { isConnected, lastHealthCheck, consecutiveFailures, totalReconnects, avgResponseTime }
|
|
186
107
|
|
|
187
|
-
|
|
108
|
+
await manager.disconnect()
|
|
109
|
+
```
|
|
188
110
|
|
|
189
|
-
|
|
190
|
-
| ------------------------ | ---------------------------------------- | --------------------------------------- |
|
|
191
|
-
| `XMTP_STORAGE_PATH` | Custom path for XMTP database storage | `.data/xmtp` (relative to project root) |
|
|
192
|
-
| `XMTP_WALLET_KEY` | Private key for XMTP wallet | Required |
|
|
193
|
-
| `XMTP_DB_ENCRYPTION_KEY` | Encryption key for database | Required for persistent mode |
|
|
194
|
-
| `XMTP_ENV` | XMTP environment (`dev` or `production`) | `dev` |
|
|
195
|
-
| `PROJECT_ROOT` | Override project root path | Auto-detected |
|
|
111
|
+
### XMTPPlugin
|
|
196
112
|
|
|
197
|
-
|
|
113
|
+
Integrates XMTP into the agent framework's `Plugin` architecture:
|
|
198
114
|
|
|
199
115
|
```typescript
|
|
200
|
-
|
|
201
|
-
maxRetries?: number // Default: 5
|
|
202
|
-
retryDelayMs?: number // Default: 1000ms
|
|
203
|
-
healthCheckIntervalMs?: number // Default: 30000ms
|
|
204
|
-
connectionTimeoutMs?: number // Default: 10000ms
|
|
205
|
-
reconnectOnFailure?: boolean // Default: true
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Custom Storage Location
|
|
116
|
+
import { XMTPPlugin } from "@hybrd/xmtp"
|
|
210
117
|
|
|
211
|
-
|
|
118
|
+
// Applied via agent.use() or agent.listen()
|
|
119
|
+
const plugin = XMTPPlugin()
|
|
212
120
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
121
|
+
await agent.listen({
|
|
122
|
+
port: "8454",
|
|
123
|
+
plugins: [plugin],
|
|
124
|
+
behaviors: [myBehavior()]
|
|
125
|
+
})
|
|
126
|
+
```
|
|
216
127
|
|
|
217
|
-
|
|
218
|
-
|
|
128
|
+
For each incoming message (text, reply, reaction), the plugin:
|
|
129
|
+
1. Builds conversation history (up to 20 messages)
|
|
130
|
+
2. Creates `AgentRuntime` with conversation, message, and xmtpClient
|
|
131
|
+
3. Runs `behaviors.executeBefore(context)` β stops chain if `context.stopped = true`
|
|
132
|
+
4. Calls `agent.generate(messages, { runtime })`
|
|
133
|
+
5. Sets `context.response = reply`
|
|
134
|
+
6. Runs `behaviors.executeAfter(context)` β stops chain if `context.stopped = true`
|
|
135
|
+
7. Sends reply (plain text or threaded via `ContentTypeReply`)
|
|
219
136
|
|
|
220
|
-
|
|
221
|
-
pnpm with-env your-xmtp-command
|
|
222
|
-
```
|
|
137
|
+
### Resolver
|
|
223
138
|
|
|
224
|
-
|
|
139
|
+
Universal name and address resolution across XMTP, ENS, and Basenames:
|
|
225
140
|
|
|
226
|
-
|
|
141
|
+
```typescript
|
|
142
|
+
import { Resolver } from "@hybrd/xmtp"
|
|
143
|
+
import { createPublicClient, http } from "viem"
|
|
144
|
+
import { mainnet, base } from "viem/chains"
|
|
227
145
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
146
|
+
const resolver = new Resolver({
|
|
147
|
+
xmtpClient,
|
|
148
|
+
mainnetClient: createPublicClient({ chain: mainnet, transport: http() }),
|
|
149
|
+
baseClient: createPublicClient({ chain: base, transport: http() }),
|
|
150
|
+
maxCacheSize: 1000,
|
|
151
|
+
cacheTtl: 3_600_000 // 1 hour
|
|
152
|
+
})
|
|
231
153
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
154
|
+
// Universal resolution (tries ENS then Basename)
|
|
155
|
+
const address = await resolver.resolveName("vitalik.eth")
|
|
156
|
+
const address2 = await resolver.resolveName("myname.base.eth")
|
|
235
157
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
pnpm with-env pnpm --filter @hybrd/xmtp custom:storage
|
|
239
|
-
```
|
|
158
|
+
// Reverse resolution (tries Basename then ENS)
|
|
159
|
+
const name = await resolver.resolveAddressToName("0x...")
|
|
240
160
|
|
|
241
|
-
|
|
161
|
+
// Complete profile
|
|
162
|
+
const profile = await resolver.getCompleteProfile("0x...")
|
|
163
|
+
// { address, ensName, basename, ensProfile, basenameProfile }
|
|
242
164
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
| `revoke` | `pnpm --filter @hybrd/xmtp revoke` | Revoke old XMTP installations |
|
|
248
|
-
| `enhanced:demo` | `pnpm --filter @hybrd/xmtp enhanced:demo` | Demo enhanced connection features |
|
|
249
|
-
| `test:messages` | `pnpm --filter @hybrd/xmtp test:messages` | Test message reception |
|
|
250
|
-
| `refresh:identity` | `pnpm --filter @hybrd/xmtp refresh:identity` | Refresh XMTP identity |
|
|
251
|
-
| `custom:storage` | `pnpm --filter @hybrd/xmtp custom:storage` | Test custom storage configuration |
|
|
165
|
+
// XMTP-specific
|
|
166
|
+
const address3 = await resolver.resolveAddress(inboxId, conversationId)
|
|
167
|
+
const sender = await resolver.createXmtpSender(inboxId, conversationId)
|
|
168
|
+
// { address, inboxId, name, basename? }
|
|
252
169
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
> ```
|
|
170
|
+
// ENS
|
|
171
|
+
const ensProfile = await resolver.getENSProfile("vitalik.eth")
|
|
172
|
+
// { ensName, address, avatar, description, twitter, github, url }
|
|
257
173
|
|
|
258
|
-
|
|
174
|
+
// Basenames (Base network)
|
|
175
|
+
const basename = await resolver.getBasename("0x...")
|
|
176
|
+
const basenameProfile = await resolver.resolveBasenameProfile("0x...")
|
|
259
177
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const manager = await createXMTPConnectionManager(signer, config)
|
|
178
|
+
// Message resolution
|
|
179
|
+
const message = await resolver.findMessage(messageId)
|
|
180
|
+
const rootMessage = await resolver.findRootMessage(messageId) // Traverses reply chain
|
|
264
181
|
```
|
|
265
182
|
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
// For message processing, your webhook system is ideal
|
|
269
|
-
// No changes needed - it's already robust!
|
|
270
|
-
```
|
|
183
|
+
#### Individual Resolvers
|
|
271
184
|
|
|
272
|
-
### 3. Monitor Connection Health
|
|
273
185
|
```typescript
|
|
274
|
-
|
|
275
|
-
const health = connectionManager.getHealth()
|
|
276
|
-
if (health.consecutiveFailures > 3) {
|
|
277
|
-
console.warn("XMTP connection degraded")
|
|
278
|
-
}
|
|
279
|
-
}, 60000)
|
|
280
|
-
```
|
|
186
|
+
import { AddressResolver, ENSResolver, BasenameResolver, XmtpResolver } from "@hybrd/xmtp/resolver"
|
|
281
187
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
188
|
+
const ensResolver = new ENSResolver({ mainnetClient })
|
|
189
|
+
const basenameResolver = new BasenameResolver({ publicClient: baseClient })
|
|
190
|
+
const addressResolver = new AddressResolver({ xmtpClient })
|
|
191
|
+
const xmtpResolver = new XmtpResolver({ xmtpClient })
|
|
286
192
|
```
|
|
287
193
|
|
|
288
|
-
|
|
194
|
+
### JWT Utilities
|
|
289
195
|
|
|
290
|
-
|
|
196
|
+
Secure XMTP tool API endpoints with short-lived JWTs:
|
|
291
197
|
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
pnpm with-env pnpm --filter @hybrd/xmtp enhanced:demo
|
|
295
|
-
```
|
|
198
|
+
```typescript
|
|
199
|
+
import { generateXMTPToolsToken, validateXMTPToolsToken, getValidatedPayload } from "@hybrd/xmtp"
|
|
296
200
|
|
|
297
|
-
|
|
201
|
+
// Generate a 5-minute token (signed with derived secret)
|
|
202
|
+
const token = generateXMTPToolsToken({
|
|
203
|
+
action: "send",
|
|
204
|
+
conversationId: "conv-123",
|
|
205
|
+
content: "Hello!"
|
|
206
|
+
})
|
|
298
207
|
|
|
299
|
-
|
|
208
|
+
// Validate (returns null if invalid or expired)
|
|
209
|
+
const payload = validateXMTPToolsToken(token)
|
|
300
210
|
|
|
301
|
-
|
|
302
|
-
|
|
211
|
+
// Extract from Hono request context (Authorization: Bearer or ?token=)
|
|
212
|
+
const payload = getValidatedPayload(honoContext)
|
|
303
213
|
```
|
|
304
214
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
The enhanced client provides detailed metrics:
|
|
215
|
+
`XMTP_API_KEY` env var is also accepted as a static alternative to JWT.
|
|
308
216
|
|
|
309
|
-
|
|
310
|
-
- **Response Times**: Average XMTP response latency
|
|
311
|
-
- **Failure Counts**: Track connection reliability
|
|
312
|
-
- **Reconnection Events**: Monitor stability over time
|
|
313
|
-
|
|
314
|
-
## π¨ Migration Guide
|
|
315
|
-
|
|
316
|
-
### From Basic XMTP Client
|
|
217
|
+
### Mention Extraction
|
|
317
218
|
|
|
318
219
|
```typescript
|
|
319
|
-
|
|
320
|
-
const client = await createXMTPClient(signer)
|
|
321
|
-
|
|
322
|
-
// After
|
|
323
|
-
const manager = await createXMTPConnectionManager(signer)
|
|
324
|
-
const client = manager.getClient()
|
|
220
|
+
import { extractSubjects } from "@hybrd/xmtp"
|
|
325
221
|
|
|
326
|
-
//
|
|
327
|
-
await
|
|
222
|
+
// Extracts @basename.eth and @name.base.eth mentions and resolves to addresses
|
|
223
|
+
const subjects = await extractSubjects(messageContent, basenameResolver, ensResolver)
|
|
224
|
+
// { "vitalik": "0x...", "myname": "0x..." }
|
|
328
225
|
```
|
|
329
226
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
**No changes needed!** Your QStash webhook system is already providing:
|
|
333
|
-
- β
Automatic retries with exponential backoff
|
|
334
|
-
- β
Reliable message delivery guarantees
|
|
335
|
-
- β
Horizontal scalability
|
|
336
|
-
- β
Built-in monitoring and alerting
|
|
337
|
-
- β
Dead letter queue handling
|
|
227
|
+
## Scripts
|
|
338
228
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
- [QStash Documentation](https://upstash.com/docs/qstash)
|
|
343
|
-
- [Project Architecture](../../ARCHITECTURE.md)
|
|
344
|
-
|
|
345
|
-
---
|
|
229
|
+
```bash
|
|
230
|
+
# Register wallet on XMTP network
|
|
231
|
+
hybrid register
|
|
346
232
|
|
|
347
|
-
|
|
233
|
+
# Revoke specific inbox installations
|
|
234
|
+
hybrid revoke <inboxId>
|
|
348
235
|
|
|
349
|
-
#
|
|
236
|
+
# Auto-detect inbox ID and revoke all installations
|
|
237
|
+
hybrid revoke-all
|
|
238
|
+
```
|
|
350
239
|
|
|
351
|
-
|
|
240
|
+
Or run directly:
|
|
352
241
|
|
|
353
|
-
|
|
242
|
+
```bash
|
|
243
|
+
pnpm --filter @hybrd/xmtp register
|
|
244
|
+
pnpm --filter @hybrd/xmtp revoke
|
|
245
|
+
pnpm --filter @hybrd/xmtp revoke-all
|
|
246
|
+
```
|
|
354
247
|
|
|
355
|
-
|
|
248
|
+
## Re-exports
|
|
356
249
|
|
|
357
|
-
|
|
250
|
+
This package re-exports the complete `@xmtp/node-sdk` surface and all content type codecs:
|
|
358
251
|
|
|
359
252
|
```typescript
|
|
360
|
-
import {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
//
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
253
|
+
import {
|
|
254
|
+
Client,
|
|
255
|
+
Signer,
|
|
256
|
+
type XmtpEnv,
|
|
257
|
+
// Content types:
|
|
258
|
+
ContentTypeReaction,
|
|
259
|
+
ContentTypeReply,
|
|
260
|
+
ContentTypeGroupUpdated,
|
|
261
|
+
ContentTypeTransactionReference,
|
|
262
|
+
ContentTypeWalletSendCalls,
|
|
263
|
+
ContentTypeText
|
|
264
|
+
} from "@hybrd/xmtp"
|
|
265
|
+
```
|
|
369
266
|
|
|
370
|
-
|
|
371
|
-
chain: base,
|
|
372
|
-
transport: http()
|
|
373
|
-
})
|
|
267
|
+
## Database Path Resolution
|
|
374
268
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
xmtpClient: yourXmtpClient,
|
|
378
|
-
mainnetClient,
|
|
379
|
-
baseClient,
|
|
380
|
-
maxCacheSize: 1000,
|
|
381
|
-
cacheTtl: 3600000 // 1 hour
|
|
382
|
-
})
|
|
269
|
+
```typescript
|
|
270
|
+
import { getDbPath } from "@hybrd/xmtp"
|
|
383
271
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
272
|
+
const dbPath = await getDbPath("my-agent", "/custom/storage")
|
|
273
|
+
// Priority: XMTP_STORAGE_PATH env β storagePath param β .hybrid/.xmtp/
|
|
274
|
+
// If XMTP_STORAGE (R2) is in globalThis, downloads existing DB first
|
|
275
|
+
```
|
|
387
276
|
|
|
388
|
-
|
|
389
|
-
const name = await resolver.resolveAddressToName('0x...')
|
|
277
|
+
## Encryption Key Generation
|
|
390
278
|
|
|
391
|
-
|
|
392
|
-
|
|
279
|
+
```typescript
|
|
280
|
+
import { generateEncryptionKeyHex } from "@hybrd/xmtp"
|
|
393
281
|
|
|
394
|
-
|
|
395
|
-
const ensName = await resolver.resolveENSName('vitalik.eth')
|
|
396
|
-
const basename = await resolver.getBasename('0x...')
|
|
397
|
-
const message = await resolver.findMessage('messageId')
|
|
282
|
+
const key = generateEncryptionKeyHex() // 32-byte random hex string
|
|
398
283
|
```
|
|
399
284
|
|
|
400
|
-
|
|
285
|
+
## Environment Variables
|
|
401
286
|
|
|
402
|
-
|
|
287
|
+
| Variable | Description |
|
|
288
|
+
|----------|-------------|
|
|
289
|
+
| `AGENT_WALLET_KEY` | Private key for the agent wallet (required) |
|
|
290
|
+
| `XMTP_ENV` | XMTP environment: `dev` or `production` (default: `dev`) |
|
|
291
|
+
| `XMTP_STORAGE_PATH` | Custom path for XMTP database files |
|
|
292
|
+
| `XMTP_API_KEY` | Static API key for XMTP tool endpoints (alternative to JWT) |
|
|
293
|
+
| `XMTP_DEBUG` | Enable debug logging |
|
|
403
294
|
|
|
404
|
-
|
|
405
|
-
- `XmtpResolver` - XMTP message and address resolution with advanced features
|
|
406
|
-
- `ENSResolver` - ENS name resolution
|
|
407
|
-
- `BasenameResolver` - Basename resolution for Base network
|
|
295
|
+
## Testing
|
|
408
296
|
|
|
409
|
-
```
|
|
410
|
-
|
|
297
|
+
```bash
|
|
298
|
+
cd packages/xmtp
|
|
299
|
+
pnpm test
|
|
300
|
+
```
|
|
411
301
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT
|