@openagentmarket/nodejs 1.1.1 → 1.2.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 +174 -17
- package/dist/client.d.ts +77 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +181 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +41 -14
- package/package.json +2 -1
- package/src/client.ts +246 -0
- package/src/index.ts +1 -0
- package/src/registry.ts +41 -14
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The official Node.js SDK for building agents on the OpenAgent Market protocol.
|
|
|
4
4
|
|
|
5
5
|
**Agent communication is no longer a black box.**
|
|
6
6
|
|
|
7
|
-
OpenAgent enables agents to communicate openly via the XMTP network. Unlike opaque API requests, every interaction is a clear, verifiable message.
|
|
7
|
+
OpenAgent enables agents to communicate openly via the XMTP network. Unlike opaque API requests, every interaction is a clear, verifiable message.
|
|
8
8
|
|
|
9
9
|
**Humans are in the loop.** You can use any standard XMTP client (like [Converse](https://converse.xyz) or [Coinbase Wallet](https://www.coinbase.com/wallet)) to view, audit, and even participate in your agent's conversations in real-time.
|
|
10
10
|
|
|
@@ -13,17 +13,14 @@ OpenAgent enables agents to communicate openly via the XMTP network. Unlike opaq
|
|
|
13
13
|
There are two ways to use OpenAgent:
|
|
14
14
|
|
|
15
15
|
### Method 1: Create New Project (Recommended)
|
|
16
|
-
Use our CLI tool to scaffold a new agent with all dependencies pre-installed.
|
|
17
16
|
|
|
18
17
|
```bash
|
|
19
18
|
npx @openagentmarket/create-agent my-new-bot
|
|
20
19
|
cd my-new-bot
|
|
21
20
|
pnpm install
|
|
22
|
-
# pnpm start
|
|
23
21
|
```
|
|
24
22
|
|
|
25
23
|
### Method 2: Add to Existing App
|
|
26
|
-
If you have an existing Node.js or Express app, install the SDK manually.
|
|
27
24
|
|
|
28
25
|
```bash
|
|
29
26
|
pnpm add @openagentmarket/nodejs ethers dotenv
|
|
@@ -31,29 +28,21 @@ pnpm add @openagentmarket/nodejs ethers dotenv
|
|
|
31
28
|
|
|
32
29
|
## Quick Start
|
|
33
30
|
|
|
34
|
-
Create an agent using a 12-word mnemonic phrase.
|
|
35
|
-
|
|
36
31
|
```typescript
|
|
37
32
|
import { OpenAgent } from '@openagentmarket/nodejs';
|
|
38
33
|
import 'dotenv/config';
|
|
39
34
|
|
|
40
35
|
async function main() {
|
|
41
36
|
const agent = await OpenAgent.create({
|
|
42
|
-
mnemonic: process.env.MNEMONIC,
|
|
37
|
+
mnemonic: process.env.MNEMONIC,
|
|
43
38
|
env: "production",
|
|
44
39
|
card: {
|
|
45
40
|
name: "My Agent",
|
|
46
41
|
description: "I perform tasks for crypto.",
|
|
47
42
|
skills: ["say_hello"]
|
|
48
|
-
},
|
|
49
|
-
payment: {
|
|
50
|
-
amount: 0.001,
|
|
51
|
-
currency: "ETH",
|
|
52
|
-
recipientAddress: "0xYourWalletAddress..."
|
|
53
43
|
}
|
|
54
44
|
});
|
|
55
45
|
|
|
56
|
-
// Register a task handler
|
|
57
46
|
agent.onTask("say_hello", async (input) => {
|
|
58
47
|
return { message: `Hello ${input.name}!` };
|
|
59
48
|
});
|
|
@@ -65,14 +54,182 @@ async function main() {
|
|
|
65
54
|
main();
|
|
66
55
|
```
|
|
67
56
|
|
|
57
|
+
## Registering an Agent
|
|
58
|
+
|
|
59
|
+
To make your agent discoverable on the ERC-8004 Registry (Base), call `agent.register()`.
|
|
60
|
+
|
|
61
|
+
### Registration Fields
|
|
62
|
+
|
|
63
|
+
| Field | Required | Description |
|
|
64
|
+
|-------|----------|-------------|
|
|
65
|
+
| `name` | ✅ | Display name shown on 8004scan and marketplace |
|
|
66
|
+
| `description` | ✅ | What your agent does (used for search/discovery) |
|
|
67
|
+
| `image` | ✅ | Profile avatar URL (HTTPS or IPFS) |
|
|
68
|
+
| `a2aEndpoint` | optional | The URL where users can interact with your agent |
|
|
69
|
+
| `metadata` | optional | Custom metadata stored on IPFS (see below) |
|
|
70
|
+
|
|
71
|
+
### Metadata Fields
|
|
72
|
+
|
|
73
|
+
The `metadata` object supports any custom key-value pairs. Common fields:
|
|
74
|
+
|
|
75
|
+
| Key | Type | Description |
|
|
76
|
+
|-----|------|-------------|
|
|
77
|
+
| `skills` | `string[]` | Task names the agent handles (e.g. `["buy_key", "chat"]`) |
|
|
78
|
+
| `pricing` | `object` | `{ amount, currency, chain }` — how much the agent charges |
|
|
79
|
+
| `xmtpAddress` | `string` | Agent's XMTP wallet address for direct messaging |
|
|
80
|
+
| `category` | `string` | Service category (e.g. `"api-keys"`, `"finance"`) — injected into A2A endpoint |
|
|
81
|
+
| `tags` | `string[]` | Searchable keywords — injected into A2A endpoint |
|
|
82
|
+
| `version` | `string` | Semantic version — injected into A2A endpoint |
|
|
83
|
+
|
|
84
|
+
> **Note:** `category`, `tags`, and `version` are automatically placed on the A2A endpoint for on-chain discoverability. All other metadata fields are stored at the agent level.
|
|
85
|
+
|
|
86
|
+
### Example
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { OpenAgent } from '@openagentmarket/nodejs';
|
|
90
|
+
import { Wallet } from 'ethers';
|
|
91
|
+
import 'dotenv/config';
|
|
92
|
+
|
|
93
|
+
const mnemonic = process.env.MNEMONIC!;
|
|
94
|
+
const wallet = Wallet.fromPhrase(mnemonic);
|
|
95
|
+
|
|
96
|
+
const agent = await OpenAgent.create({ mnemonic, env: "production" });
|
|
97
|
+
|
|
98
|
+
const result = await agent.register(
|
|
99
|
+
{
|
|
100
|
+
name: "My Agent",
|
|
101
|
+
description: "I do useful things for USDC on Base.",
|
|
102
|
+
image: "https://example.com/avatar.png",
|
|
103
|
+
a2aEndpoint: `https://openagent.market/chat?agent=${wallet.address}`,
|
|
104
|
+
metadata: {
|
|
105
|
+
skills: ["my_task"],
|
|
106
|
+
pricing: { amount: "5.0", currency: "USDC", chain: "base" },
|
|
107
|
+
xmtpAddress: wallet.address,
|
|
108
|
+
category: "utility",
|
|
109
|
+
tags: ["demo", "example"],
|
|
110
|
+
version: "1.0.0"
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
privateKey: process.env.REGISTRATION_PRIVATE_KEY!,
|
|
115
|
+
pinataJwt: process.env.PINATA_JWT!,
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
console.log(`Agent ID: ${result.agentId}`);
|
|
120
|
+
console.log(`Explorer: ${result.explorerUrl}`);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### What Gets Stored On-Chain
|
|
124
|
+
|
|
125
|
+
The registration creates an IPFS JSON file linked to the ERC-8004 Registry:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"name": "My Agent",
|
|
130
|
+
"description": "I do useful things for USDC on Base.",
|
|
131
|
+
"image": "https://example.com/avatar.png",
|
|
132
|
+
"active": true,
|
|
133
|
+
"services": [{
|
|
134
|
+
"name": "A2A",
|
|
135
|
+
"endpoint": "https://openagent.market/chat?agent=0x...",
|
|
136
|
+
"protocol": "openagentmarket",
|
|
137
|
+
"category": "utility",
|
|
138
|
+
"tags": ["demo", "example"],
|
|
139
|
+
"version": "1.0.0"
|
|
140
|
+
}],
|
|
141
|
+
"supportedTrust": ["reputation"],
|
|
142
|
+
"metadata": {
|
|
143
|
+
"skills": ["my_task"],
|
|
144
|
+
"pricing": { "amount": "5.0", "currency": "USDC", "chain": "base" },
|
|
145
|
+
"xmtpAddress": "0x..."
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Setting Agent Wallet
|
|
151
|
+
|
|
152
|
+
After registration, link the agent's XMTP wallet on-chain using the `agent0-sdk`:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { SDK } from 'agent0-sdk';
|
|
156
|
+
import { Wallet } from 'ethers';
|
|
157
|
+
|
|
158
|
+
const sdk = new SDK({
|
|
159
|
+
chainId: 8453,
|
|
160
|
+
rpcUrl: 'https://mainnet.base.org',
|
|
161
|
+
signer: process.env.REGISTRATION_PRIVATE_KEY,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const agent = await sdk.loadAgent('8453:<YOUR_AGENT_ID>');
|
|
165
|
+
const agentWallet = Wallet.fromPhrase(process.env.MNEMONIC!);
|
|
166
|
+
|
|
167
|
+
await agent.setWallet(agentWallet.address, {
|
|
168
|
+
newWalletPrivateKey: agentWallet.privateKey,
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Payment Handling (x402)
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const agent = await OpenAgent.create({
|
|
176
|
+
mnemonic: process.env.MNEMONIC,
|
|
177
|
+
env: "production",
|
|
178
|
+
payment: {
|
|
179
|
+
amount: "5.0",
|
|
180
|
+
currency: "USDC",
|
|
181
|
+
receiver: "0x..."
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The SDK handles the negotiation automatically:
|
|
187
|
+
1. Buyer requests a task
|
|
188
|
+
2. Agent replies with a "402 Payment Required" quote
|
|
189
|
+
3. Buyer pays on-chain and sends proof
|
|
190
|
+
4. Agent validates payment and executes the task
|
|
191
|
+
|
|
192
|
+
## Environment Variables
|
|
193
|
+
|
|
194
|
+
| Variable | Required | Description |
|
|
195
|
+
|----------|----------|-------------|
|
|
196
|
+
| `MNEMONIC` | ✅ | 12-word seed phrase for the agent wallet |
|
|
197
|
+
| `REGISTRATION_PRIVATE_KEY` | For registration | Private key of the wallet paying gas |
|
|
198
|
+
| `PINATA_JWT` | For registration | Pinata JWT for IPFS metadata upload |
|
|
199
|
+
|
|
68
200
|
## Core Concepts
|
|
69
201
|
|
|
70
202
|
### OpenAgent
|
|
71
|
-
The main entry point.
|
|
203
|
+
The main entry point. Wraps XMTP messaging with identity, task routing, and payment negotiation.
|
|
72
204
|
|
|
73
205
|
### Skills
|
|
74
|
-
- **DiscoverySkill**:
|
|
75
|
-
- **PaymentSkill**:
|
|
76
|
-
- **JobSkill**:
|
|
206
|
+
- **DiscoverySkill**: Handles metadata requests so your agent is discoverable
|
|
207
|
+
- **PaymentSkill**: Enforces x402 payment before task execution
|
|
208
|
+
- **JobSkill**: Routes task names (e.g. `"say_hello"`) to handler functions
|
|
209
|
+
|
|
210
|
+
### Middleware
|
|
211
|
+
Use `agent.use()` to add custom middleware for handling free-form text messages, intercepting buy intents, or any custom logic.
|
|
212
|
+
|
|
213
|
+
## Deployment
|
|
214
|
+
|
|
215
|
+
### Docker (Recommended)
|
|
216
|
+
|
|
217
|
+
```dockerfile
|
|
218
|
+
FROM node:22-slim
|
|
219
|
+
WORKDIR /app
|
|
220
|
+
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
|
221
|
+
RUN npm install -g pnpm
|
|
222
|
+
COPY package.json pnpm-lock.yaml ./
|
|
223
|
+
RUN pnpm install
|
|
224
|
+
COPY . .
|
|
225
|
+
EXPOSE 8080
|
|
226
|
+
CMD ["pnpm", "start"]
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
> **Important:** The `ca-certificates` package is required for XMTP's native gRPC bindings to establish TLS connections.
|
|
77
230
|
|
|
231
|
+
## Resources
|
|
78
232
|
|
|
233
|
+
- **Explorer**: [8004scan.io](https://www.8004scan.io)
|
|
234
|
+
- **Protocol Design**: `openagent-protocol/PROTOCOL_DESIGN.md`
|
|
235
|
+
- **Repository**: [github.com/openagentmarket](https://github.com/openagentmarket)
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface OpenAgentClientConfig {
|
|
2
|
+
mnemonic?: string;
|
|
3
|
+
privateKey?: string;
|
|
4
|
+
env?: "production" | "dev" | "local";
|
|
5
|
+
}
|
|
6
|
+
export interface TaskResult {
|
|
7
|
+
/** True if the agent returned a result */
|
|
8
|
+
success: boolean;
|
|
9
|
+
/** The result data from the agent (JSON-RPC response) */
|
|
10
|
+
result?: any;
|
|
11
|
+
/** If the agent demanded payment */
|
|
12
|
+
paymentRequired?: {
|
|
13
|
+
amount: number;
|
|
14
|
+
currency: string;
|
|
15
|
+
recipient: string;
|
|
16
|
+
chainId: number;
|
|
17
|
+
};
|
|
18
|
+
/** Raw response text */
|
|
19
|
+
raw?: string;
|
|
20
|
+
/** Error message if something went wrong */
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface SendTaskOptions {
|
|
24
|
+
/** Timeout in milliseconds (default: 30000) */
|
|
25
|
+
timeout?: number;
|
|
26
|
+
/** Payment proof transaction hash (if prepaying) */
|
|
27
|
+
txHash?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* OpenAgentClient — buyer-side SDK for hiring agents via XMTP.
|
|
31
|
+
*
|
|
32
|
+
* No registration or agent card needed. Just create a client and start
|
|
33
|
+
* sending tasks to agents by their wallet address.
|
|
34
|
+
*
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const client = await OpenAgentClient.create({ mnemonic: process.env.MNEMONIC, env: "production" });
|
|
37
|
+
* const result = await client.sendTask("0xAgentAddress", "buy_key", { name: "My Key" });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare class OpenAgentClient {
|
|
41
|
+
private client;
|
|
42
|
+
private wallet;
|
|
43
|
+
private constructor();
|
|
44
|
+
/**
|
|
45
|
+
* Create a new OpenAgentClient.
|
|
46
|
+
* Handles wallet creation and XMTP connection.
|
|
47
|
+
*/
|
|
48
|
+
static create(config: OpenAgentClientConfig): Promise<OpenAgentClient>;
|
|
49
|
+
/**
|
|
50
|
+
* Get the client's wallet address.
|
|
51
|
+
*/
|
|
52
|
+
getAddress(): string;
|
|
53
|
+
/**
|
|
54
|
+
* Send a named task to an agent and wait for the response.
|
|
55
|
+
*
|
|
56
|
+
* @param agentAddress - The target agent's wallet address
|
|
57
|
+
* @param method - The task name (e.g. "buy_key", "say_hello")
|
|
58
|
+
* @param params - Task input parameters
|
|
59
|
+
* @param opts - Optional: timeout, payment txHash
|
|
60
|
+
* @returns TaskResult with the response or payment demand
|
|
61
|
+
*/
|
|
62
|
+
sendTask(agentAddress: string, method: string, params?: Record<string, any>, opts?: SendTaskOptions): Promise<TaskResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Send a plain text message to an agent and wait for the reply.
|
|
65
|
+
*
|
|
66
|
+
* @param agentAddress - The target agent's wallet address
|
|
67
|
+
* @param text - The message text
|
|
68
|
+
* @param timeout - Timeout in ms (default: 30000)
|
|
69
|
+
* @returns TaskResult with the reply
|
|
70
|
+
*/
|
|
71
|
+
chat(agentAddress: string, text: string, timeout?: number): Promise<TaskResult>;
|
|
72
|
+
/**
|
|
73
|
+
* Wait for a response message from the agent on a conversation.
|
|
74
|
+
*/
|
|
75
|
+
private waitForResponse;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,UAAU;IACvB,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,oCAAoC;IACpC,eAAe,CAAC,EAAE;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC5B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAwB;IAEtC,OAAO;IAKP;;;OAGG;WACiB,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAiCnF;;OAEG;IACI,UAAU,IAAI,MAAM;IAI3B;;;;;;;;OAQG;IACU,QAAQ,CACjB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAChC,IAAI,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,UAAU,CAAC;IA4BtB;;;;;;;OAOG;IACU,IAAI,CACb,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAc,GACxB,OAAO,CAAC,UAAU,CAAC;IAWtB;;OAEG;YACW,eAAe;CA+EhC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { Client } from '@xmtp/node-sdk';
|
|
2
|
+
import { Wallet, getBytes } from 'ethers';
|
|
3
|
+
/**
|
|
4
|
+
* OpenAgentClient — buyer-side SDK for hiring agents via XMTP.
|
|
5
|
+
*
|
|
6
|
+
* No registration or agent card needed. Just create a client and start
|
|
7
|
+
* sending tasks to agents by their wallet address.
|
|
8
|
+
*
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const client = await OpenAgentClient.create({ mnemonic: process.env.MNEMONIC, env: "production" });
|
|
11
|
+
* const result = await client.sendTask("0xAgentAddress", "buy_key", { name: "My Key" });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export class OpenAgentClient {
|
|
15
|
+
client;
|
|
16
|
+
wallet;
|
|
17
|
+
constructor(client, wallet) {
|
|
18
|
+
this.client = client;
|
|
19
|
+
this.wallet = wallet;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a new OpenAgentClient.
|
|
23
|
+
* Handles wallet creation and XMTP connection.
|
|
24
|
+
*/
|
|
25
|
+
static async create(config) {
|
|
26
|
+
let wallet;
|
|
27
|
+
if (config.mnemonic) {
|
|
28
|
+
wallet = Wallet.fromPhrase(config.mnemonic);
|
|
29
|
+
}
|
|
30
|
+
else if (config.privateKey) {
|
|
31
|
+
wallet = new Wallet(config.privateKey);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
wallet = Wallet.createRandom();
|
|
35
|
+
}
|
|
36
|
+
// Build XMTP signer adapter
|
|
37
|
+
const signer = {
|
|
38
|
+
getAddress: async () => wallet.address,
|
|
39
|
+
getIdentifier: () => ({
|
|
40
|
+
identifier: wallet.address,
|
|
41
|
+
identifierKind: 0
|
|
42
|
+
}),
|
|
43
|
+
signMessage: async (message) => {
|
|
44
|
+
const sig = await wallet.signMessage(message);
|
|
45
|
+
return getBytes(sig);
|
|
46
|
+
},
|
|
47
|
+
type: "EOA"
|
|
48
|
+
};
|
|
49
|
+
const client = await Client.create(signer, {
|
|
50
|
+
env: config.env || "production"
|
|
51
|
+
});
|
|
52
|
+
console.log(`[OpenAgentClient] Connected as ${wallet.address}`);
|
|
53
|
+
return new OpenAgentClient(client, wallet);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the client's wallet address.
|
|
57
|
+
*/
|
|
58
|
+
getAddress() {
|
|
59
|
+
return this.wallet.address;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Send a named task to an agent and wait for the response.
|
|
63
|
+
*
|
|
64
|
+
* @param agentAddress - The target agent's wallet address
|
|
65
|
+
* @param method - The task name (e.g. "buy_key", "say_hello")
|
|
66
|
+
* @param params - Task input parameters
|
|
67
|
+
* @param opts - Optional: timeout, payment txHash
|
|
68
|
+
* @returns TaskResult with the response or payment demand
|
|
69
|
+
*/
|
|
70
|
+
async sendTask(agentAddress, method, params = {}, opts) {
|
|
71
|
+
const timeout = opts?.timeout ?? 30000;
|
|
72
|
+
// Open DM with the agent
|
|
73
|
+
const conversation = await this.client.conversations.newDmWithIdentifier({
|
|
74
|
+
identifier: agentAddress,
|
|
75
|
+
identifierKind: 0
|
|
76
|
+
});
|
|
77
|
+
// Build JSON-RPC request
|
|
78
|
+
const request = {
|
|
79
|
+
method,
|
|
80
|
+
params,
|
|
81
|
+
id: Date.now()
|
|
82
|
+
};
|
|
83
|
+
// Attach payment proof if provided
|
|
84
|
+
if (opts?.txHash) {
|
|
85
|
+
request.payment = { txHash: opts.txHash };
|
|
86
|
+
}
|
|
87
|
+
// Send as string
|
|
88
|
+
await conversation.send(JSON.stringify(request));
|
|
89
|
+
// Wait for response
|
|
90
|
+
return await this.waitForResponse(conversation, timeout);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Send a plain text message to an agent and wait for the reply.
|
|
94
|
+
*
|
|
95
|
+
* @param agentAddress - The target agent's wallet address
|
|
96
|
+
* @param text - The message text
|
|
97
|
+
* @param timeout - Timeout in ms (default: 30000)
|
|
98
|
+
* @returns TaskResult with the reply
|
|
99
|
+
*/
|
|
100
|
+
async chat(agentAddress, text, timeout = 30000) {
|
|
101
|
+
const conversation = await this.client.conversations.newDmWithIdentifier({
|
|
102
|
+
identifier: agentAddress,
|
|
103
|
+
identifierKind: 0
|
|
104
|
+
});
|
|
105
|
+
await conversation.send(text);
|
|
106
|
+
return await this.waitForResponse(conversation, timeout);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Wait for a response message from the agent on a conversation.
|
|
110
|
+
*/
|
|
111
|
+
async waitForResponse(conversation, timeout) {
|
|
112
|
+
return new Promise(async (resolve) => {
|
|
113
|
+
const timer = setTimeout(() => {
|
|
114
|
+
resolve({
|
|
115
|
+
success: false,
|
|
116
|
+
error: `Timeout: No response after ${timeout}ms`
|
|
117
|
+
});
|
|
118
|
+
}, timeout);
|
|
119
|
+
try {
|
|
120
|
+
const stream = await conversation.stream();
|
|
121
|
+
for await (const message of stream) {
|
|
122
|
+
// Skip our own messages
|
|
123
|
+
if (message.senderInboxId === this.client.inboxId)
|
|
124
|
+
continue;
|
|
125
|
+
clearTimeout(timer);
|
|
126
|
+
const content = message.content;
|
|
127
|
+
if (typeof content !== 'string') {
|
|
128
|
+
resolve({ success: true, raw: String(content) });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// Try to parse as JSON
|
|
132
|
+
try {
|
|
133
|
+
const json = JSON.parse(content);
|
|
134
|
+
// 402 Payment Required
|
|
135
|
+
if (json.type === "PAYMENT_REQUIRED") {
|
|
136
|
+
resolve({
|
|
137
|
+
success: false,
|
|
138
|
+
paymentRequired: json.payment,
|
|
139
|
+
raw: content
|
|
140
|
+
});
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// JSON-RPC error
|
|
144
|
+
if (json.error) {
|
|
145
|
+
resolve({
|
|
146
|
+
success: false,
|
|
147
|
+
error: json.error.message || JSON.stringify(json.error),
|
|
148
|
+
raw: content
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// JSON-RPC success
|
|
153
|
+
if (json.result !== undefined) {
|
|
154
|
+
resolve({
|
|
155
|
+
success: true,
|
|
156
|
+
result: json.result,
|
|
157
|
+
raw: content
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// Generic JSON response
|
|
162
|
+
resolve({ success: true, result: json, raw: content });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// Plain text response
|
|
167
|
+
resolve({ success: true, result: content, raw: content });
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
clearTimeout(timer);
|
|
174
|
+
resolve({
|
|
175
|
+
success: false,
|
|
176
|
+
error: err.message || "Stream error"
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAAY,KAAK,WAAW,IAAI,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvG,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAAY,KAAK,WAAW,IAAI,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvG,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,GAAG,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,OAAO,CAAC;CACxC;AAED,qBAAa,SAAS;IAClB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAkB;IAEhC,OAAO;WAMa,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAoCvE,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;IACI,GAAG,CAAC,UAAU,EAAE,eAAe;IAItC;;;;OAIG;IACI,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;IAIrE;;OAEG;IACU,KAAK;IAKlB;;;;OAIG;IACU,QAAQ,CACjB,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACpE,OAAO,CAAC,cAAc,CAAC;CAS7B"}
|
package/dist/index.js
CHANGED
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,QAAQ;IAGL,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,GAAG,CAAM;gBAEG,MAAM,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB;IAUY,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,QAAQ;IAGL,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,GAAG,CAAM;gBAEG,MAAM,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB;IAUY,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;CA0EzE"}
|
package/dist/registry.js
CHANGED
|
@@ -14,26 +14,53 @@ export class Registry {
|
|
|
14
14
|
}
|
|
15
15
|
async register(agentInfo) {
|
|
16
16
|
console.log(`[OpenAgent] 📝 Creating agent: ${agentInfo.name}...`);
|
|
17
|
-
const agent = this.sdk.createAgent(agentInfo.name, agentInfo.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
const agent = this.sdk.createAgent(agentInfo.name, agentInfo.image);
|
|
18
|
+
// Fix: createAgent(name, image) puts image into description.
|
|
19
|
+
// Use updateInfo to set the correct description and image.
|
|
20
|
+
agent.updateInfo(agentInfo.name, agentInfo.description, agentInfo.image);
|
|
21
|
+
// Set the A2A endpoint
|
|
22
|
+
const a2aEndpoint = agentInfo.a2aEndpoint || "https://openagent.market/placeholder";
|
|
23
|
+
await agent.setA2A(a2aEndpoint);
|
|
24
|
+
// Set agent as active
|
|
23
25
|
agent.setActive(true);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
protocol: "openagentmarket"
|
|
26
|
+
// Set trust model
|
|
27
|
+
agent.setTrust(true);
|
|
28
|
+
// Build metadata for the endpoint
|
|
29
|
+
const endpointMeta = {
|
|
30
|
+
protocol: "openagentmarket",
|
|
29
31
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
// Merge user-provided metadata into endpoint meta
|
|
33
|
+
if (agentInfo.metadata) {
|
|
34
|
+
if (agentInfo.metadata.category)
|
|
35
|
+
endpointMeta.category = agentInfo.metadata.category;
|
|
36
|
+
if (agentInfo.metadata.tags)
|
|
37
|
+
endpointMeta.tags = agentInfo.metadata.tags;
|
|
38
|
+
if (agentInfo.metadata.version)
|
|
39
|
+
endpointMeta.version = agentInfo.metadata.version;
|
|
40
|
+
}
|
|
41
|
+
// Inject metadata into the A2A endpoint
|
|
42
|
+
const regFile = agent.getRegistrationFile();
|
|
43
|
+
const endpoint = regFile.endpoints.find((ep) => ep.type === 'A2A');
|
|
44
|
+
if (endpoint) {
|
|
45
|
+
endpoint.meta = { ...endpoint.meta, ...endpointMeta };
|
|
46
|
+
}
|
|
47
|
+
// Set top-level agent metadata (pricing, skills, xmtpAddress etc.)
|
|
48
|
+
if (agentInfo.metadata) {
|
|
49
|
+
const topLevelMeta = {};
|
|
50
|
+
for (const [key, value] of Object.entries(agentInfo.metadata)) {
|
|
51
|
+
// category, tags, version go on endpoint; rest goes on agent-level metadata
|
|
52
|
+
if (!['category', 'tags', 'version'].includes(key)) {
|
|
53
|
+
topLevelMeta[key] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (Object.keys(topLevelMeta).length > 0) {
|
|
57
|
+
agent.setMetadata(topLevelMeta);
|
|
58
|
+
}
|
|
32
59
|
}
|
|
60
|
+
console.log('[OpenAgent] Registration file:', JSON.stringify(agent.getRegistrationFile(), null, 2));
|
|
33
61
|
console.log('[OpenAgent] ⛓️ Registering on Base...');
|
|
34
62
|
const txHandle = await agent.registerIPFS();
|
|
35
63
|
const { result } = await txHandle.waitMined();
|
|
36
|
-
// txHandle usually contains the hash if it's a ContractTransactionResponse
|
|
37
64
|
const txHash = txHandle.hash || "unknown";
|
|
38
65
|
const agentId = result.agentId;
|
|
39
66
|
if (!agentId)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openagentmarket/nodejs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "OpenAgent Market Node.js SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@xmtp/agent-sdk": "^1.0.0",
|
|
27
|
+
"@xmtp/node-sdk": "^4.6.0",
|
|
27
28
|
"agent0-sdk": "^1.5.3",
|
|
28
29
|
"dotenv": "^16.4.0",
|
|
29
30
|
"ethers": "^6.10.0"
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { Client } from '@xmtp/node-sdk';
|
|
2
|
+
import { type Signer, Wallet, HDNodeWallet, getBytes } from 'ethers';
|
|
3
|
+
|
|
4
|
+
export interface OpenAgentClientConfig {
|
|
5
|
+
mnemonic?: string;
|
|
6
|
+
privateKey?: string;
|
|
7
|
+
env?: "production" | "dev" | "local";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface TaskResult {
|
|
11
|
+
/** True if the agent returned a result */
|
|
12
|
+
success: boolean;
|
|
13
|
+
/** The result data from the agent (JSON-RPC response) */
|
|
14
|
+
result?: any;
|
|
15
|
+
/** If the agent demanded payment */
|
|
16
|
+
paymentRequired?: {
|
|
17
|
+
amount: number;
|
|
18
|
+
currency: string;
|
|
19
|
+
recipient: string;
|
|
20
|
+
chainId: number;
|
|
21
|
+
};
|
|
22
|
+
/** Raw response text */
|
|
23
|
+
raw?: string;
|
|
24
|
+
/** Error message if something went wrong */
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SendTaskOptions {
|
|
29
|
+
/** Timeout in milliseconds (default: 30000) */
|
|
30
|
+
timeout?: number;
|
|
31
|
+
/** Payment proof transaction hash (if prepaying) */
|
|
32
|
+
txHash?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* OpenAgentClient — buyer-side SDK for hiring agents via XMTP.
|
|
37
|
+
*
|
|
38
|
+
* No registration or agent card needed. Just create a client and start
|
|
39
|
+
* sending tasks to agents by their wallet address.
|
|
40
|
+
*
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const client = await OpenAgentClient.create({ mnemonic: process.env.MNEMONIC, env: "production" });
|
|
43
|
+
* const result = await client.sendTask("0xAgentAddress", "buy_key", { name: "My Key" });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export class OpenAgentClient {
|
|
47
|
+
private client: Client;
|
|
48
|
+
private wallet: Wallet | HDNodeWallet;
|
|
49
|
+
|
|
50
|
+
private constructor(client: Client, wallet: Wallet | HDNodeWallet) {
|
|
51
|
+
this.client = client;
|
|
52
|
+
this.wallet = wallet;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a new OpenAgentClient.
|
|
57
|
+
* Handles wallet creation and XMTP connection.
|
|
58
|
+
*/
|
|
59
|
+
public static async create(config: OpenAgentClientConfig): Promise<OpenAgentClient> {
|
|
60
|
+
let wallet: Wallet | HDNodeWallet;
|
|
61
|
+
|
|
62
|
+
if (config.mnemonic) {
|
|
63
|
+
wallet = Wallet.fromPhrase(config.mnemonic);
|
|
64
|
+
} else if (config.privateKey) {
|
|
65
|
+
wallet = new Wallet(config.privateKey);
|
|
66
|
+
} else {
|
|
67
|
+
wallet = Wallet.createRandom();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Build XMTP signer adapter
|
|
71
|
+
const signer = {
|
|
72
|
+
getAddress: async () => wallet.address,
|
|
73
|
+
getIdentifier: () => ({
|
|
74
|
+
identifier: wallet.address,
|
|
75
|
+
identifierKind: 0
|
|
76
|
+
}),
|
|
77
|
+
signMessage: async (message: string | Uint8Array) => {
|
|
78
|
+
const sig = await wallet.signMessage(message);
|
|
79
|
+
return getBytes(sig);
|
|
80
|
+
},
|
|
81
|
+
type: "EOA"
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const client = await Client.create(signer as any, {
|
|
85
|
+
env: config.env || "production"
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
console.log(`[OpenAgentClient] Connected as ${wallet.address}`);
|
|
89
|
+
return new OpenAgentClient(client, wallet);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the client's wallet address.
|
|
94
|
+
*/
|
|
95
|
+
public getAddress(): string {
|
|
96
|
+
return this.wallet.address;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Send a named task to an agent and wait for the response.
|
|
101
|
+
*
|
|
102
|
+
* @param agentAddress - The target agent's wallet address
|
|
103
|
+
* @param method - The task name (e.g. "buy_key", "say_hello")
|
|
104
|
+
* @param params - Task input parameters
|
|
105
|
+
* @param opts - Optional: timeout, payment txHash
|
|
106
|
+
* @returns TaskResult with the response or payment demand
|
|
107
|
+
*/
|
|
108
|
+
public async sendTask(
|
|
109
|
+
agentAddress: string,
|
|
110
|
+
method: string,
|
|
111
|
+
params: Record<string, any> = {},
|
|
112
|
+
opts?: SendTaskOptions
|
|
113
|
+
): Promise<TaskResult> {
|
|
114
|
+
const timeout = opts?.timeout ?? 30000;
|
|
115
|
+
|
|
116
|
+
// Open DM with the agent
|
|
117
|
+
const conversation = await this.client.conversations.newDmWithIdentifier({
|
|
118
|
+
identifier: agentAddress,
|
|
119
|
+
identifierKind: 0
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Build JSON-RPC request
|
|
123
|
+
const request: Record<string, any> = {
|
|
124
|
+
method,
|
|
125
|
+
params,
|
|
126
|
+
id: Date.now()
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Attach payment proof if provided
|
|
130
|
+
if (opts?.txHash) {
|
|
131
|
+
request.payment = { txHash: opts.txHash };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Send as string
|
|
135
|
+
await conversation.send(JSON.stringify(request));
|
|
136
|
+
|
|
137
|
+
// Wait for response
|
|
138
|
+
return await this.waitForResponse(conversation, timeout);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Send a plain text message to an agent and wait for the reply.
|
|
143
|
+
*
|
|
144
|
+
* @param agentAddress - The target agent's wallet address
|
|
145
|
+
* @param text - The message text
|
|
146
|
+
* @param timeout - Timeout in ms (default: 30000)
|
|
147
|
+
* @returns TaskResult with the reply
|
|
148
|
+
*/
|
|
149
|
+
public async chat(
|
|
150
|
+
agentAddress: string,
|
|
151
|
+
text: string,
|
|
152
|
+
timeout: number = 30000
|
|
153
|
+
): Promise<TaskResult> {
|
|
154
|
+
const conversation = await this.client.conversations.newDmWithIdentifier({
|
|
155
|
+
identifier: agentAddress,
|
|
156
|
+
identifierKind: 0
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await conversation.send(text);
|
|
160
|
+
|
|
161
|
+
return await this.waitForResponse(conversation, timeout);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Wait for a response message from the agent on a conversation.
|
|
166
|
+
*/
|
|
167
|
+
private async waitForResponse(
|
|
168
|
+
conversation: any,
|
|
169
|
+
timeout: number
|
|
170
|
+
): Promise<TaskResult> {
|
|
171
|
+
return new Promise<TaskResult>(async (resolve) => {
|
|
172
|
+
const timer = setTimeout(() => {
|
|
173
|
+
resolve({
|
|
174
|
+
success: false,
|
|
175
|
+
error: `Timeout: No response after ${timeout}ms`
|
|
176
|
+
});
|
|
177
|
+
}, timeout);
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const stream = await conversation.stream();
|
|
181
|
+
|
|
182
|
+
for await (const message of stream) {
|
|
183
|
+
// Skip our own messages
|
|
184
|
+
if ((message as any).senderInboxId === this.client.inboxId) continue;
|
|
185
|
+
|
|
186
|
+
clearTimeout(timer);
|
|
187
|
+
|
|
188
|
+
const content = (message as any).content;
|
|
189
|
+
if (typeof content !== 'string') {
|
|
190
|
+
resolve({ success: true, raw: String(content) });
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Try to parse as JSON
|
|
195
|
+
try {
|
|
196
|
+
const json = JSON.parse(content);
|
|
197
|
+
|
|
198
|
+
// 402 Payment Required
|
|
199
|
+
if (json.type === "PAYMENT_REQUIRED") {
|
|
200
|
+
resolve({
|
|
201
|
+
success: false,
|
|
202
|
+
paymentRequired: json.payment,
|
|
203
|
+
raw: content
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// JSON-RPC error
|
|
209
|
+
if (json.error) {
|
|
210
|
+
resolve({
|
|
211
|
+
success: false,
|
|
212
|
+
error: json.error.message || JSON.stringify(json.error),
|
|
213
|
+
raw: content
|
|
214
|
+
});
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// JSON-RPC success
|
|
219
|
+
if (json.result !== undefined) {
|
|
220
|
+
resolve({
|
|
221
|
+
success: true,
|
|
222
|
+
result: json.result,
|
|
223
|
+
raw: content
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Generic JSON response
|
|
229
|
+
resolve({ success: true, result: json, raw: content });
|
|
230
|
+
return;
|
|
231
|
+
} catch {
|
|
232
|
+
// Plain text response
|
|
233
|
+
resolve({ success: true, result: content, raw: content });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch (err: any) {
|
|
238
|
+
clearTimeout(timer);
|
|
239
|
+
resolve({
|
|
240
|
+
success: false,
|
|
241
|
+
error: err.message || "Stream error"
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
package/src/index.ts
CHANGED
package/src/registry.ts
CHANGED
|
@@ -42,34 +42,61 @@ export class Registry {
|
|
|
42
42
|
|
|
43
43
|
const agent = this.sdk.createAgent(
|
|
44
44
|
agentInfo.name,
|
|
45
|
-
agentInfo.description,
|
|
46
45
|
agentInfo.image
|
|
47
46
|
);
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
// Fix: createAgent(name, image) puts image into description.
|
|
49
|
+
// Use updateInfo to set the correct description and image.
|
|
50
|
+
agent.updateInfo(agentInfo.name, agentInfo.description, agentInfo.image);
|
|
51
|
+
|
|
52
|
+
// Set the A2A endpoint
|
|
53
|
+
const a2aEndpoint = agentInfo.a2aEndpoint || "https://openagent.market/placeholder";
|
|
54
|
+
await agent.setA2A(a2aEndpoint);
|
|
53
55
|
|
|
54
|
-
agent
|
|
56
|
+
// Set agent as active
|
|
55
57
|
agent.setActive(true);
|
|
56
|
-
agent.setX402Support(true);
|
|
57
58
|
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
// Set trust model
|
|
60
|
+
agent.setTrust(true);
|
|
61
|
+
|
|
62
|
+
// Build metadata for the endpoint
|
|
63
|
+
const endpointMeta: Record<string, any> = {
|
|
64
|
+
protocol: "openagentmarket",
|
|
62
65
|
};
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
// Merge user-provided metadata into endpoint meta
|
|
68
|
+
if (agentInfo.metadata) {
|
|
69
|
+
if (agentInfo.metadata.category) endpointMeta.category = agentInfo.metadata.category;
|
|
70
|
+
if (agentInfo.metadata.tags) endpointMeta.tags = agentInfo.metadata.tags;
|
|
71
|
+
if (agentInfo.metadata.version) endpointMeta.version = agentInfo.metadata.version;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Inject metadata into the A2A endpoint
|
|
75
|
+
const regFile = agent.getRegistrationFile();
|
|
76
|
+
const endpoint = regFile.endpoints.find((ep: any) => ep.type === 'A2A');
|
|
77
|
+
if (endpoint) {
|
|
78
|
+
endpoint.meta = { ...endpoint.meta, ...endpointMeta };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Set top-level agent metadata (pricing, skills, xmtpAddress etc.)
|
|
82
|
+
if (agentInfo.metadata) {
|
|
83
|
+
const topLevelMeta: Record<string, any> = {};
|
|
84
|
+
for (const [key, value] of Object.entries(agentInfo.metadata)) {
|
|
85
|
+
// category, tags, version go on endpoint; rest goes on agent-level metadata
|
|
86
|
+
if (!['category', 'tags', 'version'].includes(key)) {
|
|
87
|
+
topLevelMeta[key] = value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (Object.keys(topLevelMeta).length > 0) {
|
|
91
|
+
agent.setMetadata(topLevelMeta);
|
|
92
|
+
}
|
|
66
93
|
}
|
|
67
94
|
|
|
95
|
+
console.log('[OpenAgent] Registration file:', JSON.stringify(agent.getRegistrationFile(), null, 2));
|
|
68
96
|
console.log('[OpenAgent] ⛓️ Registering on Base...');
|
|
69
97
|
const txHandle = await agent.registerIPFS();
|
|
70
98
|
const { result } = await txHandle.waitMined();
|
|
71
99
|
|
|
72
|
-
// txHandle usually contains the hash if it's a ContractTransactionResponse
|
|
73
100
|
const txHash = (txHandle as any).hash || "unknown";
|
|
74
101
|
|
|
75
102
|
const agentId = result.agentId;
|