agentstore 1.0.2 → 1.0.3
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/dist/index.js +156 -86
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import * as keytar from 'keytar';
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
14
|
const __dirname = path.dirname(__filename);
|
|
15
15
|
const API_BASE = 'https://api.agentstore.tools';
|
|
16
|
-
const
|
|
16
|
+
const ETHEREUM_RPC = 'https://ethereum-rpc.publicnode.com';
|
|
17
17
|
const KEYCHAIN_SERVICE = 'agentstore-wallet';
|
|
18
18
|
const KEYCHAIN_ACCOUNT = 'encryption-key';
|
|
19
19
|
// USDC contract on Ethereum mainnet
|
|
@@ -53,7 +53,7 @@ const TX_HISTORY_FILE = path.join(AGENTSTORE_DIR, 'tx_history.json');
|
|
|
53
53
|
// Create public client for reading blockchain state
|
|
54
54
|
const publicClient = createPublicClient({
|
|
55
55
|
chain: mainnet,
|
|
56
|
-
transport: http(
|
|
56
|
+
transport: http(ETHEREUM_RPC),
|
|
57
57
|
});
|
|
58
58
|
// Ensure directories exist
|
|
59
59
|
function ensureDirectories() {
|
|
@@ -214,7 +214,7 @@ async function createNewWallet() {
|
|
|
214
214
|
address: account.address,
|
|
215
215
|
createdAt: new Date().toISOString(),
|
|
216
216
|
network: 'mainnet',
|
|
217
|
-
rpcEndpoint:
|
|
217
|
+
rpcEndpoint: ETHEREUM_RPC,
|
|
218
218
|
spendLimits: {
|
|
219
219
|
perTransaction: 100,
|
|
220
220
|
daily: 500,
|
|
@@ -243,7 +243,7 @@ async function ensureWalletExists() {
|
|
|
243
243
|
address: account.address,
|
|
244
244
|
createdAt: new Date().toISOString(),
|
|
245
245
|
network: 'mainnet',
|
|
246
|
-
rpcEndpoint:
|
|
246
|
+
rpcEndpoint: ETHEREUM_RPC,
|
|
247
247
|
spendLimits: {
|
|
248
248
|
perTransaction: 100,
|
|
249
249
|
daily: 500,
|
|
@@ -413,6 +413,42 @@ function prompt(question) {
|
|
|
413
413
|
});
|
|
414
414
|
});
|
|
415
415
|
}
|
|
416
|
+
// Prompt for secret input (masked)
|
|
417
|
+
function promptSecret(question) {
|
|
418
|
+
return new Promise((resolve) => {
|
|
419
|
+
process.stdout.write(question);
|
|
420
|
+
const stdin = process.stdin;
|
|
421
|
+
const wasRaw = stdin.isRaw;
|
|
422
|
+
stdin.setRawMode?.(true);
|
|
423
|
+
stdin.resume();
|
|
424
|
+
stdin.setEncoding('utf8');
|
|
425
|
+
let input = '';
|
|
426
|
+
const onData = (ch) => {
|
|
427
|
+
if (ch === '\n' || ch === '\r') {
|
|
428
|
+
stdin.setRawMode?.(wasRaw ?? false);
|
|
429
|
+
stdin.pause();
|
|
430
|
+
stdin.removeListener('data', onData);
|
|
431
|
+
process.stdout.write('\n');
|
|
432
|
+
resolve(input);
|
|
433
|
+
}
|
|
434
|
+
else if (ch === '\u0003') {
|
|
435
|
+
// Ctrl+C
|
|
436
|
+
process.exit(0);
|
|
437
|
+
}
|
|
438
|
+
else if (ch === '\u007f' || ch === '\b') {
|
|
439
|
+
if (input.length > 0) {
|
|
440
|
+
input = input.slice(0, -1);
|
|
441
|
+
process.stdout.write('\b \b');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
input += ch;
|
|
446
|
+
process.stdout.write('*');
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
stdin.on('data', onData);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
416
452
|
// Check agent access — returns entitlement if already purchased, or 402 payment params
|
|
417
453
|
async function getPaymentRequired(agentId, walletAddress) {
|
|
418
454
|
try {
|
|
@@ -490,6 +526,18 @@ async function submitX402Payment(agentId, walletAddress, payment, authorization)
|
|
|
490
526
|
});
|
|
491
527
|
if (!response.ok) {
|
|
492
528
|
const error = await response.json();
|
|
529
|
+
if (response.status === 409) {
|
|
530
|
+
// Already purchased — re-check /access for the entitlement
|
|
531
|
+
console.log('Agent already purchased. Retrieving entitlement...');
|
|
532
|
+
const access = await getPaymentRequired(agentId, walletAddress);
|
|
533
|
+
if (access.status === 'granted' && access.entitlement) {
|
|
534
|
+
return {
|
|
535
|
+
entitlement_token: access.entitlement.token,
|
|
536
|
+
install: access.install,
|
|
537
|
+
proof: null,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
}
|
|
493
541
|
console.error(`Payment failed: ${error.error || response.statusText}`);
|
|
494
542
|
return null;
|
|
495
543
|
}
|
|
@@ -575,6 +623,9 @@ This is a prompt-based agent. Reference it by asking Claude to follow the instru
|
|
|
575
623
|
}
|
|
576
624
|
// Install command
|
|
577
625
|
async function installAgent(agentId, options) {
|
|
626
|
+
// --pay is a true alias for --yes
|
|
627
|
+
if (options.pay)
|
|
628
|
+
options.yes = true;
|
|
578
629
|
ensureDirectories();
|
|
579
630
|
console.log(`Fetching agent: ${agentId}...`);
|
|
580
631
|
const agent = await fetchAgent(agentId);
|
|
@@ -622,93 +673,112 @@ async function installAgent(agentId, options) {
|
|
|
622
673
|
console.log('\n💰 Payment Required:');
|
|
623
674
|
console.log(` Price: $${priceUsd.toFixed(2)} USDC`);
|
|
624
675
|
console.log(` Your wallet: ${walletAddress}`);
|
|
625
|
-
// Check
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
process.exit(1);
|
|
630
|
-
}
|
|
631
|
-
if (accessResult.status === 'granted' && accessResult.entitlement) {
|
|
676
|
+
// Check local entitlements first (skip API call if already purchased)
|
|
677
|
+
const localEntitlements = loadEntitlements();
|
|
678
|
+
const localEntitlement = localEntitlements.find((e) => e.agentId === agent.agent_id);
|
|
679
|
+
if (localEntitlement) {
|
|
632
680
|
console.log('\n✓ Already purchased! Using existing entitlement.');
|
|
633
|
-
entitlementToken =
|
|
634
|
-
expiresAt =
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
}
|
|
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);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
catch (error) {
|
|
659
|
-
console.log(`\n❌ Could not check balance: ${error instanceof Error ? error.message : error}`);
|
|
681
|
+
entitlementToken = localEntitlement.token;
|
|
682
|
+
expiresAt = localEntitlement.expiresAt;
|
|
683
|
+
}
|
|
684
|
+
// If no local entitlement, check server
|
|
685
|
+
if (!entitlementToken) {
|
|
686
|
+
const accessResult = await getPaymentRequired(agent.agent_id, walletAddress);
|
|
687
|
+
if (accessResult.status === 'error') {
|
|
688
|
+
console.log(`\n❌ Access check failed: ${accessResult.error}`);
|
|
660
689
|
process.exit(1);
|
|
661
690
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
if (!limitCheck.allowed) {
|
|
667
|
-
console.log(`\n❌ Spend limit exceeded: ${limitCheck.reason}`);
|
|
668
|
-
process.exit(1);
|
|
691
|
+
if (accessResult.status === 'granted' && accessResult.entitlement) {
|
|
692
|
+
console.log('\n✓ Already purchased! Using existing entitlement.');
|
|
693
|
+
entitlementToken = accessResult.entitlement.token;
|
|
694
|
+
expiresAt = accessResult.entitlement.expires_at;
|
|
669
695
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
696
|
+
}
|
|
697
|
+
if (!entitlementToken) {
|
|
698
|
+
// Need to pay — fetch 402 details
|
|
699
|
+
const accessResult = await getPaymentRequired(agent.agent_id, walletAddress);
|
|
700
|
+
if (accessResult.status !== 'payment_required') {
|
|
701
|
+
if (accessResult.status === 'granted') {
|
|
702
|
+
console.log('\n✓ Access granted (no payment needed).');
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
console.log(`\n❌ Unexpected access status: ${accessResult.status}`);
|
|
706
|
+
process.exit(1);
|
|
676
707
|
}
|
|
677
708
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
709
|
+
else {
|
|
710
|
+
const paymentRequired = accessResult.payment;
|
|
711
|
+
// Check USDC balance
|
|
712
|
+
try {
|
|
713
|
+
const balance = await getWalletBalance();
|
|
714
|
+
const requiredRaw = parseUsdc(paymentRequired.amount);
|
|
715
|
+
console.log(` Your USDC balance: $${balance.usdc}`);
|
|
716
|
+
if (balance.usdcRaw < requiredRaw) {
|
|
717
|
+
console.log(`\n⚠️ Insufficient USDC. Need $${paymentRequired.amount}, have $${balance.usdc}`);
|
|
718
|
+
const funded = await triggerFundingFlow(priceUsd);
|
|
719
|
+
if (!funded) {
|
|
720
|
+
process.exit(1);
|
|
721
|
+
}
|
|
722
|
+
// Re-check balance after funding
|
|
723
|
+
const newBalance = await getWalletBalance();
|
|
724
|
+
console.log(` New USDC balance: $${newBalance.usdc}`);
|
|
725
|
+
if (newBalance.usdcRaw < requiredRaw) {
|
|
726
|
+
console.log(`\n❌ Still insufficient. Need $${paymentRequired.amount}, have $${newBalance.usdc}`);
|
|
727
|
+
process.exit(1);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
console.log(`\n❌ Could not check balance: ${error instanceof Error ? error.message : error}`);
|
|
733
|
+
process.exit(1);
|
|
734
|
+
}
|
|
735
|
+
// Check spend limits
|
|
736
|
+
const config = loadWalletConfig();
|
|
737
|
+
const txHistory = loadTxHistory();
|
|
738
|
+
const limitCheck = checkSpendLimit(priceUsd, config, txHistory);
|
|
739
|
+
if (!limitCheck.allowed) {
|
|
740
|
+
console.log(`\n❌ Spend limit exceeded: ${limitCheck.reason}`);
|
|
741
|
+
process.exit(1);
|
|
742
|
+
}
|
|
743
|
+
// Confirm payment (skip with --yes or --pay)
|
|
744
|
+
if (!options.yes) {
|
|
745
|
+
const confirm = await prompt(`\nPay $${paymentRequired.amount} USDC for "${agent.name}"? (y/n) `);
|
|
746
|
+
if (confirm !== 'y' && confirm !== 'yes') {
|
|
747
|
+
console.log('Payment cancelled.');
|
|
748
|
+
process.exit(0);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
// Sign the EIP-3009 authorization
|
|
752
|
+
const privateKey = await loadPrivateKey();
|
|
753
|
+
if (!privateKey) {
|
|
754
|
+
console.log('\n❌ Could not load wallet private key');
|
|
755
|
+
process.exit(1);
|
|
756
|
+
}
|
|
757
|
+
console.log('\n🔐 Signing USDC authorization...');
|
|
758
|
+
const authorization = await signTransferAuthorization(paymentRequired, privateKey, walletAddress);
|
|
759
|
+
// Submit to API for facilitator relay
|
|
760
|
+
console.log('🔄 Processing payment via x402...');
|
|
761
|
+
const result = await submitX402Payment(agent.agent_id, walletAddress, paymentRequired, authorization);
|
|
762
|
+
if (!result) {
|
|
763
|
+
console.log('❌ Payment processing failed.');
|
|
764
|
+
process.exit(1);
|
|
765
|
+
}
|
|
766
|
+
entitlementToken = result.entitlement_token;
|
|
767
|
+
expiresAt = null;
|
|
768
|
+
console.log('✓ Payment confirmed! (gasless USDC via x402)');
|
|
769
|
+
// Record in local tx history
|
|
770
|
+
const txProof = result.proof;
|
|
771
|
+
txHistory.push({
|
|
772
|
+
txHash: txProof?.tx_hash || 'x402-' + Date.now(),
|
|
773
|
+
to: paymentRequired.payTo,
|
|
774
|
+
amountUsdc: paymentRequired.amount,
|
|
775
|
+
amountUsd: priceUsd,
|
|
776
|
+
agentId: agent.agent_id,
|
|
777
|
+
timestamp: new Date().toISOString(),
|
|
778
|
+
status: 'confirmed',
|
|
779
|
+
});
|
|
780
|
+
saveTxHistory(txHistory);
|
|
692
781
|
}
|
|
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).');
|
|
712
782
|
}
|
|
713
783
|
}
|
|
714
784
|
console.log('\nInstalling...');
|
|
@@ -1097,7 +1167,7 @@ walletCmd
|
|
|
1097
1167
|
console.log('Wallet already exists. Delete ~/.agentstore/wallet.* files to import a new one.');
|
|
1098
1168
|
process.exit(1);
|
|
1099
1169
|
}
|
|
1100
|
-
const key = await
|
|
1170
|
+
const key = await promptSecret('Enter private key (0x...): ');
|
|
1101
1171
|
if (!key.startsWith('0x') || key.length !== 66) {
|
|
1102
1172
|
console.log('Invalid private key format. Must be a 0x-prefixed 64-character hex string.');
|
|
1103
1173
|
process.exit(1);
|
|
@@ -1108,7 +1178,7 @@ walletCmd
|
|
|
1108
1178
|
address: account.address,
|
|
1109
1179
|
createdAt: new Date().toISOString(),
|
|
1110
1180
|
network: 'mainnet',
|
|
1111
|
-
rpcEndpoint:
|
|
1181
|
+
rpcEndpoint: ETHEREUM_RPC,
|
|
1112
1182
|
spendLimits: { perTransaction: 100, daily: 500, weekly: 2000 },
|
|
1113
1183
|
allowedPublishers: [],
|
|
1114
1184
|
};
|