@hybrd/channels 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 ADDED
@@ -0,0 +1,180 @@
1
+ # @hybrd/channels
2
+
3
+ Channel adapter framework for Hybrid AI agents. Provides a pluggable, uniform interface for connecting different messaging networks to the agent, with local HTTP IPC for inter-process communication.
4
+
5
+ ## Overview
6
+
7
+ The channels package abstracts the "listen for inbound + deliver outbound" pattern behind a `ChannelAdapter` interface. Currently ships with a full XMTP adapter; the architecture supports adding Telegram, Slack, and other channels.
8
+
9
+ Key design decisions:
10
+ - **Local HTTP IPC**: All communication between the scheduler/dispatcher and channel adapters uses `http://127.0.0.1` on fixed ports — adapters are independently deployable processes
11
+ - **Port-based routing**: Each channel has a reserved port in `DEFAULT_ADAPTER_PORTS`
12
+ - **Decoupled from agent**: Adapters communicate with the agent via HTTP only, no direct function calls
13
+
14
+ ## Architecture
15
+
16
+ ```
17
+ ┌──────────────────────────────────────────────────────────────────┐
18
+ │ @hybrd/channels │
19
+ ├──────────────────────────────────────────────────────────────────┤
20
+ │ │
21
+ │ dispatchToChannel() │
22
+ │ │ │
23
+ │ └── POST http://127.0.0.1:{port}/api/trigger │
24
+ │ │ │
25
+ │ ▼ │
26
+ │ ┌─────────────────────┐ │
27
+ │ │ XMTPAdapter │ port 8455 │
28
+ │ │ ───────────── │ │
29
+ │ │ XmtpAgent │ ← inbound messages from XMTP network │
30
+ │ │ Express server │ ← outbound trigger from scheduler │
31
+ │ │ runAgentAndReply()│ → POST {agentUrl}/api/chat │
32
+ │ └─────────────────────┘ │
33
+ │ │
34
+ │ DEFAULT_ADAPTER_PORTS = { xmtp: 8455, ... } │
35
+ │ │
36
+ └──────────────────────────────────────────────────────────────────┘
37
+ ```
38
+
39
+ ## Features
40
+
41
+ ### dispatchToChannel
42
+
43
+ Send a message to a user via any registered channel:
44
+
45
+ ```typescript
46
+ import { dispatchToChannel } from "@hybrd/channels"
47
+
48
+ const result = await dispatchToChannel({
49
+ channel: "xmtp",
50
+ to: "0x...", // Target address or conversation ID
51
+ message: "Hello from the scheduler!",
52
+ metadata: {
53
+ accountId: "0xagent...",
54
+ threadId: "optional-thread-id"
55
+ }
56
+ })
57
+
58
+ // { delivered: boolean, messageId?: string, error?: string }
59
+ ```
60
+
61
+ This POSTs a `TriggerRequest` to the adapter's local HTTP server at `http://127.0.0.1:8455/api/trigger`.
62
+
63
+ ### XMTPAdapter
64
+
65
+ The XMTP channel adapter. Handles both inbound messages from the XMTP network and outbound triggers from the scheduler.
66
+
67
+ ```typescript
68
+ import { createXMTPAdapter } from "@hybrd/channels"
69
+
70
+ const adapter = await createXMTPAdapter({
71
+ port: 8455,
72
+ agentUrl: "http://localhost:8454",
73
+ xmtpEnv: "dev",
74
+ walletKey: process.env.AGENT_WALLET_KEY!,
75
+ dbPath: "./.xmtp"
76
+ })
77
+
78
+ await adapter.start() // Connects to XMTP, starts HTTP server
79
+ await adapter.stop() // Closes HTTP server
80
+ ```
81
+
82
+ **Inbound flow** (XMTP → agent):
83
+ 1. XMTP text message arrives
84
+ 2. Deduplicates by message ID
85
+ 3. Finds conversation, builds reply context
86
+ 4. POSTs to `{agentUrl}/api/chat`
87
+ 5. Reads SSE stream, assembles full response
88
+ 6. Sends reply via `conversation.send(reply)`
89
+
90
+ **Outbound flow** (scheduler → XMTP):
91
+ 1. `POST /api/trigger` received on adapter's HTTP server
92
+ 2. Finds target conversation
93
+ 3. Calls `runAgentAndReply()` — same flow as inbound
94
+
95
+ ### Programmatic Trigger
96
+
97
+ ```typescript
98
+ // Trigger directly without HTTP
99
+ const result = await adapter.trigger({
100
+ to: "0xconversation-id-or-address",
101
+ message: "Scheduled reminder",
102
+ metadata: { accountId: "0xagent..." }
103
+ })
104
+ ```
105
+
106
+ ## Default Ports
107
+
108
+ ```typescript
109
+ import { DEFAULT_ADAPTER_PORTS } from "@hybrd/channels"
110
+
111
+ console.log(DEFAULT_ADAPTER_PORTS)
112
+ // { xmtp: 8455 }
113
+ ```
114
+
115
+ ## Running as a Standalone Process
116
+
117
+ The XMTP adapter can run as an independent process:
118
+
119
+ ```bash
120
+ # Environment variables required:
121
+ # AGENT_WALLET_KEY, XMTP_ENV, PORT (optional)
122
+
123
+ node packages/channels/src/adapters/xmtp/index.js
124
+ ```
125
+
126
+ Or from the adapter entry point — reads config from env vars, prints a banner, and starts the adapter.
127
+
128
+ ## Adding a New Channel
129
+
130
+ 1. Reserve a port in `DEFAULT_ADAPTER_PORTS`
131
+ 2. Implement the `ChannelAdapter` interface from `@hybrd/types`:
132
+
133
+ ```typescript
134
+ import type { ChannelAdapter, TriggerRequest, TriggerResponse } from "@hybrd/types"
135
+
136
+ class TelegramAdapter implements ChannelAdapter {
137
+ channel = "telegram" as const
138
+ port = 8456
139
+
140
+ async start(): Promise<void> {
141
+ // Connect to Telegram, start local HTTP server on this.port
142
+ }
143
+
144
+ async stop(): Promise<void> {
145
+ // Close HTTP server
146
+ }
147
+
148
+ async trigger(req: TriggerRequest): Promise<TriggerResponse> {
149
+ // Send message to Telegram user
150
+ }
151
+ }
152
+ ```
153
+
154
+ 3. Export from `packages/channels/src/adapters/telegram/`
155
+
156
+ ## Relation to Other Packages
157
+
158
+ - Types (`ChannelAdapter`, `TriggerRequest`, `TriggerResponse`, `CronDelivery`) come from `@hybrd/types`
159
+ - `dispatchToChannel()` is the counterpart to `SchedulerService`'s delivery mechanism in `@hybrd/scheduler`
160
+ - The `XMTPAdapter` is a clean factoring of the sidecar logic in `packages/agent/src/xmtp.ts`
161
+ - Communicates with `packages/agent` via HTTP — no direct imports from the agent package
162
+
163
+ ## Environment Variables
164
+
165
+ | Variable | Description |
166
+ |----------|-------------|
167
+ | `AGENT_WALLET_KEY` | Private key for the agent's XMTP wallet |
168
+ | `XMTP_ENV` | XMTP environment: `dev` or `production` |
169
+ | `AGENT_URL` | Base URL for the agent server (default: `http://localhost:8454`) |
170
+
171
+ ## Testing
172
+
173
+ ```bash
174
+ cd packages/channels
175
+ pnpm test
176
+ ```
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,393 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/adapters/xmtp/adapter.ts
34
+ var adapter_exports = {};
35
+ __export(adapter_exports, {
36
+ XMTPAdapter: () => XMTPAdapter,
37
+ createXMTPAdapter: () => createXMTPAdapter
38
+ });
39
+ async function createXMTPAdapter(config) {
40
+ const adapter = new XMTPAdapter(config);
41
+ await adapter.start();
42
+ return adapter;
43
+ }
44
+ var import_node_crypto, import_memory, import_agent_sdk, import_express, import_picocolors, log, XMTPAdapter;
45
+ var init_adapter = __esm({
46
+ "src/adapters/xmtp/adapter.ts"() {
47
+ "use strict";
48
+ import_node_crypto = require("crypto");
49
+ import_memory = require("@hybrid/memory");
50
+ import_agent_sdk = require("@xmtp/agent-sdk");
51
+ import_express = __toESM(require("express"), 1);
52
+ import_picocolors = __toESM(require("picocolors"), 1);
53
+ log = {
54
+ info: (msg) => console.log(`${import_picocolors.default.magenta("[xmtp]")} ${msg}`),
55
+ error: (msg) => console.error(`${import_picocolors.default.red("[xmtp]")} ${msg}`),
56
+ warn: (msg) => console.log(`${import_picocolors.default.yellow("[xmtp]")} ${msg}`),
57
+ success: (msg) => console.log(`${import_picocolors.default.green("[xmtp]")} ${msg}`)
58
+ };
59
+ XMTPAdapter = class {
60
+ channel = "xmtp";
61
+ port;
62
+ config;
63
+ agent = null;
64
+ server = null;
65
+ app;
66
+ botInboxId = null;
67
+ processedMessages = /* @__PURE__ */ new Set();
68
+ addressCache = /* @__PURE__ */ new Map();
69
+ constructor(config) {
70
+ this.port = config.port;
71
+ this.config = config;
72
+ this.app = (0, import_express.default)();
73
+ this.app.use(import_express.default.json());
74
+ }
75
+ async start() {
76
+ await this.startXMTPClient();
77
+ this.startTriggerServer();
78
+ }
79
+ async stop() {
80
+ this.server?.close();
81
+ }
82
+ async startXMTPClient() {
83
+ const { createUser: createUser2 } = await import("@xmtp/agent-sdk");
84
+ const { toBytes } = await import("viem");
85
+ const user = createUser2(this.config.walletKey);
86
+ const identifier = {
87
+ identifier: user.account.address.toLowerCase(),
88
+ identifierKind: 0
89
+ };
90
+ const signer = {
91
+ type: "EOA",
92
+ getIdentifier: () => identifier,
93
+ getChainId: async () => BigInt(1),
94
+ signMessage: async (message) => {
95
+ const sig = await user.account.signMessage({ message });
96
+ return toBytes(sig);
97
+ }
98
+ };
99
+ this.agent = await import_agent_sdk.Agent.create(
100
+ signer,
101
+ {
102
+ env: this.config.xmtpEnv ?? "dev",
103
+ dbEncryptionKey: this.config.dbEncryptionKey,
104
+ dbPath: this.config.dbPath
105
+ }
106
+ );
107
+ log.success("connected to XMTP network");
108
+ this.botInboxId = this.agent.client.inboxId;
109
+ this.agent.on("text", async (ctx) => {
110
+ const { conversation, message } = ctx;
111
+ await this.handleInbound(conversation, message);
112
+ });
113
+ await this.agent.start();
114
+ log.success("listening for XMTP messages");
115
+ }
116
+ startTriggerServer() {
117
+ this.app.post("/api/trigger", async (req, res) => {
118
+ const result = await this.handleTrigger(req.body);
119
+ res.json(result);
120
+ });
121
+ this.server = this.app.listen(this.port, "127.0.0.1", () => {
122
+ log.success(`trigger server listening on 127.0.0.1:${this.port}`);
123
+ });
124
+ }
125
+ async resolveSenderAddress(inboxId, conversation) {
126
+ const cached = this.addressCache.get(inboxId);
127
+ if (cached) return cached;
128
+ try {
129
+ const members = await conversation.members();
130
+ const sender = members.find(
131
+ (m) => m.inboxId.toLowerCase() === inboxId.toLowerCase()
132
+ );
133
+ if (sender) {
134
+ const ethIdentifier = sender.accountIdentifiers.find(
135
+ (id) => id.identifierKind === 0
136
+ );
137
+ if (ethIdentifier) {
138
+ const address = ethIdentifier.identifier.toLowerCase();
139
+ this.addressCache.set(inboxId, address);
140
+ return address;
141
+ }
142
+ }
143
+ const inboxState = await this.agent?.client.preferences.inboxStateFromInboxIds([inboxId]);
144
+ const firstState = inboxState?.[0];
145
+ if (firstState?.identifiers?.[0]?.identifier) {
146
+ const address = firstState.identifiers[0].identifier.toLowerCase();
147
+ this.addressCache.set(inboxId, address);
148
+ return address;
149
+ }
150
+ } catch (err) {
151
+ log.warn(`failed to resolve address for ${inboxId.slice(0, 16)}...`);
152
+ }
153
+ return inboxId;
154
+ }
155
+ async isSenderAllowed(senderAddress) {
156
+ if (!this.config.workspaceDir) {
157
+ return true;
158
+ }
159
+ try {
160
+ const allowFrom = await (0, import_memory.readACLAllowFrom)(this.config.workspaceDir);
161
+ if (allowFrom.length === 0) {
162
+ return true;
163
+ }
164
+ const normalized = senderAddress.toLowerCase();
165
+ return allowFrom.includes(normalized);
166
+ } catch (err) {
167
+ log.warn(`failed to read ACL, allowing sender: ${err.message}`);
168
+ return true;
169
+ }
170
+ }
171
+ async handleInbound(conversation, message) {
172
+ log.info(`message ${import_picocolors.default.gray(message.id.slice(0, 8))}`);
173
+ if (this.processedMessages.has(message.id)) {
174
+ log.warn(`skipping duplicate: ${message.id.slice(0, 8)}`);
175
+ return;
176
+ }
177
+ this.processedMessages.add(message.id);
178
+ if (this.processedMessages.size > 1e3) {
179
+ const arr = Array.from(this.processedMessages);
180
+ arr.slice(0, 500).forEach((id) => this.processedMessages.delete(id));
181
+ }
182
+ const senderAddress = await this.resolveSenderAddress(
183
+ message.senderInboxId,
184
+ conversation
185
+ );
186
+ const isAllowed = await this.isSenderAllowed(senderAddress);
187
+ if (!isAllowed) {
188
+ log.warn(
189
+ `blocked message from ${senderAddress.slice(0, 10)}... (not on allowlist)`
190
+ );
191
+ return;
192
+ }
193
+ await this.runAgentAndReply({
194
+ conversationId: conversation.id,
195
+ message: message.content,
196
+ conversation
197
+ });
198
+ }
199
+ async trigger(req) {
200
+ return this.handleTrigger(req);
201
+ }
202
+ async handleTrigger(req) {
203
+ if (!this.agent) {
204
+ return { delivered: false, error: "XMTP client not initialized" };
205
+ }
206
+ const conversations = await this.agent.client.conversations.list();
207
+ const conversation = conversations.find(
208
+ (c) => c.id === req.to
209
+ );
210
+ if (!conversation) {
211
+ return { delivered: false, error: "Conversation not found" };
212
+ }
213
+ return this.runAgentAndReply({
214
+ conversationId: req.to,
215
+ message: req.message,
216
+ conversation
217
+ });
218
+ }
219
+ async runAgentAndReply(params) {
220
+ const { conversation, message, conversationId } = params;
221
+ try {
222
+ const res = await fetch(`${this.config.agentUrl}/api/chat`, {
223
+ method: "POST",
224
+ headers: {
225
+ "Content-Type": "application/json",
226
+ "X-Request-ID": (0, import_node_crypto.randomUUID)(),
227
+ "X-Source": "xmtp-adapter"
228
+ },
229
+ body: JSON.stringify({
230
+ messages: [{ id: (0, import_node_crypto.randomUUID)(), role: "user", content: message }],
231
+ chatId: conversationId
232
+ })
233
+ });
234
+ if (!res.ok) {
235
+ log.error(`HTTP ${res.status}`);
236
+ return { delivered: false, error: `HTTP ${res.status}` };
237
+ }
238
+ const reader = res.body?.getReader();
239
+ if (!reader) {
240
+ return { delivered: false, error: "No response body" };
241
+ }
242
+ const decoder = new TextDecoder();
243
+ let reply = "";
244
+ while (true) {
245
+ const { done, value } = await reader.read();
246
+ if (done) break;
247
+ for (const line of decoder.decode(value).split("\n")) {
248
+ if (line.startsWith("data: ") && line !== "data: [DONE]") {
249
+ try {
250
+ const p = JSON.parse(line.slice(6));
251
+ if (p.type === "text" && p.content) reply += p.content;
252
+ } catch {
253
+ }
254
+ }
255
+ }
256
+ }
257
+ if (reply) {
258
+ await conversation.send(reply);
259
+ log.success(`replied (${reply.length} chars)`);
260
+ return { delivered: true };
261
+ }
262
+ return { delivered: false, error: "No reply generated" };
263
+ } catch (err) {
264
+ log.error(err.message);
265
+ return { delivered: false, error: err.message };
266
+ }
267
+ }
268
+ };
269
+ }
270
+ });
271
+
272
+ // src/adapters/xmtp/index.ts
273
+ var xmtp_exports = {};
274
+ __export(xmtp_exports, {
275
+ XMTPAdapter: () => XMTPAdapter,
276
+ createXMTPAdapter: () => createXMTPAdapter
277
+ });
278
+ module.exports = __toCommonJS(xmtp_exports);
279
+ var import_node_fs = __toESM(require("fs"), 1);
280
+ var import_node_path = __toESM(require("path"), 1);
281
+ var import_xmtp = require("@hybrd/xmtp");
282
+ var import_memory2 = require("@hybrid/memory");
283
+ var import_agent_sdk2 = require("@xmtp/agent-sdk");
284
+ var import_picocolors2 = __toESM(require("picocolors"), 1);
285
+ init_adapter();
286
+ var log2 = {
287
+ info: (msg) => console.log(`${import_picocolors2.default.magenta("[xmtp]")} ${msg}`),
288
+ error: (msg) => console.error(`${import_picocolors2.default.red("[xmtp]")} ${msg}`),
289
+ warn: (msg) => console.log(`${import_picocolors2.default.yellow("[xmtp]")} ${msg}`),
290
+ success: (msg) => console.log(`${import_picocolors2.default.green("[xmtp]")} ${msg}`)
291
+ };
292
+ var AGENT_PORT = process.env.AGENT_PORT || "8454";
293
+ var XMTP_ENV = process.env.XMTP_ENV || "dev";
294
+ var XMTP_ADAPTER_PORT = Number.parseInt(
295
+ process.env.XMTP_ADAPTER_PORT || "8455",
296
+ 10
297
+ );
298
+ process.on("uncaughtException", (err) => {
299
+ log2.error(`FATAL: ${err.message}`);
300
+ process.exit(1);
301
+ });
302
+ process.on("unhandledRejection", (reason) => {
303
+ log2.error(`FATAL: ${reason}`);
304
+ process.exit(1);
305
+ });
306
+ function printBanner(walletAddress, aclCount) {
307
+ const isHotReload = process.env.TSX_WATCH === "true";
308
+ console.log("");
309
+ console.log(
310
+ import_picocolors2.default.magenta(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E")
311
+ );
312
+ console.log(
313
+ import_picocolors2.default.magenta(" \u2502") + import_picocolors2.default.bold(import_picocolors2.default.white(" XMTP Channel Adapter")) + import_picocolors2.default.magenta(" \u2502")
314
+ );
315
+ console.log(
316
+ import_picocolors2.default.magenta(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F")
317
+ );
318
+ console.log("");
319
+ console.log(
320
+ ` ${import_picocolors2.default.bold("Network")} ${XMTP_ENV === "production" ? import_picocolors2.default.green("production") : import_picocolors2.default.cyan("dev")}`
321
+ );
322
+ console.log(
323
+ ` ${import_picocolors2.default.bold("Wallet")} ${walletAddress ? import_picocolors2.default.cyan(walletAddress) : import_picocolors2.default.gray("(not configured)")}`
324
+ );
325
+ console.log(` ${import_picocolors2.default.bold("Agent")} http://localhost:${AGENT_PORT}`);
326
+ console.log(
327
+ ` ${import_picocolors2.default.bold("Trigger")} http://127.0.0.1:${XMTP_ADAPTER_PORT}/api/trigger`
328
+ );
329
+ if (aclCount !== void 0) {
330
+ const aclStatus = aclCount > 0 ? import_picocolors2.default.green(`${aclCount} allowed`) : import_picocolors2.default.yellow("open (no allowlist)");
331
+ console.log(` ${import_picocolors2.default.bold("ACL")} ${aclStatus}`);
332
+ }
333
+ console.log("");
334
+ if (isHotReload) {
335
+ console.log(
336
+ ` ${import_picocolors2.default.yellow("\u26A1")} Hot reload enabled - watching for changes...`
337
+ );
338
+ } else {
339
+ console.log(` ${import_picocolors2.default.green("\u2713")} Listening for messages...`);
340
+ }
341
+ console.log("");
342
+ }
343
+ async function start() {
344
+ const key = process.env.AGENT_WALLET_KEY;
345
+ if (!key) {
346
+ log2.warn("AGENT_WALLET_KEY not set");
347
+ printBanner();
348
+ await new Promise(() => {
349
+ });
350
+ return;
351
+ }
352
+ const user = (0, import_agent_sdk2.createUser)(key);
353
+ let aclCount;
354
+ try {
355
+ const allowFrom = await (0, import_memory2.readACLAllowFrom)(process.cwd());
356
+ aclCount = allowFrom.length;
357
+ } catch {
358
+ aclCount = 0;
359
+ }
360
+ printBanner(user.account.address, aclCount);
361
+ const secret = (0, import_xmtp.resolveAgentSecret)(key);
362
+ const dbEncryptionKey = new Uint8Array(Buffer.from(secret, "hex"));
363
+ const dbDir = import_node_path.default.join(process.cwd(), ".hybrid", ".xmtp");
364
+ if (!import_node_fs.default.existsSync(dbDir)) {
365
+ import_node_fs.default.mkdirSync(dbDir, { recursive: true });
366
+ }
367
+ const dbPath = import_node_path.default.join(
368
+ dbDir,
369
+ `xmtp-${XMTP_ENV}-${user.account.address.toLowerCase().slice(0, 8)}.db3`
370
+ );
371
+ const config = {
372
+ port: XMTP_ADAPTER_PORT,
373
+ agentUrl: `http://localhost:${AGENT_PORT}`,
374
+ xmtpEnv: XMTP_ENV,
375
+ walletKey: key,
376
+ dbEncryptionKey,
377
+ dbPath,
378
+ workspaceDir: process.cwd()
379
+ };
380
+ await Promise.resolve().then(() => (init_adapter(), adapter_exports)).then(
381
+ ({ createXMTPAdapter: createXMTPAdapter2 }) => createXMTPAdapter2(config)
382
+ );
383
+ }
384
+ start().catch((e) => {
385
+ log2.error(e.message);
386
+ process.exit(1);
387
+ });
388
+ // Annotate the CommonJS export names for ESM import in node:
389
+ 0 && (module.exports = {
390
+ XMTPAdapter,
391
+ createXMTPAdapter
392
+ });
393
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/adapters/xmtp/adapter.ts","../../../src/adapters/xmtp/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\"\nimport type { Server } from \"node:http\"\nimport type {\n\tChannelAdapter,\n\tChannelId,\n\tTriggerRequest,\n\tTriggerResponse\n} from \"@hybrd/types\"\nimport { readACLAllowFrom } from \"@hybrid/memory\"\nimport { Agent } from \"@xmtp/agent-sdk\"\nimport type { Conversation } from \"@xmtp/node-sdk\"\nimport express from \"express\"\nimport pc from \"picocolors\"\n\nconst log = {\n\tinfo: (msg: string) => console.log(`${pc.magenta(\"[xmtp]\")} ${msg}`),\n\terror: (msg: string) => console.error(`${pc.red(\"[xmtp]\")} ${msg}`),\n\twarn: (msg: string) => console.log(`${pc.yellow(\"[xmtp]\")} ${msg}`),\n\tsuccess: (msg: string) => console.log(`${pc.green(\"[xmtp]\")} ${msg}`)\n}\n\nexport interface XMTPAdapterConfig {\n\tport: number\n\tagentUrl: string\n\txmtpEnv?: \"dev\" | \"production\"\n\twalletKey: `0x${string}`\n\tdbEncryptionKey: Uint8Array\n\tdbPath: string\n\tworkspaceDir?: string\n}\n\ninterface TextMessage {\n\tid: string\n\tcontent: string\n\tsenderInboxId: string\n}\n\nexport class XMTPAdapter implements ChannelAdapter {\n\treadonly channel: ChannelId = \"xmtp\"\n\treadonly port: number\n\n\tprivate config: XMTPAdapterConfig\n\tprivate agent: Agent | null = null\n\tprivate server: Server | null = null\n\tprivate app: express.Application\n\tprivate botInboxId: string | null = null\n\tprivate processedMessages: Set<string> = new Set()\n\tprivate addressCache: Map<string, string> = new Map()\n\n\tconstructor(config: XMTPAdapterConfig) {\n\t\tthis.port = config.port\n\t\tthis.config = config\n\t\tthis.app = express()\n\t\tthis.app.use(express.json())\n\t}\n\n\tasync start(): Promise<void> {\n\t\tawait this.startXMTPClient()\n\t\tthis.startTriggerServer()\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tthis.server?.close()\n\t}\n\n\tprivate async startXMTPClient(): Promise<void> {\n\t\tconst { createUser } = await import(\"@xmtp/agent-sdk\")\n\t\tconst { toBytes } = await import(\"viem\")\n\n\t\tconst user = createUser(this.config.walletKey)\n\t\tconst identifier = {\n\t\t\tidentifier: user.account.address.toLowerCase(),\n\t\t\tidentifierKind: 0\n\t\t}\n\n\t\tconst signer = {\n\t\t\ttype: \"EOA\" as const,\n\t\t\tgetIdentifier: () => identifier,\n\t\t\tgetChainId: async () => BigInt(1),\n\t\t\tsignMessage: async (message: string) => {\n\t\t\t\tconst sig = await user.account.signMessage({ message })\n\t\t\t\treturn toBytes(sig)\n\t\t\t}\n\t\t}\n\n\t\tthis.agent = await Agent.create(\n\t\t\tsigner as unknown as Parameters<typeof Agent.create>[0],\n\t\t\t{\n\t\t\t\tenv: this.config.xmtpEnv ?? \"dev\",\n\t\t\t\tdbEncryptionKey: this.config.dbEncryptionKey,\n\t\t\t\tdbPath: this.config.dbPath\n\t\t\t}\n\t\t)\n\n\t\tlog.success(\"connected to XMTP network\")\n\n\t\tthis.botInboxId = this.agent.client.inboxId\n\n\t\tthis.agent.on(\"text\", async (ctx) => {\n\t\t\tconst { conversation, message } = ctx\n\t\t\tawait this.handleInbound(conversation, message as TextMessage)\n\t\t})\n\n\t\tawait this.agent.start()\n\t\tlog.success(\"listening for XMTP messages\")\n\t}\n\n\tprivate startTriggerServer(): void {\n\t\tthis.app.post(\"/api/trigger\", async (req, res) => {\n\t\t\tconst result = await this.handleTrigger(req.body)\n\t\t\tres.json(result)\n\t\t})\n\n\t\tthis.server = this.app.listen(this.port, \"127.0.0.1\", () => {\n\t\t\tlog.success(`trigger server listening on 127.0.0.1:${this.port}`)\n\t\t})\n\t}\n\n\tprivate async resolveSenderAddress(\n\t\tinboxId: string,\n\t\tconversation: Conversation\n\t): Promise<string> {\n\t\tconst cached = this.addressCache.get(inboxId)\n\t\tif (cached) return cached\n\n\t\ttry {\n\t\t\tconst members = await conversation.members()\n\t\t\tconst sender = members.find(\n\t\t\t\t(m: any) => m.inboxId.toLowerCase() === inboxId.toLowerCase()\n\t\t\t)\n\n\t\t\tif (sender) {\n\t\t\t\tconst ethIdentifier = sender.accountIdentifiers.find(\n\t\t\t\t\t(id: any) => id.identifierKind === 0\n\t\t\t\t)\n\t\t\t\tif (ethIdentifier) {\n\t\t\t\t\tconst address = ethIdentifier.identifier.toLowerCase()\n\t\t\t\t\tthis.addressCache.set(inboxId, address)\n\t\t\t\t\treturn address\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst inboxState =\n\t\t\t\tawait this.agent?.client.preferences.inboxStateFromInboxIds([inboxId])\n\t\t\tconst firstState = inboxState?.[0]\n\t\t\tif (firstState?.identifiers?.[0]?.identifier) {\n\t\t\t\tconst address = firstState.identifiers[0].identifier.toLowerCase()\n\t\t\t\tthis.addressCache.set(inboxId, address)\n\t\t\t\treturn address\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tlog.warn(`failed to resolve address for ${inboxId.slice(0, 16)}...`)\n\t\t}\n\n\t\treturn inboxId\n\t}\n\n\tprivate async isSenderAllowed(senderAddress: string): Promise<boolean> {\n\t\tif (!this.config.workspaceDir) {\n\t\t\treturn true\n\t\t}\n\n\t\ttry {\n\t\t\tconst allowFrom = await readACLAllowFrom(this.config.workspaceDir)\n\t\t\tif (allowFrom.length === 0) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst normalized = senderAddress.toLowerCase()\n\t\t\treturn allowFrom.includes(normalized)\n\t\t} catch (err) {\n\t\t\tlog.warn(`failed to read ACL, allowing sender: ${(err as Error).message}`)\n\t\t\treturn true\n\t\t}\n\t}\n\n\tprivate async handleInbound(\n\t\tconversation: Conversation,\n\t\tmessage: TextMessage\n\t): Promise<void> {\n\t\tlog.info(`message ${pc.gray(message.id.slice(0, 8))}`)\n\n\t\tif (this.processedMessages.has(message.id)) {\n\t\t\tlog.warn(`skipping duplicate: ${message.id.slice(0, 8)}`)\n\t\t\treturn\n\t\t}\n\t\tthis.processedMessages.add(message.id)\n\n\t\tif (this.processedMessages.size > 1000) {\n\t\t\tconst arr = Array.from(this.processedMessages)\n\t\t\tarr.slice(0, 500).forEach((id) => this.processedMessages.delete(id))\n\t\t}\n\n\t\t// Check if sender is on the allowlist\n\t\tconst senderAddress = await this.resolveSenderAddress(\n\t\t\tmessage.senderInboxId,\n\t\t\tconversation\n\t\t)\n\t\tconst isAllowed = await this.isSenderAllowed(senderAddress)\n\n\t\tif (!isAllowed) {\n\t\t\tlog.warn(\n\t\t\t\t`blocked message from ${senderAddress.slice(0, 10)}... (not on allowlist)`\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tawait this.runAgentAndReply({\n\t\t\tconversationId: conversation.id,\n\t\t\tmessage: message.content,\n\t\t\tconversation\n\t\t})\n\t}\n\n\tasync trigger(req: TriggerRequest): Promise<TriggerResponse> {\n\t\treturn this.handleTrigger(req)\n\t}\n\n\tprivate async handleTrigger(req: TriggerRequest): Promise<TriggerResponse> {\n\t\tif (!this.agent) {\n\t\t\treturn { delivered: false, error: \"XMTP client not initialized\" }\n\t\t}\n\n\t\tconst conversations = await this.agent.client.conversations.list()\n\t\tconst conversation = conversations.find(\n\t\t\t(c: Conversation) => c.id === req.to\n\t\t)\n\n\t\tif (!conversation) {\n\t\t\treturn { delivered: false, error: \"Conversation not found\" }\n\t\t}\n\n\t\treturn this.runAgentAndReply({\n\t\t\tconversationId: req.to,\n\t\t\tmessage: req.message,\n\t\t\tconversation\n\t\t})\n\t}\n\n\tprivate async runAgentAndReply(params: {\n\t\tconversationId: string\n\t\tmessage: string\n\t\tconversation: Conversation\n\t}): Promise<TriggerResponse> {\n\t\tconst { conversation, message, conversationId } = params\n\n\t\ttry {\n\t\t\tconst res = await fetch(`${this.config.agentUrl}/api/chat`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"X-Request-ID\": randomUUID(),\n\t\t\t\t\t\"X-Source\": \"xmtp-adapter\"\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmessages: [{ id: randomUUID(), role: \"user\", content: message }],\n\t\t\t\t\tchatId: conversationId\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tif (!res.ok) {\n\t\t\t\tlog.error(`HTTP ${res.status}`)\n\t\t\t\treturn { delivered: false, error: `HTTP ${res.status}` }\n\t\t\t}\n\n\t\t\tconst reader = res.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\treturn { delivered: false, error: \"No response body\" }\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet reply = \"\"\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tfor (const line of decoder.decode(value).split(\"\\n\")) {\n\t\t\t\t\tif (line.startsWith(\"data: \") && line !== \"data: [DONE]\") {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst p = JSON.parse(line.slice(6))\n\t\t\t\t\t\t\tif (p.type === \"text\" && p.content) reply += p.content\n\t\t\t\t\t\t} catch {}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (reply) {\n\t\t\t\tawait conversation.send(reply)\n\t\t\t\tlog.success(`replied (${reply.length} chars)`)\n\t\t\t\treturn { delivered: true }\n\t\t\t}\n\n\t\t\treturn { delivered: false, error: \"No reply generated\" }\n\t\t} catch (err) {\n\t\t\tlog.error((err as Error).message)\n\t\t\treturn { delivered: false, error: (err as Error).message }\n\t\t}\n\t}\n}\n\nexport async function createXMTPAdapter(\n\tconfig: XMTPAdapterConfig\n): Promise<XMTPAdapter> {\n\tconst adapter = new XMTPAdapter(config)\n\tawait adapter.start()\n\treturn adapter\n}\n","import fs from \"node:fs\"\nimport path from \"node:path\"\nimport { resolveAgentSecret } from \"@hybrd/xmtp\"\nimport { readACLAllowFrom } from \"@hybrid/memory\"\nimport { createUser } from \"@xmtp/agent-sdk\"\nimport pc from \"picocolors\"\nimport { type XMTPAdapterConfig } from \"./adapter.js\"\n\nconst log = {\n\tinfo: (msg: string) => console.log(`${pc.magenta(\"[xmtp]\")} ${msg}`),\n\terror: (msg: string) => console.error(`${pc.red(\"[xmtp]\")} ${msg}`),\n\twarn: (msg: string) => console.log(`${pc.yellow(\"[xmtp]\")} ${msg}`),\n\tsuccess: (msg: string) => console.log(`${pc.green(\"[xmtp]\")} ${msg}`)\n}\n\nconst AGENT_PORT = process.env.AGENT_PORT || \"8454\"\nconst XMTP_ENV = (process.env.XMTP_ENV || \"dev\") as \"dev\" | \"production\"\nconst XMTP_ADAPTER_PORT = Number.parseInt(\n\tprocess.env.XMTP_ADAPTER_PORT || \"8455\",\n\t10\n)\n\nprocess.on(\"uncaughtException\", (err) => {\n\tlog.error(`FATAL: ${err.message}`)\n\tprocess.exit(1)\n})\n\nprocess.on(\"unhandledRejection\", (reason) => {\n\tlog.error(`FATAL: ${reason}`)\n\tprocess.exit(1)\n})\n\nfunction printBanner(walletAddress?: string, aclCount?: number) {\n\tconst isHotReload = process.env.TSX_WATCH === \"true\"\n\n\tconsole.log(\"\")\n\tconsole.log(\n\t\tpc.magenta(\" ╭───────────────────────────────────────────────────╮\")\n\t)\n\tconsole.log(\n\t\tpc.magenta(\" │\") +\n\t\t\tpc.bold(pc.white(\" XMTP Channel Adapter\")) +\n\t\t\tpc.magenta(\" │\")\n\t)\n\tconsole.log(\n\t\tpc.magenta(\" ╰───────────────────────────────────────────────────╯\")\n\t)\n\tconsole.log(\"\")\n\tconsole.log(\n\t\t` ${pc.bold(\"Network\")} ${XMTP_ENV === \"production\" ? pc.green(\"production\") : pc.cyan(\"dev\")}`\n\t)\n\tconsole.log(\n\t\t` ${pc.bold(\"Wallet\")} ${walletAddress ? pc.cyan(walletAddress) : pc.gray(\"(not configured)\")}`\n\t)\n\tconsole.log(` ${pc.bold(\"Agent\")} http://localhost:${AGENT_PORT}`)\n\tconsole.log(\n\t\t` ${pc.bold(\"Trigger\")} http://127.0.0.1:${XMTP_ADAPTER_PORT}/api/trigger`\n\t)\n\tif (aclCount !== undefined) {\n\t\tconst aclStatus =\n\t\t\taclCount > 0\n\t\t\t\t? pc.green(`${aclCount} allowed`)\n\t\t\t\t: pc.yellow(\"open (no allowlist)\")\n\t\tconsole.log(` ${pc.bold(\"ACL\")} ${aclStatus}`)\n\t}\n\tconsole.log(\"\")\n\n\tif (isHotReload) {\n\t\tconsole.log(\n\t\t\t` ${pc.yellow(\"⚡\")} Hot reload enabled - watching for changes...`\n\t\t)\n\t} else {\n\t\tconsole.log(` ${pc.green(\"✓\")} Listening for messages...`)\n\t}\n\tconsole.log(\"\")\n}\n\nasync function start() {\n\tconst key = process.env.AGENT_WALLET_KEY\n\n\tif (!key) {\n\t\tlog.warn(\"AGENT_WALLET_KEY not set\")\n\t\tprintBanner()\n\t\tawait new Promise(() => {})\n\t\treturn\n\t}\n\n\tconst user = createUser(key as `0x${string}`)\n\n\t// Read ACL count for banner\n\tlet aclCount: number | undefined\n\ttry {\n\t\tconst allowFrom = await readACLAllowFrom(process.cwd())\n\t\taclCount = allowFrom.length\n\t} catch {\n\t\t// ACL doesn't exist yet, that's fine\n\t\taclCount = 0\n\t}\n\n\tprintBanner(user.account.address, aclCount)\n\n\tconst secret = resolveAgentSecret(key)\n\tconst dbEncryptionKey = new Uint8Array(Buffer.from(secret, \"hex\"))\n\n\tconst dbDir = path.join(process.cwd(), \".hybrid\", \".xmtp\")\n\tif (!fs.existsSync(dbDir)) {\n\t\tfs.mkdirSync(dbDir, { recursive: true })\n\t}\n\tconst dbPath = path.join(\n\t\tdbDir,\n\t\t`xmtp-${XMTP_ENV}-${user.account.address.toLowerCase().slice(0, 8)}.db3`\n\t)\n\n\tconst config: XMTPAdapterConfig = {\n\t\tport: XMTP_ADAPTER_PORT,\n\t\tagentUrl: `http://localhost:${AGENT_PORT}`,\n\t\txmtpEnv: XMTP_ENV,\n\t\twalletKey: key as `0x${string}`,\n\t\tdbEncryptionKey,\n\t\tdbPath,\n\t\tworkspaceDir: process.cwd()\n\t}\n\n\tawait import(\"./adapter.js\").then(({ createXMTPAdapter }) =>\n\t\tcreateXMTPAdapter(config)\n\t)\n}\n\nstart().catch((e) => {\n\tlog.error(e.message)\n\tprocess.exit(1)\n})\n\nexport {\n\tXMTPAdapter,\n\ttype XMTPAdapterConfig,\n\tcreateXMTPAdapter\n} from \"./adapter.js\"\n"],"mappings":"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","names":["pc","express","createUser","import_memory","import_agent_sdk","import_picocolors","log","pc","path","fs","createXMTPAdapter"]}
@@ -0,0 +1,36 @@
1
+ import { ChannelAdapter, ChannelId, TriggerRequest, TriggerResponse } from '@hybrd/types';
2
+
3
+ interface XMTPAdapterConfig {
4
+ port: number;
5
+ agentUrl: string;
6
+ xmtpEnv?: "dev" | "production";
7
+ walletKey: `0x${string}`;
8
+ dbEncryptionKey: Uint8Array;
9
+ dbPath: string;
10
+ workspaceDir?: string;
11
+ }
12
+ declare class XMTPAdapter implements ChannelAdapter {
13
+ readonly channel: ChannelId;
14
+ readonly port: number;
15
+ private config;
16
+ private agent;
17
+ private server;
18
+ private app;
19
+ private botInboxId;
20
+ private processedMessages;
21
+ private addressCache;
22
+ constructor(config: XMTPAdapterConfig);
23
+ start(): Promise<void>;
24
+ stop(): Promise<void>;
25
+ private startXMTPClient;
26
+ private startTriggerServer;
27
+ private resolveSenderAddress;
28
+ private isSenderAllowed;
29
+ private handleInbound;
30
+ trigger(req: TriggerRequest): Promise<TriggerResponse>;
31
+ private handleTrigger;
32
+ private runAgentAndReply;
33
+ }
34
+ declare function createXMTPAdapter(config: XMTPAdapterConfig): Promise<XMTPAdapter>;
35
+
36
+ export { XMTPAdapter, type XMTPAdapterConfig, createXMTPAdapter };