@newblock/iautopay-mcp 0.0.6 → 0.0.7

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.
@@ -8,8 +8,6 @@ Configuration is automatically generated at installation via `opencode.json`.
8
8
 
9
9
  Available commands:
10
10
  - `/autopay_guide` - Show iAutoPay usage guide
11
- - `/autopay_toA` - Quick payment of 0.01 USDC
12
- - `/autopay_toB` - Quick payment of 0.05 USDC (with confirmation)
13
11
  - `/autopay_buy_apikey_1day` - Buy 1-day API Key (0.09 USDC)
14
12
  - `/autopay_buy_apikey_7days` - Buy 7-day API Key (0.49 USDC)
15
13
  - `/autopay_get_info` - Get iAutoPay server information
@@ -68,9 +66,7 @@ When using Claude CLI with MCP enabled, the following tools are available:
68
66
 
69
67
  - `guide` - Display complete iAutoPay usage guide
70
68
  - `info` - Get server information (stock, prices, network config)
71
- - `buy_apikey` - Buy API key (supports 1/7 day durations)
72
- - `pay_stablecoin` - Pay stablecoin to specified address
73
- - `sync_opencode_config` - Auto-configure opencode.json shortcuts
69
+ - `buy_apikey` - Buy API key (supports 1/7/30 day durations)
74
70
  - `refresh_pricing` - Refresh prices from server
75
71
 
76
72
  ## Usage Examples
package/README.md CHANGED
@@ -120,16 +120,8 @@ Add these shortcuts to your `opencode.json` for faster access:
120
120
  }
121
121
  }
122
122
  },
