agentic-x402 0.3.7 → 0.3.8

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "agentic-x402",
3
3
  "name": "x402 Payments",
4
- "version": "0.3.7",
4
+ "version": "0.3.8",
5
5
  "description": "Agent plugin for x402 payments — pay for and sell gated content with background payment watching",
6
6
  "configSchema": {
7
7
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-x402",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Agent skill for x402 payments - pay for and sell gated content",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,20 +4,28 @@
4
4
  import type { PluginTool } from './types.js';
5
5
  import type { PaymentWatcher } from './watcher.js';
6
6
 
7
+ /** Helper to wrap a result as OpenClaw tool response */
8
+ function json(payload: unknown) {
9
+ return {
10
+ content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],
11
+ details: payload,
12
+ };
13
+ }
14
+
7
15
  export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
8
16
  return [
9
17
  // 1. x402_balance
10
18
  {
11
19
  name: 'x402_balance',
12
20
  description: 'Check wallet USDC and ETH balances on Base',
13
- inputSchema: { type: 'object', properties: {}, required: [] },
14
- async execute() {
21
+ parameters: { type: 'object', properties: {} },
22
+ async execute(_toolCallId, _params) {
15
23
  const { getClient, getWalletAddress, getUsdcBalance, getEthBalance } = await import('../core/client.js');
16
24
  const client = getClient();
17
25
  const address = getWalletAddress();
18
26
  const [usdc, eth] = await Promise.all([getUsdcBalance(), getEthBalance()]);
19
27
 
20
- return {
28
+ return json({
21
29
  address,
22
30
  network: client.config.network,
23
31
  chainId: client.config.chainId,
@@ -25,7 +33,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
25
33
  usdc: { raw: usdc.raw.toString(), formatted: usdc.formatted },
26
34
  eth: { raw: eth.raw.toString(), formatted: eth.formatted },
27
35
  },
28
- };
36
+ });
29
37
  },
30
38
  },
31
39
 
@@ -33,7 +41,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
33
41
  {
34
42
  name: 'x402_pay',
35
43
  description: 'Pay for an x402-gated resource. Returns the response body after payment.',
36
- inputSchema: {
44
+ parameters: {
37
45
  type: 'object',
38
46
  properties: {
39
47
  url: { type: 'string', description: 'URL of the x402-gated resource' },
@@ -44,7 +52,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
44
52
  },
45
53
  required: ['url'],
46
54
  },
47
- async execute(params) {
55
+ async execute(_toolCallId, params) {
48
56
  const url = params.url as string;
49
57
  const method = (params.method as string) || 'GET';
50
58
  const body = params.body as string | undefined;
@@ -65,7 +73,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
65
73
  const responseBody = contentType?.includes('json')
66
74
  ? await probe.json()
67
75
  : await probe.text();
68
- return { paid: false, status: probe.status, response: responseBody };
76
+ return json({ paid: false, status: probe.status, response: responseBody });
69
77
  }
70
78
 
71
79
  // Parse payment info
@@ -84,18 +92,18 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
84
92
  const effectiveMax = maxPaymentUsd ?? client.config.maxPaymentUsd;
85
93
 
86
94
  if (priceNum > effectiveMax) {
87
- return { paid: false, error: `Price $${priceNum} exceeds max $${effectiveMax}`, paymentInfo };
95
+ return json({ paid: false, error: `Price $${priceNum} exceeds max $${effectiveMax}`, paymentInfo });
88
96
  }
89
97
 
90
98
  if (dryRun) {
91
- return { paid: false, dryRun: true, price: priceNum, paymentInfo };
99
+ return json({ paid: false, dryRun: true, price: priceNum, paymentInfo });
92
100
  }
93
101
 
94
102
  // Execute payment
95
103
  const response = await client.fetchWithPayment(url, { method, body, headers });
96
104
 
97
105
  if (!response.ok) {
98
- return { paid: false, error: `${response.status} ${response.statusText}` };
106
+ return json({ paid: false, error: `${response.status} ${response.statusText}` });
99
107
  }
100
108
 
101
109
  const contentType = response.headers.get('content-type');
@@ -112,7 +120,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
112
120
  } catch { /* */ }
113
121
  }
114
122
 
115
- return { paid: true, price: priceNum, transactionHash: txHash, response: responseBody };
123
+ return json({ paid: true, price: priceNum, transactionHash: txHash, response: responseBody });
116
124
  },
117
125
  },
118
126
 
