@nevermined-io/openclaw-plugin 1.0.12 → 1.0.14

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.
@@ -0,0 +1,142 @@
1
+ import { buildPaymentRequired } from '@nevermined-io/payments';
2
+ /**
3
+ * Registers a paid HTTP endpoint on the OpenClaw gateway.
4
+ * The endpoint handles x402 payment verification, processes requests,
5
+ * and settles credits after successful processing.
6
+ */
7
+ export function registerPaidEndpoint(api, getPayments, config, agentHandler) {
8
+ const path = config.agentEndpointPath ?? '/nevermined/agent';
9
+ const handler = agentHandler ?? mockWeatherHandler;
10
+ const routeHandler = async (req, res) => {
11
+ // 1. Extract payment signature
12
+ const accessToken = getHeader(req.headers, 'payment-signature') ??
13
+ getHeader(req.headers, 'PAYMENT-SIGNATURE');
14
+ if (!accessToken) {
15
+ res.writeHead(402, { 'Content-Type': 'application/json' });
16
+ res.end(JSON.stringify({ error: 'Payment required — missing payment-signature header' }));
17
+ return;
18
+ }
19
+ // 2. Build payment required descriptor
20
+ const planId = config.planId;
21
+ if (!planId) {
22
+ res.writeHead(500, { 'Content-Type': 'application/json' });
23
+ res.end(JSON.stringify({ error: 'Server misconfigured — planId not set' }));
24
+ return;
25
+ }
26
+ const paymentRequired = buildPaymentRequired(planId, {
27
+ endpoint: path,
28
+ agentId: config.agentId,
29
+ httpVerb: 'POST',
30
+ });
31
+ const maxAmount = BigInt(config.creditsPerRequest ?? 1);
32
+ // 3. Verify permissions (check credits without burning)
33
+ try {
34
+ const verification = await getPayments().facilitator.verifyPermissions({
35
+ paymentRequired,
36
+ x402AccessToken: accessToken,
37
+ maxAmount,
38
+ });
39
+ if (!verification.isValid) {
40
+ res.writeHead(402, { 'Content-Type': 'application/json' });
41
+ res.end(JSON.stringify({
42
+ error: 'Insufficient credits — order the plan first',
43
+ details: verification,
44
+ }));
45
+ return;
46
+ }
47
+ }
48
+ catch (err) {
49
+ res.writeHead(402, { 'Content-Type': 'application/json' });
50
+ res.end(JSON.stringify({
51
+ error: 'Payment verification failed',
52
+ message: err instanceof Error ? err.message : String(err),
53
+ }));
54
+ return;
55
+ }
56
+ // 4. Parse request body and process
57
+ const body = await parseBody(req);
58
+ const prompt = typeof body === 'object' && body !== null && 'prompt' in body
59
+ ? body.prompt
60
+ : String(body);
61
+ let result;
62
+ try {
63
+ result = await handler({ prompt });
64
+ }
65
+ catch (err) {
66
+ res.writeHead(500, { 'Content-Type': 'application/json' });
67
+ res.end(JSON.stringify({
68
+ error: 'Agent processing failed',
69
+ message: err instanceof Error ? err.message : String(err),
70
+ }));
71
+ return;
72
+ }
73
+ // 5. Settle permissions (burn credits)
74
+ let settlement;
75
+ try {
76
+ settlement = await getPayments().facilitator.settlePermissions({
77
+ paymentRequired,
78
+ x402AccessToken: accessToken,
79
+ maxAmount,
80
+ });
81
+ }
82
+ catch (err) {
83
+ // Log settlement failure but still return the result — the agent already processed
84
+ api.logger.warn(`Credit settlement failed: ${err instanceof Error ? err.message : String(err)}`);
85
+ settlement = { error: 'settlement_failed' };
86
+ }
87
+ // 6. Return response with payment-response header
88
+ const paymentResponse = Buffer.from(JSON.stringify(settlement)).toString('base64');
89
+ res.writeHead(200, {
90
+ 'Content-Type': 'application/json',
91
+ 'payment-response': paymentResponse,
92
+ });
93
+ res.end(JSON.stringify(result));
94
+ };
95
+ api.registerHttpRoute({ path, handler: routeHandler });
96
+ api.logger.info(`Registered paid endpoint at ${path}`);
97
+ }
98
+ /**
99
+ * Mock weather forecast handler for demonstration purposes.
100
+ * Returns simulated weather data based on the prompt.
101
+ */
102
+ export async function mockWeatherHandler(body) {
103
+ const city = extractCity(body.prompt) ?? 'Unknown';
104
+ return {
105
+ city,
106
+ forecast: 'Partly cloudy with a chance of innovation',
107
+ temperature: Math.floor(Math.random() * 30) + 5,
108
+ unit: 'celsius',
109
+ humidity: Math.floor(Math.random() * 60) + 30,
110
+ source: 'Weather Oracle (Nevermined demo)',
111
+ };
112
+ }
113
+ function extractCity(prompt) {
114
+ // Simple heuristic: look for "in <City>" or "for <City>" patterns
115
+ // Use word boundary to avoid matching partial words
116
+ // Use a lookbehind for word boundary + space to avoid matching "at" inside "What"
117
+ const match = prompt.match(/(?:^|\s)(?:in|for|at)\s+([A-Z][a-zA-Z\s]{1,30}?)(?:\?|$|\.|\s*,)/);
118
+ return match ? match[1].trim() : undefined;
119
+ }
120
+ function getHeader(headers, name) {
121
+ const value = headers[name] ?? headers[name.toLowerCase()];
122
+ if (Array.isArray(value))
123
+ return value[0];
124
+ return value;
125
+ }
126
+ function parseBody(req) {
127
+ return new Promise((resolve, reject) => {
128
+ const chunks = [];
129
+ req.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
130
+ req.on('end', () => {
131
+ const raw = Buffer.concat(chunks).toString('utf-8');
132
+ try {
133
+ resolve(raw ? JSON.parse(raw) : {});
134
+ }
135
+ catch {
136
+ resolve(raw);
137
+ }
138
+ });
139
+ req.on('error', reject);
140
+ });
141
+ }
142
+ //# sourceMappingURL=paid-endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paid-endpoint.js","sourceRoot":"","sources":["../src/paid-endpoint.ts"],"names":[],"mappings":"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"}
package/dist/tools.d.ts CHANGED
@@ -1,16 +1,23 @@
1
1
  import type { Payments } from '@nevermined-io/payments';