123
- "command": {
124
- "autopay_toA": {
125
- "template": "Use pay_stablecoin tool to pay 0.01 USDC to 0x1a85156c2943b63febeee7883bd84a7d1cf0da0c, params: to=\"0x1a85156c2943b63febeee7883bd84a7d1cf0da0c\", amount=\"10000\"",
126
- "description": "Pay 0.01 USDC to account A"
127
- },
128
- "autopay_toB": {
129
- "template": "First use question tool to ask user confirmation with options: 1) Confirm (continue payment), 2) Cancel (do not pay). Show payment details: Pay 0.05 USDC to 0x1a85156c2943b63febeee7883bd84a7d1cf0da0c, params: to=\"0x1a85156c2943b63febeee7883bd84a7d1cf0da0c\", amount=\"50000\". Only proceed if user confirms.",
130
- "description": "Pay 0.05 USDC to account A (requires confirmation)"
131
- },
132
- "autopay_buy_apikey_1day": {
123
+ "command": {
124
+ "autopay_buy_apikey_1day": {
133
125
  "template": "Use buy_apikey tool to buy 1-day API Key, params: {\"duration\": 1}",
134
126
  "description": "Buy 1-day API Key (0.09 USDC)"
135
127
  },
@@ -149,8 +141,6 @@ Add these shortcuts to your `opencode.json` for faster access:
149
141
  }
150
142
  ```
151
143
 
152
- Run `sync_opencode_config` tool to auto-add these commands to your config.
153
-
154
144
  ### Opencode Quick Commands Usage Examples
155
145
 
156
146
  ### 1: /autopay_guide
@@ -161,13 +151,9 @@ iAutoPay Usage Guide
161
151
  Available Tools
162
152
  - guide - Display complete usage guide
163
153
  - info - Get server information (stock, prices, network config)
164
- - buy_apikey - Buy API key (supports 1/7 day durations)
165
- - pay_stablecoin - Pay stablecoin to specified address
166
- - sync_opencode_config - Auto-configure opencode.json shortcuts
154
+ - buy_apikey - Buy API key (supports 1/7/30 day durations)
167
155
  - refresh_pricing - Refresh prices from server
168
156
  Quick Commands
169
- - autopay_toA - Quick pay 0.01 USDC
170
- - autopay_toB - Pay 0.1 USDC (requires confirmation)
171
157
  - autopay_buy_apikey_1day - Buy 1-day API Key (0.1 USDC)
172
158
  - autopay_buy_apikey_7days - Buy 7-day API Key (0.9 USDC)
173
159
  - autopay_get_info - Quick get server information
@@ -180,31 +166,7 @@ Pricing
180
166
  - 7 days: 0.9 USDC
181
167
  Environment: dev
182
168
 
183
- ### 2: /autopay_toA
184
-
185
- Output:
186
-
187
- Paying 0.01 USDC to 0x1a85156c2943b63febeee7883bd84a7d1cf0da0c...
188
- Transaction hash: 0xabc123...
189
- Payment successful!
190
-
191
- ### 3: /autopay_toB
192
-
193
- Output:
194
-
195
- Please confirm payment:
196
- - Amount: 0.05 USDC
197
- - Recipient: 0x1a85156c2943b63febeee7883bd84a7d1cf0da0c
198
- - Network: Base Sepolia (84532)
199
-
200
- Select: 1) Confirm 2) Cancel
201
- [User selects confirm]
202
-
203
- Paying 0.05 USDC to 0x1a85156c2943b63febeee7883bd84a7d1cf0da0c...
204
- Transaction hash: 0xdef456...
205
- Payment successful!
206
-
207
- ### 4: /autopay_buy_apikey_1day
169
+ ### 2: /autopay_buy_apikey_1day
208
170
 
209
171
  Output:
210
172
 
@@ -451,13 +451,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
451
451
  { name: "guide", description: "显示完整使用指南" },
452
452
  { name: "info", description: "获取服务器信息(库存、价格、网络配置)" },
453
453
  { name: "buy_apikey", description: "购买 API key(支持1/7/30天时长)" },
454
- { name: "pay_stablecoin", description: "支付稳定币到指定地址" },
455
- { name: "sync_opencode_config", description: "自动配置 opencode.json 快捷命令" },
456
454
  { name: "refresh_pricing", description: "从服务器刷新价格" }
457
455
  ],
458
456
  commands: [
459
- { name: "autopay_toA", description: "快速支付 0.01 USDC" },
460
- { name: "autopay_toB", description: "支付 0.05 USDC(需确认)" },
461
457
  { name: "autopay_buy_apikey_1day", description: `购买1天API Key(${pricing["1day"]})` },
462
458
  { name: "autopay_buy_apikey_7days", description: `购买7天API Key(${pricing["7days"]})` },
463
459
  { name: "autopay_buy_apikey_30days", description: `购买30天API Key(${pricing["30days"]})` },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newblock/iautopay-mcp",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "iAutoPay is an MCP service that enables AI agents to automatically pay for purchases. It currently runs on Base chain (operated by Coinbase) and supports USDC payments.",
5
5
  "type": "module",
6
6
  "main": "dist/iautopay-mcp.js",
package/dist/server.js DELETED
@@ -1,585 +0,0 @@
1
- import crypto from "node:crypto";
2
- import { fileURLToPath } from "node:url";
3
- import { dirname } from "node:path";
4
- const __filename = fileURLToPath(import.meta.url);
5
- const __dirname = dirname(__filename);
6
- import { Server, } from "@modelcontextprotocol/sdk/server/index.js";
7
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
- import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
9
- import { z } from "zod";
10
- import { zodToJsonSchema } from "zod-to-json-schema";
11
- import { createPublicClient, createWalletClient, hexToSignature, http, isAddress, } from "viem";
12
- import { privateKeyToAccount } from "viem/accounts";
13
- // ============================================================================
14
- // Hardcoded Configuration (no .env dependency)
15
- // ============================================================================
16
- const CUR_ENV = 'dev';
17
- const CONFIG = {
18
- // User's private key for signing payments (provided via opencode.json environment)
19
- BUYER_PRIVATE_KEY: process.env.BUYER_PRIVATE_KEY,
20
- // Remote Fact API server
21
- FACT_API_URL: CUR_ENV == 'dev' ? "https://apipaymcp.okart.fun" : "https://apipaymcp.okart.fun",
22
- // Blockchain configuration (Base Sepolia for dev, Base Mainnet for prod)
23
- RPC_URL: CUR_ENV == 'dev' ? "https://sepolia.base.org" : "https://mainnet.base.org",
24
- CHAIN_ID: CUR_ENV == 'dev' ? "84532" : "8453",
25
- // Payment token details
26
- PAYMENT_TOKEN_NAME: "USDC",
27
- };
28
- // Validate required configuration
29
- if (!CONFIG.BUYER_PRIVATE_KEY) {
30
- throw new Error("BUYER_PRIVATE_KEY must be provided via opencode.json environment");
31
- }
32
- // Validate private key format
33
- try {
34
- privateKeyToAccount(CONFIG.BUYER_PRIVATE_KEY);
35
- }
36
- catch (error) {
37
- throw new Error(`Invalid BUYER_PRIVATE_KEY format: ${error}`);
38
- }
39
- // Derive values from config
40
- const BUYER_CHAIN_ID = CONFIG.CHAIN_ID;
41
- const BUYER_RPC_URL = CONFIG.RPC_URL;
42
- const FACT_API_URL = CONFIG.FACT_API_URL;
43
- // USDC addresses
44
- const USDC_ADDRESSES = {
45
- dev: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
46
- prod: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
47
- };
48
- const CURRENT_USDC = USDC_ADDRESSES[CUR_ENV];
49
- const paymentRequirementsSchema = z.object({
50
- scheme: z.string().optional(),
51
- network: z.string().min(1),
52
- asset: z.string().min(1),
53
- price: z.string().min(1),
54
- payee: z.string().optional(),
55
- });
56
- const payStablecoinInput = z.object({
57
- to: z.string().min(1),
58
- amount: z.string().min(1),
59
- });
60
- const buyApikeyInput = z.object({
61
- duration: z.number().optional().refine(val => !val || [1, 7, 30].includes(val), {
62
- message: "Duration must be 1, 7, or 30 days"
63
- })
64
- });
65
- const getInfoInput = z.object({});
66
- const guideInput = z.object({});
67
- const syncOpencodeConfigInput = z.object({});
68
- const refreshPricingInput = z.object({});
69
- // ============================================================================
70
- // Dynamic Pricing from Fact API
71
- // ============================================================================
72
- let CACHED_PRICING = null;
73
- async function fetchPricingFromFactAPI() {
74
- try {
75
- const res = await fetch(`${FACT_API_URL}/info`);
76
- if (res.ok) {
77
- const data = await res.json();
78
- CACHED_PRICING = {
79
- "1day": `${data.prices?.["1dayUsdc"] || 0.09} USDC`,
80
- "7days": `${data.prices?.["7daysUsdc"] || 0.49} USDC`,
81
- "30days": `${data.prices?.["30daysUsdc"] || 0.99} USDC`
82
- };
83
- return CACHED_PRICING;
84
- }
85
- }
86
- catch (error) {
87
- }
88
- CACHED_PRICING = {
89
- "1day": "0.09 USDC",
90
- "7days": "0.49 USDC",
91
- "30days": "0.99 USDC"
92
- };
93
- return CACHED_PRICING;
94
- }
95
- const buyerAccount = privateKeyToAccount(CONFIG.BUYER_PRIVATE_KEY);
96
- const publicClient = createPublicClient({
97
- transport: http(BUYER_RPC_URL),
98
- });
99
- const walletClient = createWalletClient({
100
- account: buyerAccount,
101
- transport: http(BUYER_RPC_URL),
102
- });
103
- // ============================================================================
104
- // Retry Configuration
105
- // ============================================================================
106
- const RETRY_CONFIG = {
107
- MAX_RETRIES: 3,
108
- BASE_DELAY: 1000,
109
- };
110
- async function fetchWithRetry(url, options, context = 'API Request') {
111
- for (let i = 0; i < RETRY_CONFIG.MAX_RETRIES; i++) {
112
- try {
113
- const response = await fetch(url, options);
114
- if (response.ok || response.status < 500) {
115
- return response;
116
- }
117
- }
118
- catch (error) {
119
- }
120
- if (i < RETRY_CONFIG.MAX_RETRIES - 1) {
121
- const delay = RETRY_CONFIG.BASE_DELAY * Math.pow(2, i);
122
- await new Promise(resolve => setTimeout(resolve, delay));
123
- }
124
- }
125
- throw new Error(`${context} failed after ${RETRY_CONFIG.MAX_RETRIES} retries`);
126
- }
127
- // ============================================================================
128
- // Token ABI
129
- // ============================================================================
130
- const tokenAbi = [
131
- {
132
- type: "function",
133
- name: "name",
134
- stateMutability: "view",
135
- inputs: [],
136
- outputs: [{ name: "", type: "string" }],
137
- },
138
- {
139
- type: "function",
140
- name: "balanceOf",
141
- stateMutability: "view",
142
- inputs: [{ name: "account", type: "address" }],
143
- outputs: [{ name: "", type: "uint256" }],
144
- },
145
- {
146
- type: "function",
147
- name: "decimals",
148
- stateMutability: "view",
149
- inputs: [],
150
- outputs: [{ name: "", type: "uint8" }],
151
- },
152
- {
153
- type: "function",
154
- name: "transferWithAuthorization",
155
- stateMutability: "nonpayable",
156
- inputs: [
157
- { name: "from", type: "address" },
158
- { name: "to", type: "address" },
159
- { name: "value", type: "uint256" },
160
- { name: "validAfter", type: "uint256" },
161
- { name: "validBefore", type: "uint256" },
162
- { name: "nonce", type: "bytes32" },
163
- { name: "v", type: "uint8" },
164
- { name: "r", type: "bytes32" },
165
- { name: "s", type: "bytes32" },
166
- ],
167
- outputs: [{ name: "", type: "bool" }],
168
- },
169
- ];
170
- const transferWithAuthorizationTypes = {
171
- TransferWithAuthorization: [
172
- { name: "from", type: "address" },
173
- { name: "to", type: "address" },
174
- { name: "value", type: "uint256" },
175
- { name: "validAfter", type: "uint256" },
176
- { name: "validBefore", type: "uint256" },
177
- { name: "nonce", type: "bytes32" },
178
- ],
179
- };
180
- function parseChainId(network) {
181
- const parts = network.split(":");
182
- if (parts.length === 2 && parts[0] === "eip155") {
183
- const value = Number(parts[1]);
184
- if (!Number.isNaN(value))
185
- return value;
186
- }
187
- return Number(BUYER_CHAIN_ID);
188
- }
189
- async function buildPaymentSignature(requirements) {
190
- const chainId = parseChainId(requirements.network);
191
- const verifyingContract = requirements.asset;
192
- if (!isAddress(verifyingContract)) {
193
- throw new Error("Invalid payment asset address");
194
- }
195
- let tokenName = await publicClient
196
- .readContract({
197
- address: verifyingContract,
198
- abi: tokenAbi,
199
- functionName: "name",
200
- })
201
- .catch(() => CONFIG.PAYMENT_TOKEN_NAME);
202
- let version = "1";
203
- // Special handling for Base Sepolia USDC and Base Mainnet USDC
204
- const sepoliaUSDC = "0x036cbd53842c5426634e7929541ec2318f3dcf7e";
205
- const mainnetUSDC = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
206
- if (requirements.network === "eip155:84532" &&
207
- requirements.asset.toLowerCase() === sepoliaUSDC.toLowerCase()) {
208
- tokenName = "USDC";
209
- version = "2";
210
- }
211
- else if (requirements.network === "eip155:8453" &&
212
- requirements.asset.toLowerCase() === mainnetUSDC.toLowerCase()) {
213
- tokenName = "USD Coin";
214
- version = "2";
215
- }
216
- const nonce = `0x${crypto.randomBytes(32).toString("hex")}`;
217
- const now = Math.floor(Date.now() / 1000);
218
- const validAfter = BigInt(0);
219
- const validBefore = BigInt(now + 28800);
220
- const payee = requirements.payee ?? buyerAccount.address;
221
- const signature = await walletClient.signTypedData({
222
- domain: {
223
- name: tokenName,
224
- version,
225
- chainId,
226
- verifyingContract,
227
- },
228
- types: transferWithAuthorizationTypes,
229
- primaryType: "TransferWithAuthorization",
230
- message: {
231
- from: buyerAccount.address,
232
- to: payee,
233
- value: BigInt(requirements.price),
234
- validAfter,
235
- validBefore,
236
- nonce,
237
- },
238
- });
239
- const { v, r, s } = hexToSignature(signature);
240
- return {
241
- from: buyerAccount.address,
242
- to: payee,
243
- value: requirements.price,
244
- validAfter: validAfter.toString(),
245
- validBefore: validBefore.toString(),
246
- nonce,
247
- v: Number(v),
248
- r,
249
- s,
250
- };
251
- }
252
- async function payStablecoin(params) {
253
- const [balance, decimals] = await Promise.all([
254
- publicClient.readContract({
255
- address: params.asset,
256
- abi: tokenAbi,
257
- functionName: 'balanceOf',
258
- args: [buyerAccount.address]
259
- }),
260
- publicClient.readContract({
261
- address: params.asset,
262
- abi: tokenAbi,
263
- functionName: 'decimals'
264
- })
265
- ]);
266
- const balanceNumber = Number(balance) / (10 ** Number(decimals));
267
- const amountNumber = Number(params.amount) / 1e6;
268
- if (balanceNumber < amountNumber) {
269
- throw new Error(`Insufficient balance: required ${amountNumber.toFixed(6)} USDC, available ${balanceNumber.toFixed(6)} USDC`);
270
- }
271
- const requirements = {
272
- scheme: "exact",
273
- network: params.isTestnet ? "eip155:84532" : `eip155:${BUYER_CHAIN_ID}`,
274
- asset: params.asset,
275
- price: params.amount,
276
- payee: params.to,
277
- };
278
- const signaturePayload = await buildPaymentSignature(requirements);
279
- const transferRes = await fetchWithRetry(`${FACT_API_URL}/v1/transfer`, {
280
- method: "POST",
281
- headers: {
282
- "content-type": "application/json",
283
- "PAYMENT-SIGNATURE": JSON.stringify(signaturePayload),
284
- },
285
- body: JSON.stringify({
286
- to: params.to,
287
- amount: params.amount,
288
- asset: params.asset,
289
- }),
290
- }, 'PAY_STABLECOIN Transfer');
291
- if (!transferRes.ok) {
292
- const error = await transferRes.text();
293
- const networkInfo = params.isTestnet ? "Testnet (Base Sepolia 84532)" : `Mainnet (Base ${BUYER_CHAIN_ID === '8453' ? 'Mainnet' : BUYER_CHAIN_ID})`;
294
- throw new Error(`Transfer failed (${networkInfo}): ${error}`);
295
- }
296
- const result = await transferRes.json();
297
- const deductedAmount = amountNumber.toFixed(6);
298
- const currentBalance = (balanceNumber - amountNumber).toFixed(6);
299
- return {
300
- ...result,
301
- from: buyerAccount.address,
302
- to: params.to,
303
- amount: `${amountNumber.toFixed(6)} USDC`,
304
- asset: params.asset,
305
- network: params.isTestnet ? "Testnet (Base Sepolia)" : "Mainnet (Base)",
306
- deductedAmount: `${deductedAmount} USDC`,
307
- currentBalance: `${currentBalance} USDC`
308
- };
309
- }
310
- const server = new Server({ name: "autopay-server", version: "0.1.0" }, { capabilities: { tools: {} } });
311
- server.setRequestHandler(ListToolsRequestSchema, async () => {
312
- return {
313
- tools: [
314
- {
315
- name: "guide",
316
- description: "⭐ FIRST TIME? Run this guide to learn how to use iAutoPay tools and commands.",
317
- inputSchema: zodToJsonSchema(guideInput),
318
- },
319
- {
320
- name: "info",
321
- description: "Get iAutoPay server information (API key stock, price, network config). Tip: Check before buying API keys.",
322
- inputSchema: zodToJsonSchema(getInfoInput),
323
- },
324
- {
325
- name: "buy_apikey",
326
- description: "Purchase an API key with optional duration (1/7/30 days). Prices: 1 day=0.9 USDC, 7 days=4.9 USDC, 30 days=9.9 USDC. Run 'info' first to confirm stock.",
327
- inputSchema: zodToJsonSchema(buyApikeyInput),
328
- },
329
- {
330
- name: "pay_stablecoin",
331
- description: "Pay stablecoin to any address using EIP-3009. Amount is in smallest unit (e.g., 100000 = 0.1 USDC).",
332
- inputSchema: zodToJsonSchema(payStablecoinInput),
333
- },
334
- {
335
- name: "sync_opencode_config",
336
- description: "Auto-configure opencode.json with quick commands (autopay_toA, autopay_toB, etc.)",
337
- inputSchema: zodToJsonSchema(syncOpencodeConfigInput),
338
- },
339
- {
340
- name: "refresh_pricing",
341
- description: "Refresh pricing from API. Use this if prices are changed on the server.",
342
- inputSchema: zodToJsonSchema(refreshPricingInput),
343
- },
344
- ],
345
- };
346
- });
347
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
348
- const { name, arguments: args } = request.params;
349
- if (name === "pay_stablecoin") {
350
- const parsed = payStablecoinInput.parse(args);
351
- try {
352
- const result = await payStablecoin({
353
- to: parsed.to,
354
- amount: parsed.amount,
355
- asset: CURRENT_USDC,
356
- isTestnet: CUR_ENV === 'dev',
357
- });
358
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
359
- }
360
- catch (error) {
361
- throw error;
362
- }
363
- }
364
- if (name === "buy_apikey") {
365
- const parsed = buyApikeyInput.parse(args);
366
- const duration = parsed.duration || 1;
367
- const [balance, decimals] = await Promise.all([
368
- publicClient.readContract({
369
- address: CURRENT_USDC,
370
- abi: tokenAbi,
371
- functionName: 'balanceOf',
372
- args: [buyerAccount.address]
373
- }),
374
- publicClient.readContract({
375
- address: CURRENT_USDC,
376
- abi: tokenAbi,
377
- functionName: 'decimals'
378
- })
379
- ]);
380
- const balanceNumber = Number(balance) / (10 ** Number(decimals));
381
- const priceMap = {
382
- 1: CACHED_PRICING?.["1day"] || "0.09 USDC",
383
- 7: CACHED_PRICING?.["7days"] || "0.49 USDC",
384
- 30: CACHED_PRICING?.["30days"] || "0.99 USDC",
385
- };
386
- const priceString = priceMap[duration];
387
- const priceNumber = parseFloat(priceString);
388
- const priceWei = (priceNumber * 1e6).toString();
389
- if (balanceNumber < priceNumber) {
390
- throw new Error(`Insufficient balance: required ${priceNumber.toFixed(6)} USDC, available ${balanceNumber.toFixed(6)} USDC`);
391
- }
392
- const requirements = {
393
- scheme: "exact",
394
- network: CUR_ENV === 'dev' ? "eip155:84532" : `eip155:${BUYER_CHAIN_ID}`,
395
- asset: CURRENT_USDC,
396
- price: priceWei,
397
- payee: "0x1a85156c2943b63febeee7883bd84a7d1cf0da0c",
398
- };
399
- const signaturePayload = await buildPaymentSignature(requirements);
400
- const buyRes = await fetch(`${FACT_API_URL}/v1/buy-apikey`, {
401
- method: "POST",
402
- headers: {
403
- "content-type": "application/json",
404
- "PAYMENT-SIGNATURE": JSON.stringify(signaturePayload),
405
- },
406
- body: JSON.stringify({ duration })
407
- });
408
- if (!buyRes.ok) {
409
- const error = await buyRes.text();
410
- const networkInfo = CUR_ENV === 'dev' ? "Testnet (Base Sepolia 84532)" : `Mainnet (Base ${BUYER_CHAIN_ID})`;
411
- throw new Error(`Buy API key failed (${networkInfo}): ${error}`);
412
- }
413
- const result = await buyRes.json();
414
- return {
415
- content: [{
416
- type: "text",
417
- text: JSON.stringify({
418
- ...result,
419
- price: `${priceNumber.toFixed(6)} USDC`,
420
- deductedAmount: `${priceNumber.toFixed(6)} USDC`,
421
- currentBalance: `${(balanceNumber - priceNumber).toFixed(6)} USDC`
422
- })
423
- }]
424
- };
425
- }
426
- if (name === "info") {
427
- const parsed = getInfoInput.parse(args);
428
- try {
429
- const res = await fetch(`${FACT_API_URL}/info`);
430
- if (res.ok) {
431
- const data = await res.json();
432
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
433
- }
434
- else {
435
- throw new Error(`Failed to fetch info: ${res.statusText}`);
436
- }
437
- }
438
- catch (error) {
439
- throw error;
440
- }
441
- }
442
- if (name === "guide") {
443
- const parsed = guideInput.parse(args);
444
- const networkInfo = `${BUYER_CHAIN_ID === '84532' ? 'Base Sepolia' : 'Base Mainnet'} (${BUYER_CHAIN_ID})`;
445
- const pricing = CACHED_PRICING || {
446
- "1day": "0.09 USDC",
447
- "7days": "0.49 USDC",
448
- "30days": "0.99 USDC"
449
- };
450
- const toolsData = {
451
- tools: [
452
- { name: "guide", description: "显示完整使用指南" },
453
- { name: "info", description: "获取服务器信息(库存、价格、网络配置)" },
454
- { name: "buy_apikey", description: "购买 API key(支持1/7/30天时长)" },
455
- { name: "pay_stablecoin", description: "支付稳定币到指定地址" },
456
- { name: "sync_opencode_config", description: "自动配置 opencode.json 快捷命令" },
457
- { name: "refresh_pricing", description: "从服务器刷新价格" }
458
- ],
459
- commands: [
460
- { name: "autopay_toA", description: "快速支付 0.01 USDC" },
461
- { name: "autopay_toB", description: "支付 0.05 USDC(需确认)" },
462
- { name: "autopay_buy_apikey_1day", description: `购买1天API Key(${pricing["1day"]})` },
463
- { name: "autopay_buy_apikey_7days", description: `购买7天API Key(${pricing["7days"]})` },
464
- { name: "autopay_buy_apikey_30days", description: `购买30天API Key(${pricing["30days"]})` },
465
- { name: "autopay_get_info", description: "快速获取服务器信息" }
466
- ],
467
- network: {
468
- testnet: "Base Sepolia (84532)",
469
- mainnet: "Base Mainnet (8453)",
470
- current: networkInfo
471
- },
472
- pricing,
473
- environment: CUR_ENV
474
- };
475
- return { content: [{ type: "text", text: JSON.stringify(toolsData, null, 2) }] };
476
- }
477
- if (name === "sync_opencode_config") {
478
- const parsed = syncOpencodeConfigInput.parse(args);
479
- try {
480
- const fs = await import('fs/promises');
481
- const opencodePath = '/Users/michael/opc/proj/iautopay/opencode.json';
482
- const opencodeData = JSON.parse(await fs.readFile(opencodePath, 'utf-8'));
483
- const pricing = CACHED_PRICING || {
484
- "1day": "0.09 USDC",
485
- "7days": "0.49 USDC",
486
- "30days": "0.99 USDC"
487
- };
488
- const requiredCommands = {
489
- "autopay_toA": {
490
- "template": "使用 pay_stablecoin 工具向 0x1a85156c2943b63febeee7883bd84a7d1cf0da0c 支付 0.01 USDC,参数为:to=\"0x1a85156c2943b63febeee7883bd84a7d1cf0da0c\", amount=\"10000\"",
491
- "description": "支付0.01 USDC给A账户"
492
- },
493
- "autopay_toB": {
494
- "template": "首先使用 question 工具询问用户确认,选项包括:1) 确认(继续支付),2) 取消(不进行支付)。显示支付详情:向 0x1a85156c2943b63febeee7883bd84a7d1cf0da0c 支付 0.05 USDC,参数为:to=\"0x1a85156c2943b63febeee7883bd84a7d1cf0da0c\", amount=\"50000\"。只有用户选择确认时才继续支付。",
495
- "description": "支付0.05 USDC给A账户(需要确认)"
496
- },
497
- "autopay_buy_apikey_1day": {
498
- "template": "使用 buy_apikey 工具购买1天API Key,参数为:{\"duration\": 1}",
499
- "description": `购买1天API Key(${pricing["1day"]})`
500
- },
501
- "autopay_buy_apikey_7days": {
502
- "template": "使用 buy_apikey 工具购买7天API Key,参数为:{\"duration\": 7}",
503
- "description": `购买7天API Key(${pricing["7days"]})`
504
- },
505
- "autopay_buy_apikey_30days": {
506
- "template": "使用 buy_apikey 工具购买30天API Key,参数为:{\"duration\": 30}",
507
- "description": `购买30天API Key(${pricing["30days"]})`
508
- },
509
- "autopay_get_info": {
510
- "template": "使用 info 工具获取服务器信息(API Key 库存、价格、网络配置)",
511
- "description": "获取iAutoPay服务器信息"
512
- },
513
- "autopay_guide": {
514
- "template": "使用 guide 工具显示 iAutoPay 使用指南",
515
- "description": "显示iAutoPay使用指南"
516
- }
517
- };
518
- let addedCommands = [];
519
- let updatedCommands = [];
520
- if (!opencodeData.command) {
521
- opencodeData.command = {};
522
- }
523
- for (const [key, value] of Object.entries(requiredCommands)) {
524
- if (!opencodeData.command[key]) {
525
- opencodeData.command[key] = value;
526
- addedCommands.push(key);
527
- }
528
- }
529
- if (addedCommands.length > 0) {
530
- await fs.writeFile(opencodePath, JSON.stringify(opencodeData, null, 2), 'utf-8');
531
- return {
532
- content: [{
533
- type: "text",
534
- text: `✅ 已添加 ${addedCommands.length} 个命令到 opencode.json:\n${addedCommands.map(c => ` - ${c}`).join('\n')}`
535
- }]
536
- };
537
- }
538
- else {
539
- return {
540
- content: [{
541
- type: "text",
542
- text: "✅ 所有 autopay_ 命令已存在,无需更新"
543
- }]
544
- };
545
- }
546
- }
547
- catch (error) {
548
- throw new Error(`同步配置失败: ${error}`);
549
- }
550
- }
551
- if (name === "refresh_pricing") {
552
- const parsed = refreshPricingInput.parse(args);
553
- try {
554
- await fetchPricingFromFactAPI();
555
- return {
556
- content: [{
557
- type: "text",
558
- text: `✅ 价格已刷新:\n${JSON.stringify(CACHED_PRICING, null, 2)}`
559
- }]
560
- };
561
- }
562
- catch (error) {
563
- throw new Error(`刷新价格失败: ${error}`);
564
- }
565
- }
566
- throw new Error(`Unknown tool: ${name}`);
567
- });
568
- const transport = new StdioServerTransport();
569
- await fetchPricingFromFactAPI();
570
- try {
571
- const usdcBalance = await publicClient.readContract({
572
- address: CURRENT_USDC,
573
- abi: tokenAbi,
574
- functionName: 'balanceOf',
575
- args: [buyerAccount.address]
576
- });
577
- const decimals = await publicClient.readContract({
578
- address: CURRENT_USDC,
579
- abi: tokenAbi,
580
- functionName: 'decimals'
581
- });
582
- }
583
- catch (error) {
584
- }
585
- await server.connect(transport);