@@ -120,7 +128,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
120
128
  {
121
129
  name: 'x402_fetch',
122
130
  description: 'Fetch a URL with automatic x402 payment handling. Simpler than x402_pay — just returns the content.',
123
- inputSchema: {
131
+ parameters: {
124
132
  type: 'object',
125
133
  properties: {
126
134
  url: { type: 'string', description: 'URL to fetch' },
@@ -129,7 +137,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
129
137
  },
130
138
  required: ['url'],
131
139
  },
132
- async execute(params) {
140
+ async execute(_toolCallId, params) {
133
141
  const url = params.url as string;
134
142
  const method = (params.method as string) || 'GET';
135
143
  const body = params.body as string | undefined;
@@ -143,7 +151,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
143
151
  const response = await client.fetchWithPayment(url, { method, body, headers });
144
152
 
145
153
  if (!response.ok) {
146
- return { success: false, status: response.status, error: response.statusText };
154
+ return json({ success: false, status: response.status, error: response.statusText });
147
155
  }
148
156
 
149
157
  const contentType = response.headers.get('content-type');
@@ -151,7 +159,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
151
159
  ? await response.json()
152
160
  : await response.text();
153
161
 
154
- return { success: true, status: response.status, response: responseBody };
162
+ return json({ success: true, status: response.status, response: responseBody });
155
163
  },
156
164
  },
157
165
 
@@ -159,7 +167,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
159
167
  {
160
168
  name: 'x402_create_link',
161
169
  description: 'Create a payment link via 21.cash to sell gated content',
162
- inputSchema: {
170
+ parameters: {
163
171
  type: 'object',
164
172
  properties: {
165
173
  name: { type: 'string', description: 'Name of the payment link' },
@@ -171,7 +179,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
171
179
  },
172
180
  required: ['name', 'price'],
173
181
  },
174
- async execute(params) {
182
+ async execute(_toolCallId, params) {
175
183
  const name = params.name as string;
176
184
  const price = params.price as string;
177
185
  const gatedUrl = params.url as string | undefined;
@@ -180,7 +188,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
180
188
  const webhookUrl = params.webhookUrl as string | undefined;
181
189
 
182
190
  if (!gatedUrl && !gatedText) {
183
- return { success: false, error: 'Either url or text is required' };
191
+ return json({ success: false, error: 'Either url or text is required' });
184
192
  }
185
193
 
186
194
  const { getClient, getWalletAddress } = await import('../core/client.js');
@@ -210,10 +218,10 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
210
218
 
211
219
  const data = await response.json();
212
220
  if (!response.ok || !(data as Record<string, unknown>).success) {
213
- return { success: false, error: (data as Record<string, unknown>).error ?? 'Unknown error' };
221
+ return json({ success: false, error: (data as Record<string, unknown>).error ?? 'Unknown error' });
214
222
  }
215
223
 
216
- return data;
224
+ return json(data);
217
225
  },
218
226
  },
219
227
 
@@ -221,14 +229,14 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
221
229
  {
222
230
  name: 'x402_link_info',
223
231
  description: 'Get details about a payment link by router address',
224
- inputSchema: {
232
+ parameters: {
225
233
  type: 'object',
226
234
  properties: {
227
235
  routerAddress: { type: 'string', description: 'Router contract address or payment URL' },
228
236
  },
229
237
  required: ['routerAddress'],
230
238
  },
231
- async execute(params) {
239
+ async execute(_toolCallId, params) {
232
240
  let routerAddress = params.routerAddress as string;
233
241
 
234
242
  // Extract address from URL if needed
@@ -239,7 +247,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
239
247
  }
240
248
 
241
249
  if (!routerAddress.startsWith('0x') || routerAddress.length !== 42) {
242
- return { success: false, error: 'Invalid router address' };
250
+ return json({ success: false, error: 'Invalid router address' });
243
251
  }
244
252
 
245
253
  const { getConfig } = await import('../core/config.js');
@@ -250,10 +258,10 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
250
258
  const data = await response.json();
251
259
 
252
260
  if (!response.ok || !(data as Record<string, unknown>).success) {
253
- return { success: false, error: (data as Record<string, unknown>).error ?? 'Link not found' };
261
+ return json({ success: false, error: (data as Record<string, unknown>).error ?? 'Link not found' });
254
262
  }
255
263
 
256
- return data;
264
+ return json(data);
257
265
  },
258
266
  },
259
267
 
@@ -261,14 +269,13 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
261
269
  {
262
270
  name: 'x402_routers',
263
271
  description: 'List payment routers where your wallet is a beneficiary',
264
- inputSchema: {
272
+ parameters: {
265
273
  type: 'object',
266
274
  properties: {
267
275
  withBalances: { type: 'boolean', description: 'Fetch on-chain USDC balance for each router' },
268
276
  },
269
- required: [],
270
277
  },
271
- async execute(params) {
278
+ async execute(_toolCallId, params) {
272
279
  const withBalances = params.withBalances as boolean | undefined;
273
280
 
274
281
  const { getWalletAddress } = await import('../core/client.js');
@@ -282,13 +289,13 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
282
289
  const data = await response.json() as { success: boolean; links?: Array<Record<string, unknown>>; error?: string };
283
290
 
284
291
  if (!response.ok || !data.success) {
285
- return { success: false, error: data.error ?? 'Failed to fetch routers' };
292
+ return json({ success: false, error: data.error ?? 'Failed to fetch routers' });
286
293
  }
287
294
 
288
295
  const links = data.links ?? [];
289
296
 
290
297
  if (!withBalances) {
291
- return {
298
+ return json({
292
299
  success: true,
293
300
  address,
294
301
  routers: links.map((l) => ({
@@ -298,7 +305,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
298
305
  sharePercent: l.beneficiary_percentage,
299
306
  createdAt: l.created_at,
300
307
  })),
301
- };
308
+ });
302
309
  }
303
310
 
304
311
  // Fetch balances
@@ -340,7 +347,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
340
347
  };
341
348
  }));
342
349
 
343
- return { success: true, address, routers };
350
+ return json({ success: true, address, routers });
344
351
  },
345
352
  },
346
353
 
@@ -348,7 +355,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
348
355
  {
349
356
  name: 'x402_distribute',
350
357
  description: 'Distribute (withdraw) USDC from a PaymentRouter contract',
351
- inputSchema: {
358
+ parameters: {
352
359
  type: 'object',
353
360
  properties: {
354
361
  routerAddress: { type: 'string', description: 'PaymentRouter contract address' },
@@ -356,12 +363,12 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
356
363
  },
357
364
  required: ['routerAddress'],
358
365
  },
359
- async execute(params) {
366
+ async execute(_toolCallId, params) {
360
367
  const routerAddress = params.routerAddress as string;
361
368
  const specifiedAmount = params.amount as string | undefined;
362
369
 
363
370
  if (!routerAddress.startsWith('0x') || routerAddress.length !== 42) {
364
- return { success: false, error: 'Invalid router address' };
371
+ return json({ success: false, error: 'Invalid router address' });
365
372
  }
366
373
 
367
374
  const { getClient } = await import('../core/client.js');
@@ -400,17 +407,17 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
400
407
  });
401
408
 
402
409
  if (routerBalance === 0n) {
403
- return { success: false, error: 'Router has no USDC balance to distribute' };
410
+ return json({ success: false, error: 'Router has no USDC balance to distribute' });
404
411
  }
405
412
 
406
413
  let distributeAmount: bigint;
407
414
  if (specifiedAmount) {
408
415
  distributeAmount = parseUnits(specifiedAmount, 6);
409
416
  if (distributeAmount > routerBalance) {
410
- return {
417
+ return json({
411
418
  success: false,
412
419
  error: `Requested ${specifiedAmount} USDC exceeds balance ${formatUnits(routerBalance, 6)} USDC`,
413
- };
420
+ });
414
421
  }
415
422
  } else {
416
423
  distributeAmount = routerBalance;
@@ -426,7 +433,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
426
433
 
427
434
  const receipt = await client.publicClient.waitForTransactionReceipt({ hash: txHash });
428
435
 
429
- return {
436
+ return json({
430
437
  success: receipt.status === 'success',
431
438
  routerAddress,
432
439
  amount: formatUnits(distributeAmount, 6),
@@ -434,7 +441,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
434
441
  transactionHash: txHash,
435
442
  blockNumber: receipt.blockNumber.toString(),
436
443
  status: receipt.status,
437
- };
444
+ });
438
445
  },
439
446
  },
440
447
 
@@ -442,12 +449,12 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
442
449
  {
443
450
  name: 'x402_watcher_status',
444
451
  description: 'Get the status of the background payment watcher (tracked routers, payments detected)',
445
- inputSchema: { type: 'object', properties: {}, required: [] },
452
+ parameters: { type: 'object', properties: {} },
446
453
  async execute() {
447
454
  if (!watcher) {
448
- return { running: false, error: 'Watcher is not enabled' };
455
+ return json({ running: false, error: 'Watcher is not enabled' });
449
456
  }
450
- return watcher.getStatus();
457
+ return json(watcher.getStatus());
451
458
  },
452
459
  },
453
460
  ];
@@ -15,24 +15,33 @@ export interface PluginService {
15
15
  stop(): Promise<void>;
16
16
  }
17
17
 
18
- /** JSON Schema for tool input parameters */
19
- export interface ToolInputSchema {
18
+ /** JSON Schema for tool parameters */
19
+ export interface ToolParameters {
20
20
  type: 'object';
21
21
  properties: Record<string, {
22
22
  type: string;
23
- description: string;
23
+ description?: string;
24
24
  enum?: string[];
25
25
  default?: unknown;
26
26
  }>;
27
27
  required?: string[];
28
28
  }
29
29
 
30
+ /** Tool result content block */
31
+ export interface ToolResultContent {
32
+ type: 'text';
33
+ text: string;
34
+ }
35
+
30
36
  /** An agent tool registered by a plugin */
31
37
  export interface PluginTool {
32
38
  name: string;
33
39
  description: string;
34
- inputSchema: ToolInputSchema;
35
- execute(params: Record<string, unknown>): Promise<unknown>;
40
+ parameters: ToolParameters;
41
+ execute(toolCallId: string, params: Record<string, unknown>): Promise<{
42
+ content: ToolResultContent[];
43
+ details?: unknown;
44
+ }>;
36
45
  }
37
46
 
38
47
  /** Commander.js-style program for CLI registration */