wip-agent-pay 1.0.1

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/cli.js ADDED
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env node
2
+ // wip-pay CLI
3
+ // Your agent's wallet.
4
+ //
5
+ // Commands:
6
+ // wip-pay pay <url> Pay for paywalled content
7
+ // wip-pay fund <amount> Fund wallet (Apple Pay)
8
+ // wip-pay balance Check wallet balance
9
+ // wip-pay history Transaction history
10
+ // wip-pay budget View/set spending limits
11
+ // wip-pay <amount> <service> [note] Mint one-time URL
12
+ //
13
+ // Default: Pool Mode (Apple Pay per transaction, Parker's float)
14
+ // --wallet=cdp or --wallet=privy: Path 1 (self-custody, instant)
15
+
16
+ import { argv } from 'node:process';
17
+ import { pay, fund, mint, balance, history, budget } from './providers/index.js';
18
+
19
+ const args = argv.slice(2);
20
+ const command = args[0];
21
+
22
+ // --- Help ---
23
+ if (args.includes('--help') || args.length === 0) {
24
+ console.log('\n wip-pay ... your agent\'s wallet');
25
+ console.log('\n Commands:');
26
+ console.log(' wip-pay pay <url> Pay for paywalled content');
27
+ console.log(' wip-pay fund <amount> Fund wallet (Apple Pay)');
28
+ console.log(' wip-pay balance Check wallet balance');
29
+ console.log(' wip-pay history Transaction history');
30
+ console.log(' wip-pay budget View spending limits');
31
+ console.log(' wip-pay budget set <daily> [perTx] Set spending limits');
32
+ console.log(' wip-pay <amount> <service> [note] Mint one-time URL');
33
+ console.log('\n Options:');
34
+ console.log(' --wallet=cdp Use your CDP wallet (instant, no Apple Pay)');
35
+ console.log(' --wallet=privy Use your Privy wallet (instant, no Apple Pay)');
36
+ console.log(' (no flag) Pool Mode: Apple Pay per transaction');
37
+ console.log('\n Pool Mode pricing:');
38
+ console.log(' x402 price + Stripe fees + $0.25 flat fee');
39
+ console.log(' Max $25 per transaction. Over $25 requires your own wallet.');
40
+ console.log('\n Examples:');
41
+ console.log(' wip-pay pay https://morning-stew.../v1/issues/MS-3');
42
+ console.log(' wip-pay pay https://morning-stew.../v1/issues/MS-3 --wallet=cdp');
43
+ console.log(' wip-pay fund 10');
44
+ console.log(' wip-pay balance');
45
+ console.log(' wip-pay history');
46
+ console.log(' wip-pay budget set 5.00 1.00');
47
+ console.log(' wip-pay 0.10 morning-stew "MS-#8"');
48
+ console.log('\n Setup:');
49
+ console.log(' See SETUP.md for configuration.\n');
50
+ process.exit(0);
51
+ }
52
+
53
+ // Parse flags
54
+ const walletFlag = args.find(a => a.startsWith('--wallet='));
55
+ const wallet = walletFlag ? walletFlag.split('=')[1] : undefined; // undefined = Pool Mode
56
+ const cleanArgs = args.filter(a => !a.startsWith('--'));
57
+
58
+ // --- Pay ---
59
+ if (command === 'pay') {
60
+ const url = cleanArgs[1];
61
+ if (!url) {
62
+ console.error(' Error: url is required. Usage: wip-pay pay <url>');
63
+ process.exit(1);
64
+ }
65
+
66
+ const result = await pay(url, { wallet });
67
+
68
+ // Path 1: wallet-signed payment (instant)
69
+ if (result.success && !result.needsPayment) {
70
+ if (result.free) {
71
+ console.log(`\n Content is free. No payment needed.\n`);
72
+ } else {
73
+ console.log(`\n Paid: $${result.amount}`);
74
+ console.log(` Service: ${result.service}`);
75
+ if (result.txHash) console.log(` Tx: ${result.txHash}`);
76
+ console.log();
77
+ }
78
+ }
79
+ // Pool Mode: needs Apple Pay
80
+ else if (result.success && result.needsPayment) {
81
+ const p = result.pricing;
82
+ console.log(`\n x402 price: $${p.x402Amount}`);
83
+ console.log(` Our fee: $${p.poolFee}`);
84
+ console.log(` Stripe fee: $${p.stripeFee}`);
85
+ console.log(` Total charge: $${p.totalCharge}`);
86
+ console.log(` Service: ${result.service}`);
87
+ console.log(`\n Opening Apple Pay checkout...`);
88
+
89
+ // Open checkout in browser
90
+ try {
91
+ const { execSync } = await import('node:child_process');
92
+ execSync(`open "${result.checkoutUrl}"`, { stdio: 'ignore' });
93
+ console.log(' (Opened in your browser)');
94
+ } catch {
95
+ console.log(` Open this URL to pay: ${result.checkoutUrl}`);
96
+ }
97
+
98
+ // Wait for payment confirmation
99
+ console.log(' Waiting for payment...\n');
100
+ const { waitForPayment } = await import('./providers/passthrough.js');
101
+ const confirmation = await waitForPayment(result.paymentId);
102
+
103
+ if (confirmation.success) {
104
+ console.log(` Paid. Content received.\n`);
105
+ } else {
106
+ console.error(` ${confirmation.error || 'Payment not completed.'}\n`);
107
+ process.exit(1);
108
+ }
109
+ }
110
+ // Over pool limit
111
+ else if (result.overPoolLimit) {
112
+ console.log(`\n This costs $${result.amount}. That exceeds the $${result.poolMax} pool limit.`);
113
+ console.log(` Use your own wallet: wip-pay pay ${url} --wallet=cdp`);
114
+ console.log(` Or set up a user wallet: see SETUP.md\n`);
115
+ process.exit(1);
116
+ }
117
+ // Error
118
+ else {
119
+ console.error(`\n Payment failed: ${result.error || 'unknown error'}\n`);
120
+ process.exit(1);
121
+ }
122
+ }
123
+
124
+ // --- Fund (Apple Pay) ---
125
+ else if (command === 'fund') {
126
+ const amount = parseFloat(cleanArgs[1]);
127
+ if (isNaN(amount) || amount <= 0) {
128
+ console.error(' Error: amount must be a positive number. Usage: wip-pay fund <amount>');
129
+ process.exit(1);
130
+ }
131
+
132
+ const result = await fund(amount, { wallet });
133
+
134
+ if (result.success) {
135
+ console.log(`\n Checkout ready. Open this URL to fund your wallet:`);
136
+ console.log(` ${result.checkoutUrl}`);
137
+ console.log(`\n Amount: $${result.amount}`);
138
+ console.log(` Wallet: ${result.wallet}\n`);
139
+
140
+ // Try to open in browser
141
+ try {
142
+ const { execSync } = await import('node:child_process');
143
+ execSync(`open "${result.checkoutUrl}"`, { stdio: 'ignore' });
144
+ console.log(' (Opened in your browser)\n');
145
+ } catch {
146
+ // Not on macOS or open failed
147
+ }
148
+ } else {
149
+ console.error(`\n Funding failed: ${result.error || 'unknown error'}\n`);
150
+ process.exit(1);
151
+ }
152
+ }
153
+
154
+ // --- Balance ---
155
+ else if (command === 'balance') {
156
+ const result = await balance({ wallet });
157
+
158
+ if (result.error) {
159
+ console.error(`\n ${result.error}\n`);
160
+ process.exit(1);
161
+ }
162
+
163
+ console.log(`\n Wallet: ${wallet || 'pool'}`);
164
+ console.log(` Balance: $${result.balance || '0.00'}`);
165
+ if (result.address) console.log(` Address: ${result.address}`);
166
+ console.log();
167
+ }
168
+
169
+ // --- History ---
170
+ else if (command === 'history') {
171
+ const limit = parseInt(cleanArgs[1]) || 20;
172
+ const result = await history({ wallet, limit });
173
+
174
+ if (result.error) {
175
+ console.error(`\n ${result.error}\n`);
176
+ process.exit(1);
177
+ }
178
+
179
+ console.log(`\n Wallet: ${wallet || 'pool'}`);
180
+ console.log(` Recent transactions:\n`);
181
+
182
+ if (!result.transactions || result.transactions.length === 0) {
183
+ console.log(' No transactions yet.\n');
184
+ } else {
185
+ for (const tx of result.transactions) {
186
+ const dir = tx.type === 'fund' ? '+' : '-';
187
+ const date = new Date(tx.timestamp).toLocaleString();
188
+ console.log(` ${dir}$${tx.amount} ${tx.service || tx.type} ${date}`);
189
+ if (tx.note) console.log(` ${tx.note}`);
190
+ }
191
+ console.log();
192
+ }
193
+ }
194
+
195
+ // --- Budget ---
196
+ else if (command === 'budget') {
197
+ const subcommand = cleanArgs[1];
198
+
199
+ if (subcommand === 'set') {
200
+ const daily = parseFloat(cleanArgs[2]);
201
+ const perTx = cleanArgs[3] ? parseFloat(cleanArgs[3]) : undefined;
202
+
203
+ if (isNaN(daily) || daily <= 0) {
204
+ console.error(' Error: daily limit required. Usage: wip-pay budget set <daily> [perTx]');
205
+ process.exit(1);
206
+ }
207
+
208
+ const result = await budget({ wallet, daily, perTx });
209
+
210
+ if (result.error) {
211
+ console.error(`\n ${result.error}\n`);
212
+ process.exit(1);
213
+ }
214
+
215
+ console.log(`\n Budget updated:`);
216
+ console.log(` Daily limit: $${result.daily}`);
217
+ if (result.perTx) console.log(` Per-transaction limit: $${result.perTx}`);
218
+ console.log();
219
+ } else {
220
+ // View current budget
221
+ const result = await budget({ wallet });
222
+
223
+ if (result.error) {
224
+ console.error(`\n ${result.error}\n`);
225
+ process.exit(1);
226
+ }
227
+
228
+ console.log(`\n Wallet: ${wallet || 'pool'}`);
229
+ console.log(` Daily limit: $${result.daily || 'none'}`);
230
+ console.log(` Per-transaction limit: $${result.perTx || 'none'}`);
231
+ console.log(` Spent today: $${result.spentToday || '0.00'}`);
232
+ console.log(` Remaining today: $${result.remainingToday || result.daily || 'unlimited'}`);
233
+ console.log();
234
+ }
235
+ }
236
+
237
+ // --- Mint one-time URL (existing Mode B) ---
238
+ else {
239
+ const amount = parseFloat(cleanArgs[0]);
240
+ const service = cleanArgs[1];
241
+ const note = cleanArgs[2] || '';
242
+
243
+ if (isNaN(amount) || amount <= 0) {
244
+ console.error(' Error: amount must be a positive number');
245
+ process.exit(1);
246
+ }
247
+ if (!service) {
248
+ console.error(' Error: service is required. Usage: wip-pay <amount> <service> [note]');
249
+ process.exit(1);
250
+ }
251
+
252
+ const result = await mint(amount, service, note);
253
+
254
+ if (result.success) {
255
+ if (result.demo) {
256
+ console.log(` Demo URL (not real): ${result.url}\n`);
257
+ } else {
258
+ console.log(`\n One-time URL (paste this back to your agent):`);
259
+ console.log(` ${result.url}\n`);
260
+ }
261
+ } else {
262
+ console.error(`\n Payment failed: ${result.error || 'unknown error'}\n`);
263
+ process.exit(1);
264
+ }
265
+ }
package/detect.mjs ADDED
@@ -0,0 +1,13 @@
1
+ import fs from 'fs';
2
+
3
+ export default async function detect(dir) {
4
+ try {
5
+ return (
6
+ fs.existsSync(`${dir}/SKILL.md`) &&
7
+ fs.existsSync(`${dir}/SPEC.md`) &&
8
+ fs.existsSync(`${dir}/package.json`)
9
+ );
10
+ } catch {
11
+ return false;
12
+ }
13
+ }
@@ -0,0 +1,17 @@
1
+ # examples/minimal/
2
+
3
+ Minimal example of using wip-agent-pay in your agent.
4
+
5
+ Just say to any agent:
6
+
7
+ > Authorize 0.10 USDC for morning-stew MS-#8
8
+
9
+ The agent will automatically call:
10
+
11
+ ```bash
12
+ wip-agent-pay 0.10 morning-stew "MS-#8"
13
+ ```
14
+
15
+ You run it ... approve on Coinbase ... paste the returned URL back to the agent.
16
+
17
+ See REFERENCE.md for full setup.
package/install.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ console.log('\n wip-agent-pay installed successfully!');
3
+ console.log('');
4
+ console.log(' Run `wip-agent-pay --help` to see usage');
5
+ console.log(' Complete the one-time Coinbase setup in REFERENCE.md');
6
+ console.log('');
@@ -0,0 +1,131 @@
1
+ // lib/cdp-auth.js
2
+ // Ed25519 JWT generation for Coinbase CDP Server Wallets v2.
3
+ // Uses Web Crypto API ... works in Cloudflare Workers and Node 20+.
4
+ //
5
+ // CDP requires two JWTs per request:
6
+ // 1. Bearer token (API auth) ... signed with API Key Secret
7
+ // 2. X-Wallet-Auth (wallet auth) ... signed with Wallet Secret
8
+ //
9
+ // Both use Ed25519 (EdDSA).
10
+
11
+ /**
12
+ * Base64url encode a buffer.
13
+ */
14
+ function base64url(buffer) {
15
+ const bytes = new Uint8Array(buffer);
16
+ let str = '';
17
+ for (const b of bytes) str += String.fromCharCode(b);
18
+ return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
19
+ }
20
+
21
+ /**
22
+ * Import an Ed25519 private key from base64-encoded raw bytes.
23
+ */
24
+ async function importEd25519Key(base64Secret) {
25
+ const raw = Uint8Array.from(atob(base64Secret), c => c.charCodeAt(0));
26
+ // Ed25519 PKCS8 wrapping: OID prefix + raw 32-byte key
27
+ // PKCS8 header for Ed25519: 302e020100300506032b657004220420
28
+ const pkcs8Prefix = new Uint8Array([
29
+ 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06,
30
+ 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20
31
+ ]);
32
+ const pkcs8 = new Uint8Array(pkcs8Prefix.length + raw.length);
33
+ pkcs8.set(pkcs8Prefix);
34
+ pkcs8.set(raw, pkcs8Prefix.length);
35
+
36
+ return crypto.subtle.importKey(
37
+ 'pkcs8', pkcs8, { name: 'Ed25519' }, false, ['sign']
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Sign a JWT payload with Ed25519.
43
+ */
44
+ async function signJWT(payload, key) {
45
+ const header = { alg: 'EdDSA', typ: 'JWT' };
46
+ const enc = new TextEncoder();
47
+ const headerB64 = base64url(enc.encode(JSON.stringify(header)));
48
+ const payloadB64 = base64url(enc.encode(JSON.stringify(payload)));
49
+ const signingInput = `${headerB64}.${payloadB64}`;
50
+ const signature = await crypto.subtle.sign('Ed25519', key, enc.encode(signingInput));
51
+ return `${signingInput}.${base64url(signature)}`;
52
+ }
53
+
54
+ /**
55
+ * Generate the Bearer token (API auth JWT).
56
+ *
57
+ * @param {string} apiKeyId - CDP API Key ID
58
+ * @param {string} apiKeySecret - CDP API Key Secret (base64 Ed25519)
59
+ * @param {string} method - HTTP method (GET, POST, etc.)
60
+ * @param {string} url - Full request URL
61
+ */
62
+ export async function createBearerToken(apiKeyId, apiKeySecret, method, url) {
63
+ const key = await importEd25519Key(apiKeySecret);
64
+ const now = Math.floor(Date.now() / 1000);
65
+ const parsed = new URL(url);
66
+ const uri = `${method.toUpperCase()} ${parsed.host}${parsed.pathname}`;
67
+
68
+ const payload = {
69
+ sub: apiKeyId,
70
+ iss: 'cdp',
71
+ aud: ['cdp_service'],
72
+ nbf: now,
73
+ exp: now + 120,
74
+ uris: [uri],
75
+ };
76
+
77
+ return signJWT(payload, key);
78
+ }
79
+
80
+ /**
81
+ * Generate the X-Wallet-Auth JWT.
82
+ *
83
+ * @param {string} walletSecret - Wallet Secret (base64 Ed25519)
84
+ * @param {string} method - HTTP method
85
+ * @param {string} url - Full request URL
86
+ * @param {string} body - Request body (JSON string) or empty
87
+ */
88
+ export async function createWalletAuth(walletSecret, method, url, body = '') {
89
+ const key = await importEd25519Key(walletSecret);
90
+ const now = Math.floor(Date.now() / 1000);
91
+ const parsed = new URL(url);
92
+ const uri = `${method.toUpperCase()} ${parsed.host}${parsed.pathname}`;
93
+
94
+ // SHA-256 hash of canonical request body
95
+ const enc = new TextEncoder();
96
+ const bodyHash = await crypto.subtle.digest('SHA-256', enc.encode(body));
97
+ const reqHash = base64url(bodyHash);
98
+
99
+ const jti = crypto.randomUUID();
100
+
101
+ const payload = {
102
+ iat: now,
103
+ nbf: now,
104
+ exp: now + 120,
105
+ jti,
106
+ uris: [uri],
107
+ reqHash,
108
+ };
109
+
110
+ return signJWT(payload, key);
111
+ }
112
+
113
+ /**
114
+ * Generate both auth headers for a CDP API request.
115
+ *
116
+ * @param {object} creds - { apiKeyId, apiKeySecret, walletSecret }
117
+ * @param {string} method - HTTP method
118
+ * @param {string} url - Full request URL
119
+ * @param {string} body - Request body JSON string
120
+ * @returns {{ authorization: string, walletAuth: string }}
121
+ */
122
+ export async function createAuthHeaders(creds, method, url, body = '') {
123
+ const [bearer, walletAuth] = await Promise.all([
124
+ createBearerToken(creds.apiKeyId, creds.apiKeySecret, method, url),
125
+ createWalletAuth(creds.walletSecret, method, url, body),
126
+ ]);
127
+ return {
128
+ authorization: `Bearer ${bearer}`,
129
+ walletAuth,
130
+ };
131
+ }
package/mcp-server.mjs ADDED
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ // wip-agent-pay MCP server
3
+ // Exposes agent_pay, agent_pay_x402, agent_pay_fund tools via MCP stdio transport.
4
+
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
8
+ import { pay, fund, mint } from './providers/index.js';
9
+
10
+ const server = new Server(
11
+ { name: 'wip-agent-pay', version: '2.0.0' },
12
+ { capabilities: { tools: {} } }
13
+ );
14
+
15
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
16
+ tools: [
17
+ {
18
+ name: 'agent_pay',
19
+ description: 'Authorize a micropayment from the agent wallet. Returns a one-time self-destructing URL.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {
23
+ amount: { type: 'number', description: 'Payment amount in USD' },
24
+ service: { type: 'string', description: 'Service identifier (e.g. "morning-stew")' },
25
+ note: { type: 'string', description: 'Optional note (e.g. "MS-#8")' },
26
+ },
27
+ required: ['amount', 'service'],
28
+ },
29
+ },
30
+ {
31
+ name: 'agent_pay_x402',
32
+ description: 'Pay for a paywalled URL. The agent wallet handles payment automatically. Returns the content.',
33
+ inputSchema: {
34
+ type: 'object',
35
+ properties: {
36
+ url: { type: 'string', description: 'The paywalled URL to pay for' },
37
+ wallet: { type: 'string', enum: ['cdp', 'privy'], description: 'Which wallet to use (default: cdp)' },
38
+ },
39
+ required: ['url'],
40
+ },
41
+ },
42
+ {
43
+ name: 'agent_pay_fund',
44
+ description: 'Fund the agent wallet. Returns a checkout URL the user opens to pay via Apple Pay.',
45
+ inputSchema: {
46
+ type: 'object',
47
+ properties: {
48
+ amount: { type: 'number', description: 'USD amount to fund' },
49
+ wallet: { type: 'string', enum: ['cdp', 'privy'], description: 'Which wallet to fund (default: cdp)' },
50
+ },
51
+ required: ['amount'],
52
+ },
53
+ },
54
+ ],
55
+ }));
56
+
57
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
58
+ const { name, arguments: args } = request.params;
59
+
60
+ try {
61
+ if (name === 'agent_pay') {
62
+ if (typeof args.amount !== 'number' || args.amount <= 0) {
63
+ return { content: [{ type: 'text', text: 'amount must be a positive number' }], isError: true };
64
+ }
65
+ if (typeof args.service !== 'string' || args.service.length === 0) {
66
+ return { content: [{ type: 'text', text: 'service is required' }], isError: true };
67
+ }
68
+ const result = await mint(args.amount, args.service, args.note || '');
69
+ if (result.success) {
70
+ const prefix = result.demo ? '[DEMO] ' : '';
71
+ return { content: [{ type: 'text', text: `${prefix}Payment authorized. One-time URL: ${result.url}` }] };
72
+ }
73
+ return { content: [{ type: 'text', text: `Payment failed: ${result.error || 'unknown error'}` }], isError: true };
74
+ }
75
+
76
+ if (name === 'agent_pay_x402') {
77
+ if (!args.url) {
78
+ return { content: [{ type: 'text', text: 'url is required' }], isError: true };
79
+ }
80
+ const result = await pay(args.url, { wallet: args.wallet || 'cdp' });
81
+ if (result.success) {
82
+ return { content: [{ type: 'text', text: `Paid ${result.amount} USDC on ${result.network || 'base'}. Service: ${result.service}.` }] };
83
+ }
84
+ return { content: [{ type: 'text', text: `Payment failed: ${result.error || 'unknown error'}` }], isError: true };
85
+ }
86
+
87
+ if (name === 'agent_pay_fund') {
88
+ if (typeof args.amount !== 'number' || args.amount <= 0) {
89
+ return { content: [{ type: 'text', text: 'amount must be a positive number' }], isError: true };
90
+ }
91
+ const result = await fund(args.amount, { wallet: args.wallet || 'cdp' });
92
+ if (result.success) {
93
+ return { content: [{ type: 'text', text: `Checkout ready. Have the user open: ${result.checkoutUrl}` }] };
94
+ }
95
+ return { content: [{ type: 'text', text: `Funding failed: ${result.error || 'unknown error'}` }], isError: true };
96
+ }
97
+
98
+ return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
99
+ } catch (err) {
100
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
101
+ }
102
+ });
103
+
104
+ const transport = new StdioServerTransport();
105
+ await server.connect(transport);
package/openclaw.mjs ADDED
@@ -0,0 +1,88 @@
1
+ // wip-agent-pay OpenClaw plugin
2
+ // Registers agent_pay, agent_pay_x402, agent_pay_fund as tools in the agent context.
3
+
4
+ import { pay, fund, mint } from './providers/index.js';
5
+
6
+ export default {
7
+ register(api) {
8
+ // --- Mint one-time URL (Mode B) ---
9
+ api.registerTool({
10
+ name: 'agent_pay',
11
+ label: 'Authorize Agent Payment',
12
+ description: 'Authorize a micropayment from the agent wallet. Returns a one-time self-destructing URL.',
13
+ parameters: {
14
+ type: 'object',
15
+ properties: {
16
+ amount: { type: 'number', description: 'Payment amount in USD' },
17
+ service: { type: 'string', description: 'Service identifier (e.g. "morning-stew")' },
18
+ note: { type: 'string', description: 'Optional note (e.g. "MS-#8")' },
19
+ },
20
+ required: ['amount', 'service'],
21
+ },
22
+ async execute(_id, params) {
23
+ try {
24
+ const result = await mint(params.amount, params.service, params.note || '');
25
+ if (result.success) {
26
+ const prefix = result.demo ? '[DEMO] ' : '';
27
+ return { content: [{ type: 'text', text: `${prefix}Payment authorized. One-time URL: ${result.url}` }] };
28
+ }
29
+ return { content: [{ type: 'text', text: `Payment failed: ${result.error || 'unknown error'}` }], isError: true };
30
+ } catch (err) {
31
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
32
+ }
33
+ },
34
+ }, { optional: true });
35
+
36
+ // --- Pay paywalled URL (x402) ---
37
+ api.registerTool({
38
+ name: 'agent_pay_x402',
39
+ label: 'Pay Paywalled URL',
40
+ description: 'Pay for a paywalled URL. The agent wallet handles payment automatically. Returns the content.',
41
+ parameters: {
42
+ type: 'object',
43
+ properties: {
44
+ url: { type: 'string', description: 'The paywalled URL to pay for' },
45
+ wallet: { type: 'string', enum: ['cdp', 'privy'], description: 'Which wallet to use (default: cdp)' },
46
+ },
47
+ required: ['url'],
48
+ },
49
+ async execute(_id, params) {
50
+ try {
51
+ const result = await pay(params.url, { wallet: params.wallet || 'cdp' });
52
+ if (result.success) {
53
+ return { content: [{ type: 'text', text: `Paid ${result.amount} USDC on ${result.network || 'base'}. Service: ${result.service}.` }] };
54
+ }
55
+ return { content: [{ type: 'text', text: `Payment failed: ${result.error || 'unknown error'}` }], isError: true };
56
+ } catch (err) {
57
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
58
+ }
59
+ },
60
+ }, { optional: true });
61
+
62
+ // --- Fund wallet (Apple Pay) ---
63
+ api.registerTool({
64
+ name: 'agent_pay_fund',
65
+ label: 'Fund Agent Wallet',
66
+ description: 'Fund the agent wallet. Returns a checkout URL the user opens to pay via Apple Pay.',
67
+ parameters: {
68
+ type: 'object',
69
+ properties: {
70
+ amount: { type: 'number', description: 'USD amount to fund' },
71
+ wallet: { type: 'string', enum: ['cdp', 'privy'], description: 'Which wallet to fund (default: cdp)' },
72
+ },
73
+ required: ['amount'],
74
+ },
75
+ async execute(_id, params) {
76
+ try {
77
+ const result = await fund(params.amount, { wallet: params.wallet || 'cdp' });
78
+ if (result.success) {
79
+ return { content: [{ type: 'text', text: `Checkout ready. Have the user open: ${result.checkoutUrl}` }] };
80
+ }
81
+ return { content: [{ type: 'text', text: `Funding failed: ${result.error || 'unknown error'}` }], isError: true };
82
+ } catch (err) {
83
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
84
+ }
85
+ },
86
+ }, { optional: true });
87
+ }
88
+ };
@@ -0,0 +1,17 @@
1
+ {
2
+ "id": "wip-agent-pay",
3
+ "name": "Agent Pay",
4
+ "description": "Disposable wallet for agents. x402 + Coinbase isolated portfolio.",
5
+ "activation": {
6
+ "onStartup": true
7
+ },
8
+ "contracts": {
9
+ "tools": ["agent_pay", "agent_pay_x402", "agent_pay_fund"]
10
+ },
11
+ "skills": ["./skills"],
12
+ "configSchema": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "properties": {}
16
+ }
17
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "wip-agent-pay",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "description": "Give your agent a way to pay. AI CASH (Apple/Google Pay) + AGENT WALLET (x402).",
6
+ "main": "providers/index.js",
7
+ "bin": {
8
+ "wip-pay": "./cli.js",
9
+ "wip-agent-pay": "./cli.js"
10
+ },
11
+ "exports": {
12
+ ".": "./providers/index.js",
13
+ "./providers/*": "./providers/*",
14
+ "./mcp-server": "./mcp-server.mjs"
15
+ },
16
+ "openclaw": {
17
+ "extensions": [
18
+ "./openclaw.mjs"
19
+ ]
20
+ },
21
+ "scripts": {
22
+ "test": "node cli.js --help"
23
+ },
24
+ "keywords": [
25
+ "agent",
26
+ "payment",
27
+ "coinbase",
28
+ "micropayments",
29
+ "1password",
30
+ "universal-interface",
31
+ "cloudflare"
32
+ ],
33
+ "author": "Parker Todd Brooks",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/wipcomputer/wip-agent-pay.git"
38
+ },
39
+ "homepage": "https://github.com/wipcomputer/wip-agent-pay",
40
+ "dependencies": {
41
+ "@modelcontextprotocol/sdk": "^1.12.1"
42
+ }
43
+ }