agentstore 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -15
- package/dist/index.js +319 -282
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,27 +4,19 @@ Open-source marketplace CLI for Claude Code plugins. Browse, install, publish, a
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
npm install -g agentstore
|
|
9
|
-
```
|
|
7
|
+
**Native plugin** (recommended — no npm needed):
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
npx agentstore browse
|
|
9
|
+
```
|
|
10
|
+
/plugin marketplace add techgangboss/agentstore
|
|
11
|
+
/plugin install code-reviewer@agentstore
|
|
15
12
|
```
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
**npm CLI** (adds wallet, payments, and publishing):
|
|
18
15
|
|
|
19
16
|
```bash
|
|
20
|
-
|
|
17
|
+
npm install -g agentstore
|
|
21
18
|
agentstore browse
|
|
22
|
-
|
|
23
|
-
# Install a free agent
|
|
24
|
-
agentstore install techgangboss.wallet-assistant
|
|
25
|
-
|
|
26
|
-
# Setup the MCP gateway (routes agent tools through Claude Code)
|
|
27
|
-
agentstore gateway-setup
|
|
19
|
+
agentstore install techgangboss.code-reviewer
|
|
28
20
|
```
|
|
29
21
|
|
|
30
22
|
## For Publishers
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as crypto from 'crypto';
|
|
|
7
7
|
import * as readline from 'readline';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import { generatePrivateKey, privateKeyToAccount, } from 'viem/accounts';
|
|
10
|
-
import { createPublicClient,
|
|
10
|
+
import { createPublicClient, http, parseAbi, formatUnits, parseUnits, } from 'viem';
|
|
11
11
|
import { mainnet } from 'viem/chains';
|
|
12
12
|
import * as keytar from 'keytar';
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -16,6 +16,30 @@ const API_BASE = 'https://api.agentstore.tools';
|
|
|
16
16
|
const MEV_COMMIT_RPC = 'https://fastrpc.mev-commit.xyz';
|
|
17
17
|
const KEYCHAIN_SERVICE = 'agentstore-wallet';
|
|
18
18
|
const KEYCHAIN_ACCOUNT = 'encryption-key';
|
|
19
|
+
// USDC contract on Ethereum mainnet
|
|
20
|
+
const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
21
|
+
const USDC_DECIMALS = 6;
|
|
22
|
+
const ERC20_BALANCE_ABI = parseAbi([
|
|
23
|
+
'function balanceOf(address owner) view returns (uint256)',
|
|
24
|
+
]);
|
|
25
|
+
// EIP-712 domain for USDC (EIP-3009 transferWithAuthorization)
|
|
26
|
+
const USDC_EIP712_DOMAIN = {
|
|
27
|
+
name: 'USD Coin',
|
|
28
|
+
version: '2',
|
|
29
|
+
chainId: 1,
|
|
30
|
+
verifyingContract: USDC_ADDRESS,
|
|
31
|
+
};
|
|
32
|
+
// EIP-712 types for TransferWithAuthorization
|
|
33
|
+
const TRANSFER_WITH_AUTHORIZATION_TYPES = {
|
|
34
|
+
TransferWithAuthorization: [
|
|
35
|
+
{ name: 'from', type: 'address' },
|
|
36
|
+
{ name: 'to', type: 'address' },
|
|
37
|
+
{ name: 'value', type: 'uint256' },
|
|
38
|
+
{ name: 'validAfter', type: 'uint256' },
|
|
39
|
+
{ name: 'validBefore', type: 'uint256' },
|
|
40
|
+
{ name: 'nonce', type: 'bytes32' },
|
|
41
|
+
],
|
|
42
|
+
};
|
|
19
43
|
// File paths
|
|
20
44
|
const HOME_DIR = os.homedir();
|
|
21
45
|
const AGENTSTORE_DIR = path.join(HOME_DIR, '.agentstore');
|
|
@@ -234,41 +258,50 @@ async function ensureWalletExists() {
|
|
|
234
258
|
fs.writeFileSync(TX_HISTORY_FILE, JSON.stringify([]), { mode: 0o600 });
|
|
235
259
|
return { address: account.address, created: true };
|
|
236
260
|
}
|
|
237
|
-
//
|
|
238
|
-
async function
|
|
261
|
+
// Get USDC balance for an address
|
|
262
|
+
async function getUsdcBalance(address) {
|
|
263
|
+
return publicClient.readContract({
|
|
264
|
+
address: USDC_ADDRESS,
|
|
265
|
+
abi: ERC20_BALANCE_ABI,
|
|
266
|
+
functionName: 'balanceOf',
|
|
267
|
+
args: [address],
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
// Format USDC from raw bigint (6 decimals) to human-readable string
|
|
271
|
+
function formatUsdc(raw) {
|
|
272
|
+
return formatUnits(raw, USDC_DECIMALS);
|
|
273
|
+
}
|
|
274
|
+
// Parse USDC from human-readable string to raw bigint
|
|
275
|
+
function parseUsdc(amount) {
|
|
276
|
+
const str = typeof amount === 'number' ? amount.toFixed(USDC_DECIMALS) : amount;
|
|
277
|
+
return parseUnits(str, USDC_DECIMALS);
|
|
278
|
+
}
|
|
279
|
+
// Trigger funding flow and wait for USDC funds
|
|
280
|
+
async function triggerFundingFlow(requiredUsdc) {
|
|
239
281
|
const config = loadWalletConfig();
|
|
240
282
|
if (!config)
|
|
241
283
|
return false;
|
|
242
|
-
console.log('\n💳
|
|
284
|
+
console.log('\n💳 Fund your wallet with USDC\n');
|
|
243
285
|
console.log(` Wallet: ${config.address}`);
|
|
244
|
-
console.log(` Required:
|
|
245
|
-
// Get initial balance
|
|
246
|
-
const initialBalance = await
|
|
247
|
-
|
|
248
|
-
});
|
|
249
|
-
// Get onramp URL from API
|
|
286
|
+
console.log(` Required: $${requiredUsdc.toFixed(2)} USDC\n`);
|
|
287
|
+
// Get initial USDC balance
|
|
288
|
+
const initialBalance = await getUsdcBalance(config.address);
|
|
289
|
+
// Try to get onramp URL from API
|
|
250
290
|
const response = await fetch(`${API_BASE}/api/onramp/session`, {
|
|
251
291
|
method: 'POST',
|
|
252
292
|
headers: { 'Content-Type': 'application/json' },
|
|
253
293
|
body: JSON.stringify({
|
|
254
294
|
wallet_address: config.address,
|
|
255
|
-
amount_usd: Math.ceil(
|
|
295
|
+
amount_usd: Math.ceil(requiredUsdc),
|
|
296
|
+
asset: 'USDC',
|
|
256
297
|
}),
|
|
257
298
|
});
|
|
258
299
|
const result = await response.json();
|
|
259
300
|
if (!response.ok || !result.success) {
|
|
260
|
-
|
|
261
|
-
console.log('⚠️ Coinbase Onramp not configured.\n');
|
|
262
|
-
console.log(' Manual funding instructions:');
|
|
263
|
-
console.log(` 1. ${result.manual_instructions.step1}`);
|
|
264
|
-
console.log(` 2. ${result.manual_instructions.step2}`);
|
|
265
|
-
console.log(` 3. ${result.manual_instructions.step3}`);
|
|
266
|
-
console.log(`\n Your wallet address: ${config.address}`);
|
|
267
|
-
console.log('\n After funding, run the install command again.');
|
|
268
|
-
}
|
|
301
|
+
showFundingOptions(config.address, requiredUsdc);
|
|
269
302
|
return false;
|
|
270
303
|
}
|
|
271
|
-
// Open browser
|
|
304
|
+
// Open browser for Coinbase onramp
|
|
272
305
|
const { exec } = await import('child_process');
|
|
273
306
|
const openCmd = process.platform === 'darwin'
|
|
274
307
|
? `open "${result.onramp_url}"`
|
|
@@ -277,47 +310,61 @@ async function triggerFundingFlow(requiredUsd) {
|
|
|
277
310
|
: `xdg-open "${result.onramp_url}"`;
|
|
278
311
|
exec(openCmd);
|
|
279
312
|
console.log('🌐 Coinbase opened in your browser.\n');
|
|
280
|
-
console.log(' Complete the purchase, then wait for funds to arrive.');
|
|
281
|
-
console.log('
|
|
282
|
-
|
|
313
|
+
console.log(' Complete the USDC purchase, then wait for funds to arrive.');
|
|
314
|
+
console.log(' Or use one of the other options below while waiting.\n');
|
|
315
|
+
showFundingOptionsShort(config.address);
|
|
316
|
+
console.log('\n⏳ Waiting for USDC (Ctrl+C to cancel)...\n');
|
|
317
|
+
// Poll for USDC balance
|
|
318
|
+
const requiredRaw = parseUsdc(requiredUsdc);
|
|
283
319
|
const startTime = Date.now();
|
|
284
320
|
const maxWaitTime = 10 * 60 * 1000; // 10 minutes
|
|
285
321
|
const pollInterval = 10 * 1000; // 10 seconds
|
|
286
322
|
while (Date.now() - startTime < maxWaitTime) {
|
|
287
323
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
288
324
|
try {
|
|
289
|
-
const currentBalance = await
|
|
290
|
-
|
|
291
|
-
});
|
|
292
|
-
if (currentBalance > initialBalance) {
|
|
325
|
+
const currentBalance = await getUsdcBalance(config.address);
|
|
326
|
+
if (currentBalance >= requiredRaw && currentBalance > initialBalance) {
|
|
293
327
|
const added = currentBalance - initialBalance;
|
|
294
|
-
console.log(`\n✅
|
|
328
|
+
console.log(`\n✅ USDC received! +$${formatUsdc(added)} USDC\n`);
|
|
295
329
|
return true;
|
|
296
330
|
}
|
|
297
331
|
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
298
|
-
process.stdout.write(`\r Checking balance... (${elapsed}s elapsed)`);
|
|
332
|
+
process.stdout.write(`\r Checking USDC balance... (${elapsed}s elapsed)`);
|
|
299
333
|
}
|
|
300
334
|
catch {
|
|
301
335
|
// Ignore poll errors
|
|
302
336
|
}
|
|
303
337
|
}
|
|
304
|
-
console.log('\n⚠️ Timeout waiting for
|
|
338
|
+
console.log('\n⚠️ Timeout waiting for USDC. Run install again after funding.');
|
|
305
339
|
return false;
|
|
306
340
|
}
|
|
307
|
-
//
|
|
341
|
+
// Show all funding options
|
|
342
|
+
function showFundingOptions(address, requiredUsdc) {
|
|
343
|
+
console.log(' Three ways to fund your wallet:\n');
|
|
344
|
+
console.log(' 1. Buy with card (Coinbase):');
|
|
345
|
+
console.log(' agentstore wallet fund --wait\n');
|
|
346
|
+
console.log(' 2. Send USDC directly (from any wallet/exchange):');
|
|
347
|
+
console.log(` Send $${requiredUsdc.toFixed(2)} USDC (Ethereum) to:`);
|
|
348
|
+
console.log(` ${address}\n`);
|
|
349
|
+
console.log(' 3. Import an existing wallet with USDC:');
|
|
350
|
+
console.log(' agentstore wallet import\n');
|
|
351
|
+
console.log(' After funding, run the install command again.');
|
|
352
|
+
}
|
|
353
|
+
// Short version for inline display
|
|
354
|
+
function showFundingOptionsShort(address) {
|
|
355
|
+
console.log(' Other ways to add USDC:');
|
|
356
|
+
console.log(` - Send USDC (Ethereum) to: ${address}`);
|
|
357
|
+
console.log(' - Import existing wallet: agentstore wallet import');
|
|
358
|
+
}
|
|
359
|
+
// Get wallet balance (USDC)
|
|
308
360
|
async function getWalletBalance() {
|
|
309
361
|
const config = loadWalletConfig();
|
|
310
362
|
if (!config)
|
|
311
363
|
throw new Error('Wallet not initialized');
|
|
312
|
-
const
|
|
313
|
-
address: config.address,
|
|
314
|
-
});
|
|
315
|
-
const ethBalance = formatEther(balanceWei);
|
|
316
|
-
const ethPrice = await getEthPrice();
|
|
317
|
-
const usdBalance = parseFloat(ethBalance) * ethPrice;
|
|
364
|
+
const raw = await getUsdcBalance(config.address);
|
|
318
365
|
return {
|
|
319
|
-
|
|
320
|
-
|
|
366
|
+
usdc: formatUsdc(raw),
|
|
367
|
+
usdcRaw: raw,
|
|
321
368
|
};
|
|
322
369
|
}
|
|
323
370
|
// Check spend limits
|
|
@@ -353,100 +400,6 @@ function checkSpendLimit(amountUsd, config, txHistory) {
|
|
|
353
400
|
}
|
|
354
401
|
return { allowed: true };
|
|
355
402
|
}
|
|
356
|
-
// Send payment for agent
|
|
357
|
-
async function sendAgentPayment(params) {
|
|
358
|
-
const config = loadWalletConfig();
|
|
359
|
-
if (!config)
|
|
360
|
-
throw new Error('Wallet not initialized');
|
|
361
|
-
const privateKey = await loadPrivateKey();
|
|
362
|
-
if (!privateKey)
|
|
363
|
-
throw new Error('Could not load wallet private key');
|
|
364
|
-
const txHistory = loadTxHistory();
|
|
365
|
-
// Check spend limits
|
|
366
|
-
const limitCheck = checkSpendLimit(params.amountUsd, config, txHistory);
|
|
367
|
-
if (!limitCheck.allowed) {
|
|
368
|
-
throw new Error(limitCheck.reason);
|
|
369
|
-
}
|
|
370
|
-
// Check publisher allowlist
|
|
371
|
-
if (config.allowedPublishers.length > 0 && !config.allowedPublishers.includes(params.to.toLowerCase())) {
|
|
372
|
-
throw new Error(`Publisher ${params.to} is not in your allowed publishers list`);
|
|
373
|
-
}
|
|
374
|
-
// Get current ETH price
|
|
375
|
-
const ethPrice = await getEthPrice();
|
|
376
|
-
const amountEth = params.amountUsd / ethPrice;
|
|
377
|
-
const amountWei = parseEther(amountEth.toFixed(18));
|
|
378
|
-
// Check balance
|
|
379
|
-
const balance = await getWalletBalance();
|
|
380
|
-
if (parseFloat(balance.eth) < amountEth) {
|
|
381
|
-
throw new Error(`Insufficient balance: have ${balance.eth} ETH, need ${amountEth.toFixed(6)} ETH`);
|
|
382
|
-
}
|
|
383
|
-
// Create wallet client for signing
|
|
384
|
-
const account = privateKeyToAccount(privateKey);
|
|
385
|
-
const walletClient = createWalletClient({
|
|
386
|
-
account,
|
|
387
|
-
chain: mainnet,
|
|
388
|
-
transport: http(MEV_COMMIT_RPC),
|
|
389
|
-
});
|
|
390
|
-
console.log('Sending transaction...');
|
|
391
|
-
// Send transaction
|
|
392
|
-
const txHash = await walletClient.sendTransaction({
|
|
393
|
-
to: params.to,
|
|
394
|
-
value: amountWei,
|
|
395
|
-
});
|
|
396
|
-
// Record transaction
|
|
397
|
-
const txRecord = {
|
|
398
|
-
txHash,
|
|
399
|
-
to: params.to,
|
|
400
|
-
amountEth: amountEth.toFixed(6),
|
|
401
|
-
amountUsd: params.amountUsd,
|
|
402
|
-
agentId: params.agentId,
|
|
403
|
-
timestamp: new Date().toISOString(),
|
|
404
|
-
status: 'pending',
|
|
405
|
-
};
|
|
406
|
-
txHistory.push(txRecord);
|
|
407
|
-
saveTxHistory(txHistory);
|
|
408
|
-
console.log(`Transaction sent: ${txHash}`);
|
|
409
|
-
// Wait for confirmation
|
|
410
|
-
try {
|
|
411
|
-
console.log('Waiting for confirmation...');
|
|
412
|
-
const receipt = await publicClient.waitForTransactionReceipt({
|
|
413
|
-
hash: txHash,
|
|
414
|
-
confirmations: 2,
|
|
415
|
-
timeout: 120_000,
|
|
416
|
-
});
|
|
417
|
-
const txIndex = txHistory.findIndex((tx) => tx.txHash === txHash);
|
|
418
|
-
const txRecord = txHistory[txIndex];
|
|
419
|
-
if (txIndex !== -1 && txRecord) {
|
|
420
|
-
txRecord.status = receipt.status === 'success' ? 'confirmed' : 'failed';
|
|
421
|
-
saveTxHistory(txHistory);
|
|
422
|
-
}
|
|
423
|
-
if (receipt.status !== 'success') {
|
|
424
|
-
throw new Error('Transaction failed on chain');
|
|
425
|
-
}
|
|
426
|
-
console.log('✓ Transaction confirmed!');
|
|
427
|
-
}
|
|
428
|
-
catch (error) {
|
|
429
|
-
const txIndex = txHistory.findIndex((tx) => tx.txHash === txHash);
|
|
430
|
-
const txRecord = txHistory[txIndex];
|
|
431
|
-
if (txIndex !== -1 && txRecord) {
|
|
432
|
-
txRecord.status = 'failed';
|
|
433
|
-
saveTxHistory(txHistory);
|
|
434
|
-
}
|
|
435
|
-
throw error;
|
|
436
|
-
}
|
|
437
|
-
return { txHash, amountEth: amountEth.toFixed(6) };
|
|
438
|
-
}
|
|
439
|
-
// Get ETH price from CoinGecko
|
|
440
|
-
async function getEthPrice() {
|
|
441
|
-
try {
|
|
442
|
-
const response = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd');
|
|
443
|
-
const data = (await response.json());
|
|
444
|
-
return data.ethereum?.usd || 2000;
|
|
445
|
-
}
|
|
446
|
-
catch {
|
|
447
|
-
return 2000;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
403
|
// Prompt user for confirmation
|
|
451
404
|
function prompt(question) {
|
|
452
405
|
const rl = readline.createInterface({
|
|
@@ -460,27 +413,90 @@ function prompt(question) {
|
|
|
460
413
|
});
|
|
461
414
|
});
|
|
462
415
|
}
|
|
463
|
-
//
|
|
464
|
-
async function
|
|
416
|
+
// Check agent access — returns entitlement if already purchased, or 402 payment params
|
|
417
|
+
async function getPaymentRequired(agentId, walletAddress) {
|
|
418
|
+
try {
|
|
419
|
+
const response = await fetch(`${API_BASE}/api/agents/${encodeURIComponent(agentId)}/access`, {
|
|
420
|
+
headers: { 'X-Wallet-Address': walletAddress },
|
|
421
|
+
});
|
|
422
|
+
if (response.status === 200) {
|
|
423
|
+
const data = await response.json();
|
|
424
|
+
return { status: 'granted', entitlement: data.entitlement, install: data.install };
|
|
425
|
+
}
|
|
426
|
+
if (response.status === 402) {
|
|
427
|
+
const data = await response.json();
|
|
428
|
+
return { status: 'payment_required', payment: data.payment };
|
|
429
|
+
}
|
|
430
|
+
const data = await response.json();
|
|
431
|
+
return { status: 'error', error: data.error || `HTTP ${response.status}` };
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
return { status: 'error', error: error instanceof Error ? error.message : String(error) };
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// Sign EIP-3009 TransferWithAuthorization typed data
|
|
438
|
+
async function signTransferAuthorization(payment, privateKey, walletAddress) {
|
|
439
|
+
const account = privateKeyToAccount(privateKey);
|
|
440
|
+
const value = parseUsdc(payment.amount);
|
|
441
|
+
const validBefore = BigInt(Math.floor(new Date(payment.expires_at).getTime() / 1000));
|
|
442
|
+
const authNonce = ('0x' + crypto.randomBytes(32).toString('hex'));
|
|
443
|
+
const signature = await account.signTypedData({
|
|
444
|
+
domain: USDC_EIP712_DOMAIN,
|
|
445
|
+
types: TRANSFER_WITH_AUTHORIZATION_TYPES,
|
|
446
|
+
primaryType: 'TransferWithAuthorization',
|
|
447
|
+
message: {
|
|
448
|
+
from: walletAddress,
|
|
449
|
+
to: payment.payTo,
|
|
450
|
+
value,
|
|
451
|
+
validAfter: 0n,
|
|
452
|
+
validBefore,
|
|
453
|
+
nonce: authNonce,
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
// Parse signature into v, r, s components
|
|
457
|
+
const r = ('0x' + signature.slice(2, 66));
|
|
458
|
+
const s = ('0x' + signature.slice(66, 130));
|
|
459
|
+
const v = parseInt(signature.slice(130, 132), 16);
|
|
460
|
+
return {
|
|
461
|
+
from: walletAddress,
|
|
462
|
+
to: payment.payTo,
|
|
463
|
+
value: value.toString(),
|
|
464
|
+
validAfter: '0',
|
|
465
|
+
validBefore: validBefore.toString(),
|
|
466
|
+
nonce: authNonce,
|
|
467
|
+
v,
|
|
468
|
+
r,
|
|
469
|
+
s,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
// Submit signed x402 payment to API
|
|
473
|
+
async function submitX402Payment(agentId, walletAddress, payment, authorization) {
|
|
465
474
|
try {
|
|
466
|
-
const response = await fetch(`${API_BASE}/api/
|
|
475
|
+
const response = await fetch(`${API_BASE}/api/payments/submit`, {
|
|
467
476
|
method: 'POST',
|
|
468
477
|
headers: { 'Content-Type': 'application/json' },
|
|
469
478
|
body: JSON.stringify({
|
|
470
479
|
agent_id: agentId,
|
|
471
480
|
wallet_address: walletAddress,
|
|
472
|
-
|
|
481
|
+
payment_required: {
|
|
482
|
+
amount: payment.amount,
|
|
483
|
+
currency: payment.currency,
|
|
484
|
+
payTo: payment.payTo,
|
|
485
|
+
nonce: payment.nonce,
|
|
486
|
+
expires_at: payment.expires_at,
|
|
487
|
+
},
|
|
488
|
+
authorization,
|
|
473
489
|
}),
|
|
474
490
|
});
|
|
475
491
|
if (!response.ok) {
|
|
476
|
-
const error =
|
|
477
|
-
console.error(`
|
|
492
|
+
const error = await response.json();
|
|
493
|
+
console.error(`Payment failed: ${error.error || response.statusText}`);
|
|
478
494
|
return null;
|
|
479
495
|
}
|
|
480
|
-
return
|
|
496
|
+
return await response.json();
|
|
481
497
|
}
|
|
482
498
|
catch (error) {
|
|
483
|
-
console.error(`
|
|
499
|
+
console.error(`Payment error: ${error instanceof Error ? error.message : error}`);
|
|
484
500
|
return null;
|
|
485
501
|
}
|
|
486
502
|
}
|
|
@@ -592,106 +608,107 @@ async function installAgent(agentId, options) {
|
|
|
592
608
|
}
|
|
593
609
|
}
|
|
594
610
|
console.log('└─────────────────────────────────────────────────┘');
|
|
595
|
-
// For paid agents, handle payment flow
|
|
611
|
+
// For paid agents, handle x402 USDC payment flow
|
|
596
612
|
let entitlementToken = null;
|
|
597
613
|
let expiresAt = null;
|
|
598
614
|
if (agent.type === 'proprietary' && agent.pricing.model !== 'free') {
|
|
599
|
-
|
|
615
|
+
const priceUsd = getPriceUsd(agent.pricing);
|
|
616
|
+
// Lazy wallet creation
|
|
600
617
|
const { address: walletAddress, created: walletCreated } = await ensureWalletExists();
|
|
601
618
|
if (walletCreated) {
|
|
602
619
|
console.log('\n🔐 Wallet created automatically');
|
|
603
620
|
console.log(` Address: ${walletAddress}`);
|
|
604
621
|
}
|
|
605
|
-
const wallet = loadWalletConfig();
|
|
606
|
-
const ethPrice = await getEthPrice();
|
|
607
|
-
const priceEth = getPriceUsd(agent.pricing) / ethPrice;
|
|
608
622
|
console.log('\n💰 Payment Required:');
|
|
609
|
-
console.log(` Price: $${
|
|
610
|
-
console.log(` Your wallet: ${
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
623
|
+
console.log(` Price: $${priceUsd.toFixed(2)} USDC`);
|
|
624
|
+
console.log(` Your wallet: ${walletAddress}`);
|
|
625
|
+
// Check access status (entitlement or 402)
|
|
626
|
+
const accessResult = await getPaymentRequired(agent.agent_id, walletAddress);
|
|
627
|
+
if (accessResult.status === 'error') {
|
|
628
|
+
console.log(`\n❌ Access check failed: ${accessResult.error}`);
|
|
629
|
+
process.exit(1);
|
|
630
|
+
}
|
|
631
|
+
if (accessResult.status === 'granted' && accessResult.entitlement) {
|
|
616
632
|
console.log('\n✓ Already purchased! Using existing entitlement.');
|
|
617
|
-
entitlementToken =
|
|
618
|
-
expiresAt =
|
|
633
|
+
entitlementToken = accessResult.entitlement.token;
|
|
634
|
+
expiresAt = accessResult.entitlement.expires_at;
|
|
619
635
|
}
|
|
620
|
-
else {
|
|
621
|
-
|
|
622
|
-
//
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
const balance = await getWalletBalance();
|
|
633
|
-
console.log(`\n Your balance: ${balance.eth} ETH ($${balance.usd})`);
|
|
634
|
-
// Auto-trigger funding flow if insufficient balance
|
|
635
|
-
if (balance.usd < getPriceUsd(agent.pricing)) {
|
|
636
|
-
console.log(`\n⚠️ Insufficient balance. Need $${getPriceUsd(agent.pricing)}, have $${balance.usd}`);
|
|
637
|
-
const funded = await triggerFundingFlow(getPriceUsd(agent.pricing));
|
|
638
|
-
if (!funded) {
|
|
639
|
-
process.exit(1);
|
|
640
|
-
}
|
|
641
|
-
// Re-check balance after funding
|
|
642
|
-
const newBalance = await getWalletBalance();
|
|
643
|
-
console.log(` New balance: ${newBalance.eth} ETH ($${newBalance.usd})`);
|
|
644
|
-
if (newBalance.usd < getPriceUsd(agent.pricing)) {
|
|
645
|
-
console.log(`\n❌ Still insufficient. Need $${getPriceUsd(agent.pricing)}, have $${newBalance.usd}`);
|
|
646
|
-
process.exit(1);
|
|
647
|
-
}
|
|
636
|
+
else if (accessResult.status === 'payment_required') {
|
|
637
|
+
const paymentRequired = accessResult.payment;
|
|
638
|
+
// Check USDC balance
|
|
639
|
+
try {
|
|
640
|
+
const balance = await getWalletBalance();
|
|
641
|
+
const requiredRaw = parseUsdc(paymentRequired.amount);
|
|
642
|
+
console.log(` Your USDC balance: $${balance.usdc}`);
|
|
643
|
+
if (balance.usdcRaw < requiredRaw) {
|
|
644
|
+
console.log(`\n⚠️ Insufficient USDC. Need $${paymentRequired.amount}, have $${balance.usdc}`);
|
|
645
|
+
const funded = await triggerFundingFlow(priceUsd);
|
|
646
|
+
if (!funded) {
|
|
647
|
+
process.exit(1);
|
|
648
648
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
console.log(
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (!options.yes) {
|
|
656
|
-
const confirm = await prompt(`\nPay $${getPriceUsd(agent.pricing)} (~${priceEth.toFixed(6)} ETH) to ${payoutAddress.slice(0, 10)}...? (y/n) `);
|
|
657
|
-
if (confirm !== 'y' && confirm !== 'yes') {
|
|
658
|
-
console.log('Payment cancelled.');
|
|
659
|
-
process.exit(0);
|
|
649
|
+
// Re-check balance after funding
|
|
650
|
+
const newBalance = await getWalletBalance();
|
|
651
|
+
console.log(` New USDC balance: $${newBalance.usdc}`);
|
|
652
|
+
if (newBalance.usdcRaw < requiredRaw) {
|
|
653
|
+
console.log(`\n❌ Still insufficient. Need $${paymentRequired.amount}, have $${newBalance.usdc}`);
|
|
654
|
+
process.exit(1);
|
|
660
655
|
}
|
|
661
656
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
console.log(`\n❌ Could not check balance: ${error instanceof Error ? error.message : error}`);
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
// Check spend limits
|
|
663
|
+
const config = loadWalletConfig();
|
|
664
|
+
const txHistory = loadTxHistory();
|
|
665
|
+
const limitCheck = checkSpendLimit(priceUsd, config, txHistory);
|
|
666
|
+
if (!limitCheck.allowed) {
|
|
667
|
+
console.log(`\n❌ Spend limit exceeded: ${limitCheck.reason}`);
|
|
668
|
+
process.exit(1);
|
|
669
|
+
}
|
|
670
|
+
// Confirm payment (skip with --yes or --pay)
|
|
671
|
+
if (!options.yes && !options.pay) {
|
|
672
|
+
const confirm = await prompt(`\nPay $${paymentRequired.amount} USDC for "${agent.name}"? (y/n) `);
|
|
673
|
+
if (confirm !== 'y' && confirm !== 'yes') {
|
|
674
|
+
console.log('Payment cancelled.');
|
|
675
|
+
process.exit(0);
|
|
676
676
|
}
|
|
677
677
|
}
|
|
678
|
-
//
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
console.log('
|
|
682
|
-
console.log(' 2. Manual: Send ' + priceEth.toFixed(6) + ' ETH to ' + (agent.publisher.payout_address || '[publisher]'));
|
|
683
|
-
console.log(' Then: agentstore install ' + agent.agent_id + ' --tx-hash 0x...');
|
|
678
|
+
// Sign the EIP-3009 authorization
|
|
679
|
+
const privateKey = await loadPrivateKey();
|
|
680
|
+
if (!privateKey) {
|
|
681
|
+
console.log('\n❌ Could not load wallet private key');
|
|
684
682
|
process.exit(1);
|
|
685
683
|
}
|
|
686
|
-
console.log('\
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
684
|
+
console.log('\n🔐 Signing USDC authorization...');
|
|
685
|
+
const authorization = await signTransferAuthorization(paymentRequired, privateKey, walletAddress);
|
|
686
|
+
// Submit to API for facilitator relay
|
|
687
|
+
console.log('🔄 Processing payment via x402...');
|
|
688
|
+
const result = await submitX402Payment(agent.agent_id, walletAddress, paymentRequired, authorization);
|
|
689
|
+
if (!result) {
|
|
690
|
+
console.log('❌ Payment processing failed.');
|
|
690
691
|
process.exit(1);
|
|
691
692
|
}
|
|
692
|
-
entitlementToken =
|
|
693
|
-
expiresAt =
|
|
694
|
-
console.log('✓ Payment
|
|
693
|
+
entitlementToken = result.entitlement_token;
|
|
694
|
+
expiresAt = null;
|
|
695
|
+
console.log('✓ Payment confirmed! (gasless USDC via x402)');
|
|
696
|
+
// Record in local tx history
|
|
697
|
+
const txProof = result.proof;
|
|
698
|
+
txHistory.push({
|
|
699
|
+
txHash: txProof?.tx_hash || 'x402-' + Date.now(),
|
|
700
|
+
to: paymentRequired.payTo,
|
|
701
|
+
amountUsdc: paymentRequired.amount,
|
|
702
|
+
amountUsd: priceUsd,
|
|
703
|
+
agentId: agent.agent_id,
|
|
704
|
+
timestamp: new Date().toISOString(),
|
|
705
|
+
status: 'confirmed',
|
|
706
|
+
});
|
|
707
|
+
saveTxHistory(txHistory);
|
|
708
|
+
}
|
|
709
|
+
else if (accessResult.status === 'granted') {
|
|
710
|
+
// Free agent via access route (model says free)
|
|
711
|
+
console.log('\n✓ Access granted (no payment needed).');
|
|
695
712
|
}
|
|
696
713
|
}
|
|
697
714
|
console.log('\nInstalling...');
|
|
@@ -890,9 +907,8 @@ program
|
|
|
890
907
|
program
|
|
891
908
|
.command('install <agent_id>')
|
|
892
909
|
.description('Install an agent from the marketplace')
|
|
893
|
-
.option('-y, --yes', 'Skip confirmation /
|
|
894
|
-
.option('--pay', '
|
|
895
|
-
.option('--tx-hash <hash>', 'Transaction hash for manual payment verification')
|
|
910
|
+
.option('-y, --yes', 'Skip confirmation / auto-confirm payment')
|
|
911
|
+
.option('--pay', 'Auto-confirm payment (alias for --yes)')
|
|
896
912
|
.action(installAgent);
|
|
897
913
|
program
|
|
898
914
|
.command('list')
|
|
@@ -952,7 +968,7 @@ program
|
|
|
952
968
|
console.log();
|
|
953
969
|
}
|
|
954
970
|
console.log('Install with: agentstore install <agent_id>');
|
|
955
|
-
console.log('
|
|
971
|
+
console.log('Paid agents prompt for USDC payment automatically.');
|
|
956
972
|
}
|
|
957
973
|
catch (error) {
|
|
958
974
|
console.error(`Error: ${error instanceof Error ? error.message : error}`);
|
|
@@ -978,8 +994,11 @@ walletCmd
|
|
|
978
994
|
const { address } = await createNewWallet();
|
|
979
995
|
console.log('\n✅ Wallet created!');
|
|
980
996
|
console.log(`\nAddress: ${address}`);
|
|
981
|
-
console.log('\n⚠️ Fund this address with
|
|
982
|
-
console.log('
|
|
997
|
+
console.log('\n⚠️ Fund this address with USDC to purchase paid agents.');
|
|
998
|
+
console.log(' Options:');
|
|
999
|
+
console.log(' 1. Buy with card: agentstore wallet fund');
|
|
1000
|
+
console.log(` 2. Send USDC (Ethereum) from any wallet to: ${address}`);
|
|
1001
|
+
console.log(' 3. Import existing wallet: agentstore wallet import');
|
|
983
1002
|
}
|
|
984
1003
|
catch (error) {
|
|
985
1004
|
console.error(`Error: ${error instanceof Error ? error.message : error}`);
|
|
@@ -988,7 +1007,7 @@ walletCmd
|
|
|
988
1007
|
});
|
|
989
1008
|
walletCmd
|
|
990
1009
|
.command('balance')
|
|
991
|
-
.description('Show wallet balance')
|
|
1010
|
+
.description('Show wallet USDC balance')
|
|
992
1011
|
.action(async () => {
|
|
993
1012
|
try {
|
|
994
1013
|
if (!walletExists()) {
|
|
@@ -996,10 +1015,11 @@ walletCmd
|
|
|
996
1015
|
process.exit(1);
|
|
997
1016
|
}
|
|
998
1017
|
const config = loadWalletConfig();
|
|
999
|
-
console.log(`\
|
|
1000
|
-
console.log('Fetching balance...');
|
|
1018
|
+
console.log(`\nWallet: ${config?.address}`);
|
|
1019
|
+
console.log('Fetching USDC balance...');
|
|
1001
1020
|
const balance = await getWalletBalance();
|
|
1002
|
-
console.log(
|
|
1021
|
+
console.log(`USDC Balance: $${balance.usdc}`);
|
|
1022
|
+
console.log('Network: Ethereum Mainnet');
|
|
1003
1023
|
// Show spending stats
|
|
1004
1024
|
const txHistory = loadTxHistory();
|
|
1005
1025
|
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
|
|
@@ -1041,7 +1061,7 @@ walletCmd
|
|
|
1041
1061
|
const date = new Date(tx.timestamp).toLocaleDateString();
|
|
1042
1062
|
const statusIcon = tx.status === 'confirmed' ? '✓' : tx.status === 'pending' ? '⏳' : '✗';
|
|
1043
1063
|
console.log(` ${statusIcon} ${date} | ${tx.agentId}`);
|
|
1044
|
-
console.log(`
|
|
1064
|
+
console.log(` $${tx.amountUsdc || tx.amountUsd} USDC → ${tx.to.slice(0, 10)}...`);
|
|
1045
1065
|
console.log(` ${tx.txHash.slice(0, 20)}...`);
|
|
1046
1066
|
console.log();
|
|
1047
1067
|
}
|
|
@@ -1068,12 +1088,52 @@ walletCmd
|
|
|
1068
1088
|
process.exit(1);
|
|
1069
1089
|
}
|
|
1070
1090
|
});
|
|
1091
|
+
walletCmd
|
|
1092
|
+
.command('import')
|
|
1093
|
+
.description('Import an existing wallet by private key')
|
|
1094
|
+
.action(async () => {
|
|
1095
|
+
try {
|
|
1096
|
+
if (walletExists()) {
|
|
1097
|
+
console.log('Wallet already exists. Delete ~/.agentstore/wallet.* files to import a new one.');
|
|
1098
|
+
process.exit(1);
|
|
1099
|
+
}
|
|
1100
|
+
const key = await prompt('Enter private key (0x...): ');
|
|
1101
|
+
if (!key.startsWith('0x') || key.length !== 66) {
|
|
1102
|
+
console.log('Invalid private key format. Must be a 0x-prefixed 64-character hex string.');
|
|
1103
|
+
process.exit(1);
|
|
1104
|
+
}
|
|
1105
|
+
ensureDirectories();
|
|
1106
|
+
const account = privateKeyToAccount(key);
|
|
1107
|
+
const config = {
|
|
1108
|
+
address: account.address,
|
|
1109
|
+
createdAt: new Date().toISOString(),
|
|
1110
|
+
network: 'mainnet',
|
|
1111
|
+
rpcEndpoint: MEV_COMMIT_RPC,
|
|
1112
|
+
spendLimits: { perTransaction: 100, daily: 500, weekly: 2000 },
|
|
1113
|
+
allowedPublishers: [],
|
|
1114
|
+
};
|
|
1115
|
+
const password = await getOrCreatePassword();
|
|
1116
|
+
const encrypted = encryptKey(key, password);
|
|
1117
|
+
fs.writeFileSync(KEYSTORE_FILE, JSON.stringify(encrypted), { mode: 0o600 });
|
|
1118
|
+
fs.writeFileSync(WALLET_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
1119
|
+
fs.writeFileSync(TX_HISTORY_FILE, JSON.stringify([]), { mode: 0o600 });
|
|
1120
|
+
// Show USDC balance
|
|
1121
|
+
const usdcRaw = await getUsdcBalance(account.address);
|
|
1122
|
+
console.log(`\n✅ Wallet imported!`);
|
|
1123
|
+
console.log(` Address: ${account.address}`);
|
|
1124
|
+
console.log(` USDC Balance: $${formatUsdc(usdcRaw)}`);
|
|
1125
|
+
}
|
|
1126
|
+
catch (error) {
|
|
1127
|
+
console.error(`Error: ${error instanceof Error ? error.message : error}`);
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1071
1131
|
walletCmd
|
|
1072
1132
|
.command('fund')
|
|
1073
|
-
.description('Fund your wallet with
|
|
1133
|
+
.description('Fund your wallet with USDC')
|
|
1074
1134
|
.option('-a, --amount <usd>', 'Amount in USD to purchase', parseFloat)
|
|
1075
1135
|
.option('--no-open', 'Print URL instead of opening browser')
|
|
1076
|
-
.option('--wait', 'Wait and poll for
|
|
1136
|
+
.option('--wait', 'Wait and poll for USDC to arrive')
|
|
1077
1137
|
.action(async (options) => {
|
|
1078
1138
|
try {
|
|
1079
1139
|
if (!walletExists()) {
|
|
@@ -1085,23 +1145,19 @@ walletCmd
|
|
|
1085
1145
|
console.log('Failed to load wallet config');
|
|
1086
1146
|
process.exit(1);
|
|
1087
1147
|
}
|
|
1088
|
-
console.log('\n💳
|
|
1148
|
+
console.log('\n💳 Fund Your Wallet with USDC\n');
|
|
1089
1149
|
console.log(` Wallet: ${config.address}`);
|
|
1090
|
-
// Get initial balance for comparison
|
|
1150
|
+
// Get initial USDC balance for comparison
|
|
1091
1151
|
let initialBalance;
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
}
|
|
1099
|
-
catch {
|
|
1100
|
-
// Ignore balance fetch errors
|
|
1101
|
-
}
|
|
1152
|
+
try {
|
|
1153
|
+
initialBalance = await getUsdcBalance(config.address);
|
|
1154
|
+
console.log(` Current USDC: $${formatUsdc(initialBalance)}`);
|
|
1155
|
+
}
|
|
1156
|
+
catch {
|
|
1157
|
+
// Ignore balance fetch errors
|
|
1102
1158
|
}
|
|
1103
1159
|
if (options.amount) {
|
|
1104
|
-
console.log(` Amount: $${options.amount}
|
|
1160
|
+
console.log(` Amount: $${options.amount} USDC`);
|
|
1105
1161
|
}
|
|
1106
1162
|
console.log('\n🔄 Generating secure onramp session...');
|
|
1107
1163
|
// Call API to get onramp URL
|
|
@@ -1111,38 +1167,22 @@ walletCmd
|
|
|
1111
1167
|
body: JSON.stringify({
|
|
1112
1168
|
wallet_address: config.address,
|
|
1113
1169
|
amount_usd: options.amount,
|
|
1170
|
+
asset: 'USDC',
|
|
1114
1171
|
}),
|
|
1115
1172
|
});
|
|
1116
1173
|
const result = await response.json();
|
|
1117
1174
|
if (!response.ok || !result.success) {
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
console.log('\n⚠️ Coinbase Onramp not configured on server.\n');
|
|
1121
|
-
console.log(' Manual funding instructions:');
|
|
1122
|
-
console.log(` 1. ${result.manual_instructions.step1}`);
|
|
1123
|
-
console.log(` 2. ${result.manual_instructions.step2}`);
|
|
1124
|
-
console.log(` 3. ${result.manual_instructions.step3}`);
|
|
1125
|
-
console.log(`\n Your wallet address: ${config.address}`);
|
|
1126
|
-
}
|
|
1127
|
-
else {
|
|
1128
|
-
console.log(`\n❌ Error: ${result.error || result.message || 'Unknown error'}`);
|
|
1129
|
-
if (result.fallback) {
|
|
1130
|
-
console.log(`\n ${result.fallback.message}`);
|
|
1131
|
-
console.log(` 1. ${result.fallback.step1}`);
|
|
1132
|
-
console.log(` 2. ${result.fallback.step2}`);
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1175
|
+
console.log('\n⚠️ Coinbase Onramp unavailable.\n');
|
|
1176
|
+
showFundingOptions(config.address, options.amount || 10);
|
|
1135
1177
|
process.exit(1);
|
|
1136
1178
|
}
|
|
1137
1179
|
const onrampUrl = result.onramp_url;
|
|
1138
1180
|
if (options.open === false) {
|
|
1139
|
-
// Just print the URL
|
|
1140
1181
|
console.log('\n✅ Onramp URL generated:\n');
|
|
1141
1182
|
console.log(` ${onrampUrl}\n`);
|
|
1142
|
-
console.log(' Open this URL in your browser to
|
|
1183
|
+
console.log(' Open this URL in your browser to purchase USDC.');
|
|
1143
1184
|
}
|
|
1144
1185
|
else {
|
|
1145
|
-
// Open in default browser
|
|
1146
1186
|
console.log('\n🌐 Opening Coinbase in your browser...\n');
|
|
1147
1187
|
const { exec } = await import('child_process');
|
|
1148
1188
|
const openCmd = process.platform === 'darwin'
|
|
@@ -1156,38 +1196,35 @@ walletCmd
|
|
|
1156
1196
|
console.log(` ${onrampUrl}\n`);
|
|
1157
1197
|
}
|
|
1158
1198
|
});
|
|
1159
|
-
console.log(' Complete the purchase in your browser.');
|
|
1160
|
-
console.log('
|
|
1199
|
+
console.log(' Complete the USDC purchase in your browser.');
|
|
1200
|
+
console.log(' USDC will arrive in your wallet within a few minutes.\n');
|
|
1201
|
+
showFundingOptionsShort(config.address);
|
|
1161
1202
|
}
|
|
1162
|
-
// Poll for balance changes if --wait flag is set
|
|
1203
|
+
// Poll for USDC balance changes if --wait flag is set
|
|
1163
1204
|
if (options.wait && initialBalance !== undefined) {
|
|
1164
|
-
console.log('⏳ Waiting for
|
|
1205
|
+
console.log('\n⏳ Waiting for USDC to arrive (Ctrl+C to cancel)...\n');
|
|
1165
1206
|
const startTime = Date.now();
|
|
1166
1207
|
const maxWaitTime = 10 * 60 * 1000; // 10 minutes
|
|
1167
1208
|
const pollInterval = 15 * 1000; // 15 seconds
|
|
1168
1209
|
while (Date.now() - startTime < maxWaitTime) {
|
|
1169
1210
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1170
1211
|
try {
|
|
1171
|
-
const currentBalance = await
|
|
1172
|
-
address: config.address,
|
|
1173
|
-
});
|
|
1212
|
+
const currentBalance = await getUsdcBalance(config.address);
|
|
1174
1213
|
if (currentBalance > initialBalance) {
|
|
1175
1214
|
const added = currentBalance - initialBalance;
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
console.log(
|
|
1179
|
-
console.log(` Added: ${formatEther(added)} ETH (~$${addedUsd.toFixed(2)})`);
|
|
1180
|
-
console.log(` New balance: ${formatEther(currentBalance)} ETH`);
|
|
1215
|
+
console.log('\n✅ USDC received!\n');
|
|
1216
|
+
console.log(` Added: $${formatUsdc(added)} USDC`);
|
|
1217
|
+
console.log(` New balance: $${formatUsdc(currentBalance)} USDC`);
|
|
1181
1218
|
process.exit(0);
|
|
1182
1219
|
}
|
|
1183
1220
|
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
1184
|
-
process.stdout.write(`\r Checking... (${elapsed}s elapsed)`);
|
|
1221
|
+
process.stdout.write(`\r Checking USDC... (${elapsed}s elapsed)`);
|
|
1185
1222
|
}
|
|
1186
1223
|
catch {
|
|
1187
1224
|
// Ignore individual poll errors
|
|
1188
1225
|
}
|
|
1189
1226
|
}
|
|
1190
|
-
console.log('\n\n⚠️ Timed out waiting for
|
|
1227
|
+
console.log('\n\n⚠️ Timed out waiting for USDC.');
|
|
1191
1228
|
console.log(' Funds may still arrive - check your balance later with:');
|
|
1192
1229
|
console.log(' agentstore wallet balance\n');
|
|
1193
1230
|
}
|