2
2
  import type { NeverminedPluginConfig } from './config.js';
3
- export interface ToolParam {
3
+ /**
4
+ * Creates all Nevermined payment tools for the OpenClaw plugin.
5
+ * Each tool is an object with { name, description, parameters, execute }
6
+ * compatible with the OpenClaw AnyAgentTool interface.
7
+ */
8
+ export declare function createTools(getPayments: () => Payments, config: NeverminedPluginConfig): ToolObject[];
9
+ interface ToolObject {
4
10
  name: string;
5
- type: string;
11
+ label: string;
6
12
  description: string;
7
- required: boolean;
13
+ parameters: Record<string, unknown>;
14
+ execute: (_id: string, params: Record<string, unknown>) => Promise<ToolResult>;
8
15
  }
9
- export interface ToolDefinition {
10
- name: string;
11
- description: string;
12
- params: ToolParam[];
13
- handler: (payments: Payments, config: NeverminedPluginConfig, params: Record<string, unknown>) => Promise<unknown>;
16
+ interface ToolResult {
17
+ content: Array<{
18
+ type: string;
19
+ text: string;
20
+ }>;
14
21
  }
15
- export declare const allTools: ToolDefinition[];
22
+ export {};
16
23
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAEzD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,SAAS,EAAE,CAAA;IACnB,OAAO,EAAE,CACP,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,sBAAsB,EAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,OAAO,CAAC,CAAA;CACtB;AAqPD,eAAO,MAAM,QAAQ,EAAE,cAAc,EAUpC,CAAA"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAIzD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,QAAQ,EAC3B,MAAM,EAAE,sBAAsB,GAC7B,UAAU,EAAE,CAqQd;AAID,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CAC/E;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/C"}
package/dist/tools.js CHANGED
@@ -1,218 +1,256 @@
1
- function requireParam(params, name) {
2
- const value = params[name];
3
- if (value === undefined || value === null || value === '') {
4
- throw new Error(`Missing required parameter: ${name}`);
5
- }
6
- return String(value);
1
+ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
2
+ /**
3
+ * Creates all Nevermined payment tools for the OpenClaw plugin.
4
+ * Each tool is an object with { name, description, parameters, execute }
5
+ * compatible with the OpenClaw AnyAgentTool interface.
6
+ */
7
+ export function createTools(getPayments, config) {
8
+ return [
9
+ // --- Subscriber tools ---
10
+ {
11
+ name: 'nevermined_checkBalance',
12
+ label: 'Nevermined Check Balance',
13
+ description: 'Check the credit balance for a Nevermined payment plan',
14
+ parameters: {
15
+ type: 'object',
16
+ properties: {
17
+ planId: { type: 'string', description: 'The payment plan ID (uses config default if omitted)' },
18
+ },
19
+ },
20
+ async execute(_id, params) {
21
+ const planId = str(params, 'planId') ?? config.planId;
22
+ if (!planId)
23
+ throw new Error('planId is required — provide it as a parameter or in the plugin config');
24
+ const balance = await getPayments().plans.getPlanBalance(planId);
25
+ return result({
26
+ planId: balance.planId,
27
+ planName: balance.planName,
28
+ balance: balance.balance.toString(),
29
+ isSubscriber: balance.isSubscriber,
30
+ });
31
+ },
32
+ },
33
+ {
34
+ name: 'nevermined_getAccessToken',
35
+ label: 'Nevermined Get Access Token',
36
+ description: 'Get an x402 access token for authenticating requests to a Nevermined agent',
37
+ parameters: {
38
+ type: 'object',
39
+ properties: {
40
+ planId: { type: 'string', description: 'The payment plan ID' },
41
+ agentId: { type: 'string', description: 'The agent ID' },
42
+ },
43
+ },
44
+ async execute(_id, params) {
45
+ const planId = str(params, 'planId') ?? config.planId;
46
+ if (!planId)
47
+ throw new Error('planId is required — provide it as a parameter or in the plugin config');
48
+ const agentId = str(params, 'agentId') ?? config.agentId;
49
+ const token = await getPayments().x402.getX402AccessToken(planId, agentId);
50
+ return result({ accessToken: token.accessToken });
51
+ },
52
+ },
53
+ {
54
+ name: 'nevermined_orderPlan',
55
+ label: 'Nevermined Order Plan',
56
+ description: 'Order (purchase) a Nevermined payment plan',
57
+ parameters: {
58
+ type: 'object',
59
+ properties: {
60
+ planId: { type: 'string', description: 'The payment plan ID to order' },
61
+ },
62
+ },
63
+ async execute(_id, params) {
64
+ const planId = str(params, 'planId') ?? config.planId;
65
+ if (!planId)
66
+ throw new Error('planId is required — provide it as a parameter or in the plugin config');
67
+ const res = await getPayments().plans.orderPlan(planId);
68
+ return result(res);
69
+ },
70
+ },
71
+ {
72
+ name: 'nevermined_queryAgent',
73
+ label: 'Nevermined Query Agent',
74
+ description: 'Query a Nevermined AI agent end-to-end: acquires an x402 access token, sends the prompt to the agent, and returns the response',
75
+ parameters: {
76
+ type: 'object',
77
+ properties: {
78
+ agentUrl: { type: 'string', description: 'The URL of the agent to query' },
79
+ prompt: { type: 'string', description: 'The prompt to send to the agent' },
80
+ planId: { type: 'string', description: 'The payment plan ID' },
81
+ agentId: { type: 'string', description: 'The agent ID' },
82
+ method: { type: 'string', description: 'HTTP method (default: POST)' },
83
+ },
84
+ required: ['agentUrl', 'prompt'],
85
+ },
86
+ async execute(_id, params) {
87
+ const agentUrl = requireStr(params, 'agentUrl');
88
+ const prompt = requireStr(params, 'prompt');
89
+ const planId = str(params, 'planId') ?? config.planId;
90
+ if (!planId)
91
+ throw new Error('planId is required — provide it as a parameter or in the plugin config');
92
+ const agentId = str(params, 'agentId') ?? config.agentId;
93
+ const method = str(params, 'method') ?? 'POST';
94
+ const { accessToken } = await getPayments().x402.getX402AccessToken(planId, agentId);
95
+ const response = await fetch(agentUrl, {
96
+ method,
97
+ headers: {
98
+ 'Content-Type': 'application/json',
99
+ 'PAYMENT-SIGNATURE': accessToken,
100
+ },
101
+ body: method !== 'GET' ? JSON.stringify({ prompt }) : undefined,
102
+ });
103
+ if (response.status === 402) {
104
+ return result({
105
+ error: 'Payment required — insufficient credits. Order the plan first using nevermined_orderPlan.',
106
+ status: 402,
107
+ });
108
+ }
109
+ if (!response.ok) {
110
+ return result({
111
+ error: `Agent returned HTTP ${response.status}: ${response.statusText}`,
112
+ status: response.status,
113
+ });
114
+ }
115
+ const body = await response.json();
116
+ return result(body);
117
+ },
118
+ },
119
+ // --- Builder tools ---
120
+ {
121
+ name: 'nevermined_registerAgent',
122
+ label: 'Nevermined Register Agent',
123
+ description: 'Register a new AI agent with an associated payment plan on Nevermined',
124
+ parameters: {
125
+ type: 'object',
126
+ properties: {
127
+ name: { type: 'string', description: 'Agent name' },
128
+ description: { type: 'string', description: 'Agent description' },
129
+ agentUrl: { type: 'string', description: 'The endpoint URL for the agent' },
130
+ planName: { type: 'string', description: 'Name for the payment plan' },
131
+ priceAmounts: { type: 'string', description: 'Comma-separated price amounts in wei' },
132
+ priceReceivers: { type: 'string', description: 'Comma-separated receiver addresses' },
133
+ creditsAmount: { type: 'number', description: 'Number of credits in the plan' },
134
+ tokenAddress: { type: 'string', description: 'ERC20 token address (e.g. USDC). Omit for native token.' },
135
+ },
136
+ required: ['name', 'agentUrl', 'planName', 'priceAmounts', 'priceReceivers', 'creditsAmount'],
137
+ },
138
+ async execute(_id, params) {
139
+ const name = requireStr(params, 'name');
140
+ const description = str(params, 'description') ?? '';
141
+ const agentUrl = requireStr(params, 'agentUrl');
142
+ const planName = requireStr(params, 'planName');
143
+ const priceAmounts = requireStr(params, 'priceAmounts')
144
+ .split(',')
145
+ .map((s) => BigInt(s.trim()));
146
+ const priceReceivers = requireStr(params, 'priceReceivers')
147
+ .split(',')
148
+ .map((s) => s.trim());
149
+ const creditsAmount = Number(requireStr(params, 'creditsAmount'));
150
+ const tokenAddress = str(params, 'tokenAddress');
151
+ const priceConfig = {
152
+ amounts: priceAmounts,
153
+ receivers: priceReceivers,
154
+ isCrypto: true,
155
+ tokenAddress: tokenAddress ?? ZERO_ADDRESS,
156
+ contractAddress: ZERO_ADDRESS,
157
+ feeController: ZERO_ADDRESS,
158
+ externalPriceAddress: ZERO_ADDRESS,
159
+ templateAddress: ZERO_ADDRESS,
160
+ };
161
+ const res = await getPayments().agents.registerAgentAndPlan({ name, description }, { endpoints: [{ POST: agentUrl }], agentDefinitionUrl: agentUrl }, { name: planName }, priceConfig, {
162
+ isRedemptionAmountFixed: true,
163
+ redemptionType: 4,
164
+ proofRequired: false,
165
+ durationSecs: 0n,
166
+ amount: BigInt(creditsAmount),
167
+ minAmount: 1n,
168
+ maxAmount: BigInt(creditsAmount),
169
+ });
170
+ return result({ agentId: res.agentId, planId: res.planId, txHash: res.txHash });
171
+ },
172
+ },
173
+ {
174
+ name: 'nevermined_createPlan',
175
+ label: 'Nevermined Create Plan',
176
+ description: 'Create a new payment plan on Nevermined',
177
+ parameters: {
178
+ type: 'object',
179
+ properties: {
180
+ name: { type: 'string', description: 'Plan name' },
181
+ description: { type: 'string', description: 'Plan description' },
182
+ priceAmounts: { type: 'string', description: 'Comma-separated price amounts in wei' },
183
+ priceReceivers: { type: 'string', description: 'Comma-separated receiver addresses' },
184
+ creditsAmount: { type: 'number', description: 'Number of credits in the plan' },
185
+ accessLimit: { type: 'string', description: '"credits" or "time" (default: credits)' },
186
+ tokenAddress: { type: 'string', description: 'ERC20 token address (e.g. USDC). Omit for native token.' },
187
+ },
188
+ required: ['name', 'priceAmounts', 'priceReceivers', 'creditsAmount'],
189
+ },
190
+ async execute(_id, params) {
191
+ const name = requireStr(params, 'name');
192
+ const description = str(params, 'description') ?? '';
193
+ const priceAmounts = requireStr(params, 'priceAmounts')
194
+ .split(',')
195
+ .map((s) => BigInt(s.trim()));
196
+ const priceReceivers = requireStr(params, 'priceReceivers')
197
+ .split(',')
198
+ .map((s) => s.trim());
199
+ const creditsAmount = Number(requireStr(params, 'creditsAmount'));
200
+ const accessLimit = (str(params, 'accessLimit') ?? 'credits');
201
+ const tokenAddress = str(params, 'tokenAddress');
202
+ const priceConfig = {
203
+ amounts: priceAmounts,
204
+ receivers: priceReceivers,
205
+ isCrypto: true,
206
+ tokenAddress: tokenAddress ?? ZERO_ADDRESS,
207
+ contractAddress: ZERO_ADDRESS,
208
+ feeController: ZERO_ADDRESS,
209
+ externalPriceAddress: ZERO_ADDRESS,
210
+ templateAddress: ZERO_ADDRESS,
211
+ };
212
+ const res = await getPayments().plans.registerPlan({ name, description, accessLimit }, priceConfig, {
213
+ isRedemptionAmountFixed: true,
214
+ redemptionType: 4,
215
+ proofRequired: false,
216
+ durationSecs: 0n,
217
+ amount: BigInt(creditsAmount),
218
+ minAmount: 1n,
219
+ maxAmount: BigInt(creditsAmount),
220
+ }, undefined, accessLimit);
221
+ return result({ planId: res.planId });
222
+ },
223
+ },
224
+ {
225
+ name: 'nevermined_listPlans',
226
+ label: 'Nevermined List Plans',
227
+ description: "List the builder's payment plans on Nevermined",
228
+ parameters: {
229
+ type: 'object',
230
+ properties: {},
231
+ },
232
+ async execute() {
233
+ const res = await getPayments().plans.getPlans();
234
+ return result(res);
235
+ },
236
+ },
237
+ ];
7
238
  }
8
- function optionalParam(params, name, fallback) {
9
- const value = params[name];
10
- if (value === undefined || value === null || value === '') {
11
- return fallback;
12
- }
13
- return String(value);
239
+ function result(payload) {
240
+ return {
241
+ content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],
242
+ };
243
+ }
244
+ function str(params, key) {
245
+ const v = params[key];
246
+ if (v === undefined || v === null || v === '')
247
+ return undefined;
248
+ return String(v);
249
+ }
250
+ function requireStr(params, key) {
251
+ const v = str(params, key);
252
+ if (!v)
253
+ throw new Error(`Missing required parameter: ${key}`);
254
+ return v;
14
255
  }
15
- // --- Subscriber tools ---
16
- const checkBalance = {
17
- name: 'nevermined.checkBalance',
18
- description: 'Check the credit balance for a Nevermined payment plan',
19
- params: [
20
- { name: 'planId', type: 'string', description: 'The payment plan ID', required: false },
21
- ],
22
- handler: async (payments, config, params) => {
23
- const planId = optionalParam(params, 'planId', config.planId);
24
- if (!planId) {
25
- throw new Error('planId is required — provide it as a parameter or in the plugin config');
26
- }
27
- const balance = await payments.plans.getPlanBalance(planId);
28
- return {
29
- planId: balance.planId,
30
- planName: balance.planName,
31
- balance: balance.balance.toString(),
32
- isSubscriber: balance.isSubscriber,
33
- };
34
- },
35
- };
36
- const getAccessToken = {
37
- name: 'nevermined.getAccessToken',
38
- description: 'Get an x402 access token for authenticating requests to a Nevermined agent',
39
- params: [
40
- { name: 'planId', type: 'string', description: 'The payment plan ID', required: false },
41
- { name: 'agentId', type: 'string', description: 'The agent ID', required: false },
42
- ],
43
- handler: async (payments, config, params) => {
44
- const planId = optionalParam(params, 'planId', config.planId);
45
- if (!planId) {
46
- throw new Error('planId is required — provide it as a parameter or in the plugin config');
47
- }
48
- const agentId = optionalParam(params, 'agentId', config.agentId);
49
- const result = await payments.x402.getX402AccessToken(planId, agentId);
50
- return { accessToken: result.accessToken };
51
- },
52
- };
53
- const orderPlan = {
54
- name: 'nevermined.orderPlan',
55
- description: 'Order (purchase) a Nevermined payment plan',
56
- params: [
57
- { name: 'planId', type: 'string', description: 'The payment plan ID to order', required: false },
58
- ],
59
- handler: async (payments, config, params) => {
60
- const planId = optionalParam(params, 'planId', config.planId);
61
- if (!planId) {
62
- throw new Error('planId is required — provide it as a parameter or in the plugin config');
63
- }
64
- const result = await payments.plans.orderPlan(planId);
65
- return result;
66
- },
67
- };
68
- const queryAgent = {
69
- name: 'nevermined.queryAgent',
70
- description: 'Query a Nevermined AI agent end-to-end: acquires an x402 access token, sends the prompt to the agent, and returns the response',
71
- params: [
72
- { name: 'agentUrl', type: 'string', description: 'The URL of the agent to query', required: true },
73
- { name: 'prompt', type: 'string', description: 'The prompt to send to the agent', required: true },
74
- { name: 'planId', type: 'string', description: 'The payment plan ID', required: false },
75
- { name: 'agentId', type: 'string', description: 'The agent ID', required: false },
76
- { name: 'method', type: 'string', description: 'HTTP method (default: POST)', required: false },
77
- ],
78
- handler: async (payments, config, params) => {
79
- const agentUrl = requireParam(params, 'agentUrl');
80
- const prompt = requireParam(params, 'prompt');
81
- const planId = optionalParam(params, 'planId', config.planId);
82
- if (!planId) {
83
- throw new Error('planId is required — provide it as a parameter or in the plugin config');
84
- }
85
- const agentId = optionalParam(params, 'agentId', config.agentId);
86
- const method = optionalParam(params, 'method', 'POST') ?? 'POST';
87
- const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId);
88
- const response = await fetch(agentUrl, {
89
- method,
90
- headers: {
91
- 'Content-Type': 'application/json',
92
- 'PAYMENT-SIGNATURE': accessToken,
93
- },
94
- body: method !== 'GET' ? JSON.stringify({ prompt }) : undefined,
95
- });
96
- if (response.status === 402) {
97
- return {
98
- error: 'Payment required — insufficient credits. Order the plan first using nevermined.orderPlan.',
99
- status: 402,
100
- };
101
- }
102
- if (!response.ok) {
103
- return {
104
- error: `Agent returned HTTP ${response.status}: ${response.statusText}`,
105
- status: response.status,
106
- };
107
- }
108
- const body = await response.json();
109
- return body;
110
- },
111
- };
112
- // --- Builder tools ---
113
- const registerAgent = {
114
- name: 'nevermined.registerAgent',
115
- description: 'Register a new AI agent with an associated payment plan on Nevermined',
116
- params: [
117
- { name: 'name', type: 'string', description: 'Agent name', required: true },
118
- { name: 'description', type: 'string', description: 'Agent description', required: false },
119
- { name: 'agentUrl', type: 'string', description: 'The endpoint URL for the agent', required: true },
120
- { name: 'planName', type: 'string', description: 'Name for the payment plan', required: true },
121
- { name: 'priceAmounts', type: 'string', description: 'Comma-separated price amounts in wei', required: true },
122
- { name: 'priceReceivers', type: 'string', description: 'Comma-separated receiver addresses', required: true },
123
- { name: 'creditsAmount', type: 'number', description: 'Number of credits in the plan', required: true },
124
- ],
125
- handler: async (payments, _config, params) => {
126
- const name = requireParam(params, 'name');
127
- const description = optionalParam(params, 'description', '');
128
- const agentUrl = requireParam(params, 'agentUrl');
129
- const planName = requireParam(params, 'planName');
130
- const priceAmounts = requireParam(params, 'priceAmounts')
131
- .split(',')
132
- .map((s) => BigInt(s.trim()));
133
- const priceReceivers = requireParam(params, 'priceReceivers')
134
- .split(',')
135
- .map((s) => s.trim());
136
- const creditsAmount = Number(requireParam(params, 'creditsAmount'));
137
- const result = await payments.agents.registerAgentAndPlan({ name, description }, {
138
- endpoints: [{ POST: agentUrl }],
139
- agentDefinitionUrl: agentUrl,
140
- }, { name: planName }, {
141
- amounts: priceAmounts,
142
- receivers: priceReceivers,
143
- isCrypto: true,
144
- }, {
145
- isRedemptionAmountFixed: true,
146
- redemptionType: 4, // ONLY_SUBSCRIBER
147
- proofRequired: false,
148
- durationSecs: 0n,
149
- amount: BigInt(creditsAmount),
150
- minAmount: 1n,
151
- maxAmount: BigInt(creditsAmount),
152
- });
153
- return {
154
- agentId: result.agentId,
155
- planId: result.planId,
156
- txHash: result.txHash,
157
- };
158
- },
159
- };
160
- const createPlan = {
161
- name: 'nevermined.createPlan',
162
- description: 'Create a new payment plan on Nevermined',
163
- params: [
164
- { name: 'name', type: 'string', description: 'Plan name', required: true },
165
- { name: 'description', type: 'string', description: 'Plan description', required: false },
166
- { name: 'priceAmounts', type: 'string', description: 'Comma-separated price amounts in wei', required: true },
167
- { name: 'priceReceivers', type: 'string', description: 'Comma-separated receiver addresses', required: true },
168
- { name: 'creditsAmount', type: 'number', description: 'Number of credits in the plan', required: true },
169
- { name: 'accessLimit', type: 'string', description: '"credits" or "time" (default: credits)', required: false },
170
- ],
171
- handler: async (payments, _config, params) => {
172
- const name = requireParam(params, 'name');
173
- const description = optionalParam(params, 'description', '');
174
- const priceAmounts = requireParam(params, 'priceAmounts')
175
- .split(',')
176
- .map((s) => BigInt(s.trim()));
177
- const priceReceivers = requireParam(params, 'priceReceivers')
178
- .split(',')
179
- .map((s) => s.trim());
180
- const creditsAmount = Number(requireParam(params, 'creditsAmount'));
181
- const accessLimit = optionalParam(params, 'accessLimit', 'credits');
182
- const result = await payments.plans.registerPlan({ name, description, accessLimit }, {
183
- amounts: priceAmounts,
184
- receivers: priceReceivers,
185
- isCrypto: true,
186
- }, {
187
- isRedemptionAmountFixed: true,
188
- redemptionType: 4,
189
- proofRequired: false,
190
- durationSecs: 0n,
191
- amount: BigInt(creditsAmount),
192
- minAmount: 1n,
193
- maxAmount: BigInt(creditsAmount),
194
- }, undefined, accessLimit);
195
- return { planId: result.planId };
196
- },
197
- };
198
- const listPlans = {
199
- name: 'nevermined.listPlans',
200
- description: "List the builder's payment plans on Nevermined",
201
- params: [],
202
- handler: async (payments) => {
203
- const result = await payments.plans.getPlans();
204
- return result;
205
- },
206
- };
207
- export const allTools = [
208
- // Subscriber
209
- checkBalance,
210
- getAccessToken,
211
- orderPlan,
212
- queryAgent,
213
- // Builder
214
- registerAgent,
215
- createPlan,
216
- listPlans,
217
- ];
218
256
  //# sourceMappingURL=tools.js.map