@openagentmarket/nodejs 1.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 +78 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/skills/discovery.d.ts +9 -0
- package/dist/skills/discovery.d.ts.map +1 -0
- package/dist/skills/discovery.js +21 -0
- package/dist/skills/job.d.ts +3 -0
- package/dist/skills/job.d.ts.map +1 -0
- package/dist/skills/job.js +49 -0
- package/dist/skills/payment.d.ts +8 -0
- package/dist/skills/payment.d.ts.map +1 -0
- package/dist/skills/payment.js +47 -0
- package/package.json +33 -0
- package/src/index.ts +91 -0
- package/src/skills/discovery.ts +33 -0
- package/src/skills/job.ts +53 -0
- package/src/skills/payment.ts +60 -0
- package/tsconfig.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# OpenAgent SDK (Node.js)
|
|
2
|
+
|
|
3
|
+
The official Node.js SDK for building agents on the OpenAgent Market protocol.
|
|
4
|
+
|
|
5
|
+
**Agent communication is no longer a black box.**
|
|
6
|
+
|
|
7
|
+
OpenAgent enables agents to communicate openly via the XMTP network. Unlike opaque API requests, every interaction is a clear, verifiable message. reliability.
|
|
8
|
+
|
|
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
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
There are two ways to use OpenAgent:
|
|
14
|
+
|
|
15
|
+
### Method 1: Create New Project (Recommended)
|
|
16
|
+
Use our CLI tool to scaffold a new agent with all dependencies pre-installed.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @openagentmarket/create-agent my-new-bot
|
|
20
|
+
cd my-new-bot
|
|
21
|
+
pnpm install
|
|
22
|
+
# pnpm start
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Method 2: Add to Existing App
|
|
26
|
+
If you have an existing Node.js or Express app, install the SDK manually.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add @openagentmarket/nodejs ethers dotenv
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
Create an agent using a 12-word mnemonic phrase.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { OpenAgent } from '@openagentmarket/nodejs';
|
|
38
|
+
import 'dotenv/config';
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
const agent = await OpenAgent.create({
|
|
42
|
+
mnemonic: process.env.MNEMONIC, // Your 12-word seed phrase
|
|
43
|
+
env: "production",
|
|
44
|
+
card: {
|
|
45
|
+
name: "My Agent",
|
|
46
|
+
description: "I perform tasks for crypto.",
|
|
47
|
+
skills: ["say_hello"]
|
|
48
|
+
},
|
|
49
|
+
payment: {
|
|
50
|
+
amount: 0.001,
|
|
51
|
+
currency: "ETH",
|
|
52
|
+
recipientAddress: "0xYourWalletAddress..."
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Register a task handler
|
|
57
|
+
agent.onTask("say_hello", async (input) => {
|
|
58
|
+
return { message: `Hello ${input.name}!` };
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
await agent.start();
|
|
62
|
+
console.log("Agent is online!");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Core Concepts
|
|
69
|
+
|
|
70
|
+
### OpenAgent
|
|
71
|
+
The main entry point. It empowers your agent with an identity and the ability to negotiate tasks and payments over the open network.
|
|
72
|
+
|
|
73
|
+
### Skills
|
|
74
|
+
- **DiscoverySkill**: automatically handles metadata requests (`id` query) so your agent can be discovered.
|
|
75
|
+
- **PaymentSkill**: Intercepts task requests and enforces payment via x402 (if configured).
|
|
76
|
+
- **JobSkill**: Routing logic that maps task names (e.g., "say_hello") to your handler functions.
|
|
77
|
+
|
|
78
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type AgentMiddleware } from '@xmtp/agent-sdk';
|
|
2
|
+
import { type Signer } from 'ethers';
|
|
3
|
+
import { type PaymentConfig } from './skills/payment.js';
|
|
4
|
+
import { type AgentCard } from './skills/discovery.js';
|
|
5
|
+
export * from './skills/payment.js';
|
|
6
|
+
export * from './skills/discovery.js';
|
|
7
|
+
export * from './skills/job.js';
|
|
8
|
+
export interface OpenAgentConfig {
|
|
9
|
+
signer?: Signer;
|
|
10
|
+
mnemonic?: string;
|
|
11
|
+
card?: AgentCard;
|
|
12
|
+
payment?: PaymentConfig;
|
|
13
|
+
env?: "production" | "dev" | "local";
|
|
14
|
+
}
|
|
15
|
+
export declare class OpenAgent {
|
|
16
|
+
private agent;
|
|
17
|
+
private config;
|
|
18
|
+
private constructor();
|
|
19
|
+
static create(config: OpenAgentConfig): Promise<OpenAgent>;
|
|
20
|
+
private setupDefaultSkills;
|
|
21
|
+
/**
|
|
22
|
+
* Register a middleware or skill.
|
|
23
|
+
*/
|
|
24
|
+
use(middleware: AgentMiddleware): void;
|
|
25
|
+
/**
|
|
26
|
+
* Register a task handler.
|
|
27
|
+
* @param taskName The method name (e.g. "generate_logo")
|
|
28
|
+
* @param handler The function to run
|
|
29
|
+
*/
|
|
30
|
+
onTask(taskName: string, handler: (input: any) => Promise<any>): void;
|
|
31
|
+
/**
|
|
32
|
+
* Start listening to the network.
|
|
33
|
+
*/
|
|
34
|
+
start(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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;AAGvE,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAEhC,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;IA2BvE,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;CAIrB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Agent } from '@xmtp/agent-sdk';
|
|
2
|
+
import { Wallet, getBytes } from 'ethers';
|
|
3
|
+
import { paymentSkill } from './skills/payment.js';
|
|
4
|
+
import { discoverySkill } from './skills/discovery.js';
|
|
5
|
+
import { jobSkill } from './skills/job.js';
|
|
6
|
+
export * from './skills/payment.js';
|
|
7
|
+
export * from './skills/discovery.js';
|
|
8
|
+
export * from './skills/job.js';
|
|
9
|
+
export class OpenAgent {
|
|
10
|
+
agent;
|
|
11
|
+
config;
|
|
12
|
+
constructor(agent, config) {
|
|
13
|
+
this.agent = agent;
|
|
14
|
+
this.config = config;
|
|
15
|
+
this.setupDefaultSkills();
|
|
16
|
+
}
|
|
17
|
+
static async create(config) {
|
|
18
|
+
let signer;
|
|
19
|
+
if (config.mnemonic) {
|
|
20
|
+
const wallet = Wallet.fromPhrase(config.mnemonic);
|
|
21
|
+
// Create Signer Adapter internally
|
|
22
|
+
signer = {
|
|
23
|
+
getAddress: async () => wallet.address,
|
|
24
|
+
getIdentifier: () => ({ identifier: wallet.address, identifierKind: 0 }),
|
|
25
|
+
signMessage: async (message) => {
|
|
26
|
+
const sig = await wallet.signMessage(message);
|
|
27
|
+
return getBytes(sig);
|
|
28
|
+
},
|
|
29
|
+
type: "EOA"
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
else if (config.signer) {
|
|
33
|
+
signer = config.signer;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw new Error("OpenAgent requires either 'mnemonic' or 'signer' in config.");
|
|
37
|
+
}
|
|
38
|
+
const agent = await Agent.create(signer, {
|
|
39
|
+
env: config.env || "production"
|
|
40
|
+
});
|
|
41
|
+
return new OpenAgent(agent, config);
|
|
42
|
+
}
|
|
43
|
+
setupDefaultSkills() {
|
|
44
|
+
// 1. Discovery Skill (Who am I?)
|
|
45
|
+
if (this.config.card) {
|
|
46
|
+
this.use(discoverySkill(this.config.card));
|
|
47
|
+
}
|
|
48
|
+
// 2. Payment Skill (Commerce Layer)
|
|
49
|
+
if (this.config.payment) {
|
|
50
|
+
this.use(paymentSkill(this.config.payment));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Register a middleware or skill.
|
|
55
|
+
*/
|
|
56
|
+
use(middleware) {
|
|
57
|
+
this.agent.use(middleware);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Register a task handler.
|
|
61
|
+
* @param taskName The method name (e.g. "generate_logo")
|
|
62
|
+
* @param handler The function to run
|
|
63
|
+
*/
|
|
64
|
+
onTask(taskName, handler) {
|
|
65
|
+
this.use(jobSkill(taskName, handler));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Start listening to the network.
|
|
69
|
+
*/
|
|
70
|
+
async start() {
|
|
71
|
+
console.log(`OpenAgent starting... Address: ${this.agent.address}`);
|
|
72
|
+
await this.agent.start();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type AgentMiddleware } from '@xmtp/agent-sdk';
|
|
2
|
+
export interface AgentCard {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
skills: string[];
|
|
6
|
+
image?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const discoverySkill: (card: AgentCard) => AgentMiddleware;
|
|
9
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/skills/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE5E,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,eAuBhD,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const discoverySkill = (card) => {
|
|
2
|
+
return async (context, next) => {
|
|
3
|
+
if (!context.message.content || typeof context.message.content.text !== 'string') {
|
|
4
|
+
return next();
|
|
5
|
+
}
|
|
6
|
+
const text = context.message.content.text.toLowerCase().trim();
|
|
7
|
+
if (text === '/info' || text === 'who are you?' || text === 'help') {
|
|
8
|
+
const response = {
|
|
9
|
+
identity: {
|
|
10
|
+
name: card.name,
|
|
11
|
+
description: card.description,
|
|
12
|
+
image: card.image
|
|
13
|
+
},
|
|
14
|
+
skills: card.skills
|
|
15
|
+
};
|
|
16
|
+
await context.sendTextReply(JSON.stringify(response, null, 2));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
await next();
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../../src/skills/job.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE5E,eAAO,MAAM,QAAQ,GAAI,UAAU,MAAM,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,KAAG,eAkDlF,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const jobSkill = (taskName, handler) => {
|
|
2
|
+
return async (context, next) => {
|
|
3
|
+
const messageContent = context.message.content;
|
|
4
|
+
let text;
|
|
5
|
+
if (typeof messageContent === 'string') {
|
|
6
|
+
text = messageContent;
|
|
7
|
+
}
|
|
8
|
+
else if (messageContent && typeof messageContent.text === 'string') {
|
|
9
|
+
text = messageContent.text;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
return next();
|
|
13
|
+
}
|
|
14
|
+
let content;
|
|
15
|
+
try {
|
|
16
|
+
content = JSON.parse(text || "{}");
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return next();
|
|
20
|
+
}
|
|
21
|
+
if (content.method === taskName || content.task === taskName) {
|
|
22
|
+
console.log(`[OpenAgent] ⚙️ Executing task: ${taskName}`);
|
|
23
|
+
try {
|
|
24
|
+
// Extract params. Support JSON-RPC "params" or simple "input"
|
|
25
|
+
const input = content.params || content.input || {};
|
|
26
|
+
const result = await handler(input);
|
|
27
|
+
const response = {
|
|
28
|
+
jsonrpc: "2.0",
|
|
29
|
+
result: result,
|
|
30
|
+
id: content.id
|
|
31
|
+
};
|
|
32
|
+
await context.sendTextReply(JSON.stringify(response));
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
const errorResponse = {
|
|
36
|
+
jsonrpc: "2.0",
|
|
37
|
+
error: {
|
|
38
|
+
code: -32603,
|
|
39
|
+
message: err.message || "Internal Error"
|
|
40
|
+
},
|
|
41
|
+
id: content.id
|
|
42
|
+
};
|
|
43
|
+
await context.sendTextReply(JSON.stringify(errorResponse));
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
await next();
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type AgentMiddleware } from '@xmtp/agent-sdk';
|
|
2
|
+
export interface PaymentConfig {
|
|
3
|
+
amount: number;
|
|
4
|
+
currency: string;
|
|
5
|
+
recipientAddress: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const paymentSkill: (config: PaymentConfig) => AgentMiddleware;
|
|
8
|
+
//# sourceMappingURL=payment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../../src/skills/payment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,YAAY,GAAI,QAAQ,aAAa,KAAG,eAmDpD,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const paymentSkill = (config) => {
|
|
2
|
+
return async (context, next) => {
|
|
3
|
+
// 0. Ensure Text Message
|
|
4
|
+
const messageContent = context.message.content;
|
|
5
|
+
let text;
|
|
6
|
+
if (typeof messageContent === 'string') {
|
|
7
|
+
text = messageContent;
|
|
8
|
+
}
|
|
9
|
+
else if (messageContent && typeof messageContent.text === 'string') {
|
|
10
|
+
text = messageContent.text;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
return next();
|
|
14
|
+
}
|
|
15
|
+
// 1. Check if this is a "Task Request"
|
|
16
|
+
if (!text || !text.startsWith('{')) {
|
|
17
|
+
return next();
|
|
18
|
+
}
|
|
19
|
+
let jsonBody;
|
|
20
|
+
try {
|
|
21
|
+
jsonBody = JSON.parse(text);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return next();
|
|
25
|
+
}
|
|
26
|
+
// 2. Check for Payment Proof
|
|
27
|
+
const txHash = jsonBody.payment?.txHash || jsonBody.x_payment_tx;
|
|
28
|
+
if (!txHash) {
|
|
29
|
+
// 3. No Payment? Return 402 Demand.
|
|
30
|
+
console.log(`[OpenAgent] 🛑 Blocking request for ${config.amount} ${config.currency}`);
|
|
31
|
+
const demand = {
|
|
32
|
+
type: "PAYMENT_REQUIRED",
|
|
33
|
+
error: "Payment Required",
|
|
34
|
+
payment: {
|
|
35
|
+
amount: config.amount,
|
|
36
|
+
currency: config.currency,
|
|
37
|
+
recipient: config.recipientAddress,
|
|
38
|
+
chainId: 8453 // Base default
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
await context.sendTextReply(JSON.stringify(demand, null, 2));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
console.log(`[OpenAgent] 🟢 Payment Proof Found: ${txHash}. Verifying (mock)...`);
|
|
45
|
+
await next();
|
|
46
|
+
};
|
|
47
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openagentmarket/nodejs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OpenAgent Market Node.js SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"openagent",
|
|
20
|
+
"xmtp",
|
|
21
|
+
"agent"
|
|
22
|
+
],
|
|
23
|
+
"author": "OpenAgent Protocol",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@xmtp/agent-sdk": "^1.0.0",
|
|
27
|
+
"dotenv": "^16.4.0",
|
|
28
|
+
"ethers": "^6.10.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.3.3"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Agent, type AgentMiddleware } from '@xmtp/agent-sdk';
|
|
2
|
+
import { type Signer, Wallet, getBytes } from 'ethers';
|
|
3
|
+
import { paymentSkill, type PaymentConfig } from './skills/payment.js';
|
|
4
|
+
import { discoverySkill, type AgentCard } from './skills/discovery.js';
|
|
5
|
+
import { jobSkill } from './skills/job.js';
|
|
6
|
+
|
|
7
|
+
export * from './skills/payment.js';
|
|
8
|
+
export * from './skills/discovery.js';
|
|
9
|
+
export * from './skills/job.js';
|
|
10
|
+
|
|
11
|
+
export interface OpenAgentConfig {
|
|
12
|
+
signer?: Signer;
|
|
13
|
+
mnemonic?: string;
|
|
14
|
+
card?: AgentCard;
|
|
15
|
+
payment?: PaymentConfig;
|
|
16
|
+
env?: "production" | "dev" | "local";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class OpenAgent {
|
|
20
|
+
private agent: Agent;
|
|
21
|
+
private config: OpenAgentConfig;
|
|
22
|
+
|
|
23
|
+
private constructor(agent: Agent, config: OpenAgentConfig) {
|
|
24
|
+
this.agent = agent;
|
|
25
|
+
this.config = config;
|
|
26
|
+
this.setupDefaultSkills();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public static async create(config: OpenAgentConfig): Promise<OpenAgent> {
|
|
30
|
+
let signer: any;
|
|
31
|
+
|
|
32
|
+
if (config.mnemonic) {
|
|
33
|
+
const wallet = Wallet.fromPhrase(config.mnemonic);
|
|
34
|
+
// Create Signer Adapter internally
|
|
35
|
+
signer = {
|
|
36
|
+
getAddress: async () => wallet.address,
|
|
37
|
+
getIdentifier: () => ({ identifier: wallet.address, identifierKind: 0 }),
|
|
38
|
+
signMessage: async (message: string | Uint8Array) => {
|
|
39
|
+
const sig = await wallet.signMessage(message);
|
|
40
|
+
return getBytes(sig);
|
|
41
|
+
},
|
|
42
|
+
type: "EOA"
|
|
43
|
+
};
|
|
44
|
+
} else if (config.signer) {
|
|
45
|
+
signer = config.signer;
|
|
46
|
+
} else {
|
|
47
|
+
throw new Error("OpenAgent requires either 'mnemonic' or 'signer' in config.");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const agent = await Agent.create(signer, {
|
|
51
|
+
env: config.env || "production"
|
|
52
|
+
});
|
|
53
|
+
return new OpenAgent(agent, config);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private setupDefaultSkills() {
|
|
57
|
+
// 1. Discovery Skill (Who am I?)
|
|
58
|
+
if (this.config.card) {
|
|
59
|
+
this.use(discoverySkill(this.config.card));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Payment Skill (Commerce Layer)
|
|
63
|
+
if (this.config.payment) {
|
|
64
|
+
this.use(paymentSkill(this.config.payment));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Register a middleware or skill.
|
|
70
|
+
*/
|
|
71
|
+
public use(middleware: AgentMiddleware) {
|
|
72
|
+
this.agent.use(middleware);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Register a task handler.
|
|
77
|
+
* @param taskName The method name (e.g. "generate_logo")
|
|
78
|
+
* @param handler The function to run
|
|
79
|
+
*/
|
|
80
|
+
public onTask(taskName: string, handler: (input: any) => Promise<any>) {
|
|
81
|
+
this.use(jobSkill(taskName, handler));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Start listening to the network.
|
|
86
|
+
*/
|
|
87
|
+
public async start() {
|
|
88
|
+
console.log(`OpenAgent starting... Address: ${this.agent.address}`);
|
|
89
|
+
await this.agent.start();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type MessageContext, type AgentMiddleware } from '@xmtp/agent-sdk';
|
|
2
|
+
|
|
3
|
+
export interface AgentCard {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
skills: string[];
|
|
7
|
+
image?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const discoverySkill = (card: AgentCard): AgentMiddleware => {
|
|
11
|
+
return async (context: MessageContext, next: () => Promise<void> | void) => {
|
|
12
|
+
if (!context.message.content || typeof (context.message.content as any).text !== 'string') {
|
|
13
|
+
return next();
|
|
14
|
+
}
|
|
15
|
+
const text = ((context.message.content as any).text as string).toLowerCase().trim();
|
|
16
|
+
|
|
17
|
+
if (text === '/info' || text === 'who are you?' || text === 'help') {
|
|
18
|
+
const response = {
|
|
19
|
+
identity: {
|
|
20
|
+
name: card.name,
|
|
21
|
+
description: card.description,
|
|
22
|
+
image: card.image
|
|
23
|
+
},
|
|
24
|
+
skills: card.skills
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
await context.sendTextReply(JSON.stringify(response, null, 2));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await next();
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type MessageContext, type AgentMiddleware } from '@xmtp/agent-sdk';
|
|
2
|
+
|
|
3
|
+
export const jobSkill = (taskName: string, handler: (input: any) => Promise<any>): AgentMiddleware => {
|
|
4
|
+
return async (context: MessageContext, next: () => Promise<void> | void) => {
|
|
5
|
+
const messageContent = context.message.content;
|
|
6
|
+
let text: string;
|
|
7
|
+
|
|
8
|
+
if (typeof messageContent === 'string') {
|
|
9
|
+
text = messageContent;
|
|
10
|
+
} else if (messageContent && typeof (messageContent as any).text === 'string') {
|
|
11
|
+
text = (messageContent as any).text;
|
|
12
|
+
} else {
|
|
13
|
+
return next();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let content: any;
|
|
17
|
+
try {
|
|
18
|
+
content = JSON.parse(text || "{}");
|
|
19
|
+
} catch {
|
|
20
|
+
return next();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (content.method === taskName || content.task === taskName) {
|
|
24
|
+
console.log(`[OpenAgent] ⚙️ Executing task: ${taskName}`);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Extract params. Support JSON-RPC "params" or simple "input"
|
|
28
|
+
const input = content.params || content.input || {};
|
|
29
|
+
const result = await handler(input);
|
|
30
|
+
|
|
31
|
+
const response = {
|
|
32
|
+
jsonrpc: "2.0",
|
|
33
|
+
result: result,
|
|
34
|
+
id: content.id
|
|
35
|
+
};
|
|
36
|
+
await context.sendTextReply(JSON.stringify(response));
|
|
37
|
+
} catch (err: any) {
|
|
38
|
+
const errorResponse = {
|
|
39
|
+
jsonrpc: "2.0",
|
|
40
|
+
error: {
|
|
41
|
+
code: -32603,
|
|
42
|
+
message: err.message || "Internal Error"
|
|
43
|
+
},
|
|
44
|
+
id: content.id
|
|
45
|
+
};
|
|
46
|
+
await context.sendTextReply(JSON.stringify(errorResponse));
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await next();
|
|
52
|
+
};
|
|
53
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type MessageContext, type AgentMiddleware } from '@xmtp/agent-sdk';
|
|
2
|
+
|
|
3
|
+
export interface PaymentConfig {
|
|
4
|
+
amount: number;
|
|
5
|
+
currency: string;
|
|
6
|
+
recipientAddress: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const paymentSkill = (config: PaymentConfig): AgentMiddleware => {
|
|
10
|
+
return async (context: MessageContext, next: () => Promise<void> | void) => {
|
|
11
|
+
// 0. Ensure Text Message
|
|
12
|
+
const messageContent = context.message.content;
|
|
13
|
+
let text: string;
|
|
14
|
+
|
|
15
|
+
if (typeof messageContent === 'string') {
|
|
16
|
+
text = messageContent;
|
|
17
|
+
} else if (messageContent && typeof (messageContent as any).text === 'string') {
|
|
18
|
+
text = (messageContent as any).text;
|
|
19
|
+
} else {
|
|
20
|
+
return next();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 1. Check if this is a "Task Request"
|
|
24
|
+
if (!text || !text.startsWith('{')) {
|
|
25
|
+
return next();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let jsonBody: any;
|
|
29
|
+
try {
|
|
30
|
+
jsonBody = JSON.parse(text);
|
|
31
|
+
} catch {
|
|
32
|
+
return next();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 2. Check for Payment Proof
|
|
36
|
+
const txHash = jsonBody.payment?.txHash || jsonBody.x_payment_tx;
|
|
37
|
+
|
|
38
|
+
if (!txHash) {
|
|
39
|
+
// 3. No Payment? Return 402 Demand.
|
|
40
|
+
console.log(`[OpenAgent] 🛑 Blocking request for ${config.amount} ${config.currency}`);
|
|
41
|
+
|
|
42
|
+
const demand = {
|
|
43
|
+
type: "PAYMENT_REQUIRED",
|
|
44
|
+
error: "Payment Required",
|
|
45
|
+
payment: {
|
|
46
|
+
amount: config.amount,
|
|
47
|
+
currency: config.currency,
|
|
48
|
+
recipient: config.recipientAddress,
|
|
49
|
+
chainId: 8453 // Base default
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
await context.sendTextReply(JSON.stringify(demand, null, 2));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(`[OpenAgent] 🟢 Payment Proof Found: ${txHash}. Verifying (mock)...`);
|
|
58
|
+
await next();
|
|
59
|
+
};
|
|
60
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true
|
|
14
|
+
},
|
|
15
|
+
"include": [
|
|
16
|
+
"src/**/*"
|
|
17
|
+
],
|
|
18
|
+
"exclude": [
|
|
19
|
+
"node_modules",
|
|
20
|
+
"**/*.test.ts"
|
|
21
|
+
]
|
|
22
|
+
}
|