@ghostspeak/sdk 2.0.6 → 2.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.
- package/README.md +152 -30
- package/dist/GhostSpeakClient-CWmGaM9Q.d.ts +1007 -0
- package/dist/StakingModule-C5rzuOWb.d.ts +2526 -0
- package/dist/{agent-M74TCRON.js → agent-5YLZ7DAC.js} +4 -4
- package/dist/{agent-M74TCRON.js.map → agent-5YLZ7DAC.js.map} +1 -1
- package/dist/batch-operations-45CQFEID.js +4 -0
- package/dist/batch-operations-45CQFEID.js.map +1 -0
- package/dist/browser.d.ts +45 -554
- package/dist/browser.js +15 -842
- package/dist/browser.js.map +1 -1
- package/dist/chunk-AL3HQN73.js +754 -0
- package/dist/chunk-AL3HQN73.js.map +1 -0
- package/dist/chunk-BF3IQ35I.js +284 -0
- package/dist/chunk-BF3IQ35I.js.map +1 -0
- package/dist/chunk-BQDGRTVP.js +168 -0
- package/dist/chunk-BQDGRTVP.js.map +1 -0
- package/dist/chunk-C5CDA3WX.js +7314 -0
- package/dist/chunk-C5CDA3WX.js.map +1 -0
- package/dist/chunk-E3FD2CNY.js +1869 -0
- package/dist/chunk-E3FD2CNY.js.map +1 -0
- package/dist/{chunk-F3DZMBUA.js → chunk-G7S6B6WB.js} +327 -493
- package/dist/chunk-G7S6B6WB.js.map +1 -0
- package/dist/chunk-IHVDQ4YI.js +4231 -0
- package/dist/chunk-IHVDQ4YI.js.map +1 -0
- package/dist/chunk-JV2SWONF.js +98 -0
- package/dist/chunk-JV2SWONF.js.map +1 -0
- package/dist/chunk-KB6CKIUK.js +231 -0
- package/dist/chunk-KB6CKIUK.js.map +1 -0
- package/dist/chunk-S74EH3KD.js +7890 -0
- package/dist/chunk-S74EH3KD.js.map +1 -0
- package/dist/chunk-SFTSZ3LC.js +156 -0
- package/dist/chunk-SFTSZ3LC.js.map +1 -0
- package/dist/chunk-SKMJJ3Q6.js +125 -0
- package/dist/chunk-SKMJJ3Q6.js.map +1 -0
- package/dist/chunk-SZGFSCNU.js +3682 -0
- package/dist/chunk-SZGFSCNU.js.map +1 -0
- package/dist/chunk-TTB4OS2D.js +69 -0
- package/dist/chunk-TTB4OS2D.js.map +1 -0
- package/dist/chunk-UP2VWCW5.js +33 -0
- package/dist/{chunk-NSBPE2FW.js.map → chunk-UP2VWCW5.js.map} +1 -1
- package/dist/{chunk-UJUGGLMT.js → chunk-VQZQCHUT.js} +5 -5
- package/dist/{chunk-UJUGGLMT.js.map → chunk-VQZQCHUT.js.map} +1 -1
- package/dist/client.d.ts +5 -4
- package/dist/client.js +11 -10
- package/dist/createAgentAuthorization-ULG47ZJI.js +5 -0
- package/dist/createAgentAuthorization-ULG47ZJI.js.map +1 -0
- package/dist/credentials.js +1 -1
- package/dist/crypto.js +2 -2
- package/dist/errors.js +1 -1
- package/dist/feature-flags-B1g0DCPe.d.ts +1181 -0
- package/dist/generated-EG5USUFG.js +9 -0
- package/dist/{generated-VNLHMR6Y.js.map → generated-EG5USUFG.js.map} +1 -1
- package/dist/{ghostspeak_wasm-SB2RPJ3D.js → ghostspeak_wasm-F227HOSM.js} +3 -3
- package/dist/{ghostspeak_wasm-SB2RPJ3D.js.map → ghostspeak_wasm-F227HOSM.js.map} +1 -1
- package/dist/index.d.ts +1209 -1506
- package/dist/index.js +600 -3532
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/minimal/core-minimal.d.ts +2383 -1264
- package/dist/minimal/core-minimal.js +9 -9
- package/dist/minimal/core-minimal.js.map +1 -1
- package/dist/nacl-fast-W5BJ3KZ2.js +2229 -0
- package/dist/nacl-fast-W5BJ3KZ2.js.map +1 -0
- package/dist/pda-4KP7CURF.js +4 -0
- package/dist/pda-4KP7CURF.js.map +1 -0
- package/dist/pda-Ce7VYg4T.d.ts +25 -0
- package/dist/reputation-types-Yebf0Rm_.d.ts +1071 -0
- package/dist/revokeAuthorization-OK7E7OK3.js +5 -0
- package/dist/revokeAuthorization-OK7E7OK3.js.map +1 -0
- package/dist/signature-verification-DGxR4aYQ.d.ts +448 -0
- package/dist/types.js +1 -1
- package/dist/updateReputationWithAuth-Y4ONEVSP.js +5 -0
- package/dist/updateReputationWithAuth-Y4ONEVSP.js.map +1 -0
- package/dist/utils.d.ts +69 -203
- package/dist/utils.js +15 -153
- package/dist/utils.js.map +1 -1
- package/package.json +24 -31
- package/dist/.tsbuildinfo +0 -1
- package/dist/GhostSpeakClient-D_66Uzsf.d.ts +0 -707
- package/dist/GovernanceModule-DQYYys-H.d.ts +0 -1766
- package/dist/chunk-APCKGD23.js +0 -1328
- package/dist/chunk-APCKGD23.js.map +0 -1
- package/dist/chunk-ASQXX4IT.js +0 -572
- package/dist/chunk-ASQXX4IT.js.map +0 -1
- package/dist/chunk-COGZFWOT.js +0 -19657
- package/dist/chunk-COGZFWOT.js.map +0 -1
- package/dist/chunk-F3DZMBUA.js.map +0 -1
- package/dist/chunk-GMHIUK2R.js +0 -7526
- package/dist/chunk-GMHIUK2R.js.map +0 -1
- package/dist/chunk-IAWBZYPE.js +0 -356
- package/dist/chunk-IAWBZYPE.js.map +0 -1
- package/dist/chunk-NSBPE2FW.js +0 -15
- package/dist/chunk-OWYHJG6H.js +0 -13311
- package/dist/chunk-OWYHJG6H.js.map +0 -1
- package/dist/chunk-RDDPOFR5.js +0 -3
- package/dist/chunk-RDDPOFR5.js.map +0 -1
- package/dist/chunk-RERCHKZP.js +0 -35
- package/dist/chunk-RERCHKZP.js.map +0 -1
- package/dist/chunk-TVVGXYCI.js +0 -2887
- package/dist/chunk-TVVGXYCI.js.map +0 -1
- package/dist/chunk-ZGP5552B.js +0 -377
- package/dist/chunk-ZGP5552B.js.map +0 -1
- package/dist/chunk-ZWOYNHVK.js +0 -196
- package/dist/chunk-ZWOYNHVK.js.map +0 -1
- package/dist/dist/.tsbuildinfo +0 -1
- package/dist/elgamal-VZLWB3XK.js +0 -5
- package/dist/elgamal-VZLWB3XK.js.map +0 -1
- package/dist/feature-flags-V722ZuXO.d.ts +0 -3512
- package/dist/generated-VNLHMR6Y.js +0 -5
- package/dist/ipfs-types-BOt9ZNg4.d.ts +0 -592
- package/dist/multisigConfig-BzEhy6jy.d.ts +0 -58
- package/dist/pda-B_nS8SbD.d.ts +0 -114
- package/dist/pda-S4BFJVGE.js +0 -4
- package/dist/pda-S4BFJVGE.js.map +0 -1
- package/dist/system-addresses-BFNLEbFx.d.ts +0 -857
- package/dist/token-2022-rpc-RALH4RK7.js +0 -593
- package/dist/token-2022-rpc-RALH4RK7.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,32 +1,29 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
1
|
+
export { batchGetAccounts, batchGetAccountsWithRetry, batchGetAndMap, batchGetExistingAccounts, createBatchFetcher } from './chunk-SKMJJ3Q6.js';
|
|
2
|
+
export { BaseReputationAdapter, GHOSTSPEAK_PROGRAM_ID, ReputationSource } from './chunk-TTB4OS2D.js';
|
|
3
|
+
export { AuthorizationModule, DidModule, GhostSpeakClient, PrivacyModule, UnifiedCredentialService, calculateVisibleScore, canViewerAccess, GhostSpeakClient_default as default, filterMetricsByVisibility, getDefaultMetricVisibility, getRangeDisplayString, getReputationTier, getScoreRange, getTierDisplayName, lamportsToSol, sol, validatePrivacySettings } from './chunk-E3FD2CNY.js';
|
|
4
|
+
import { init_MultiSourceAggregator } from './chunk-SZGFSCNU.js';
|
|
5
|
+
export { AgentModule, BaseModule, CacheManager, CredentialKind, CredentialModule, CredentialStatus, DEFAULT_IPFS_CONFIG, DidError, DidErrorClass, GovernanceModule, IPFSUtils, InstructionBuilder, MultiSourceAggregator, MultisigModule, PayAIClient, ReputationModule, RpcClient, ServiceEndpointType, VerificationMethodType, VerificationRelationship, canPerformAction, createEd25519VerificationMethod, createIPFSUtils, createMetadataUri, createPayAIClient, createServiceEndpoint, deriveDidDocumentPda, determineStorageMethod, didDocumentToJson, exportAsW3CDidDocument, extractPaymentRequirements, generateDidString, getIdentifierFromDid, getMethodsForRelationship, getNetworkFromDid, isDidActive, isPaymentRequired, parseDidString, payAIFetch, validateDidString } from './chunk-SZGFSCNU.js';
|
|
4
6
|
export { AccountNotFoundError, ErrorFactory, ErrorHandler, GhostSpeakError, InsufficientBalanceError, InvalidInputError, NetworkError, SimulationFailedError, TimeoutError, TransactionFailedError, ValidationError } from './chunk-5DMB3UAV.js';
|
|
5
|
-
export { decrypt, elgamal_exports as elgamal, encrypt, generateKeypair, generateTransferProof, generateWithdrawProof, isWasmAvailable, loadWasmModule, wasm_bridge_exports as wasmBridge } from './chunk-
|
|
6
|
-
import { getFeatureFlags, ClientEncryptionService } from './chunk-
|
|
7
|
-
export { ClientEncryptionService, FeatureFlagManager,
|
|
8
|
-
|
|
9
|
-
export {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import './chunk-
|
|
17
|
-
|
|
18
|
-
export { AgentType, ChannelType, ErrorCode, EscrowStatus, MessageType, isError, isSuccess, unwrap } from './chunk-SRS2SKFS.js';
|
|
7
|
+
export { decrypt, elgamal_exports as elgamal, encrypt, generateKeypair, generateTransferProof, generateWithdrawProof, isWasmAvailable, loadWasmModule, wasm_bridge_exports as wasmBridge } from './chunk-VQZQCHUT.js';
|
|
8
|
+
import { getFeatureFlags, ClientEncryptionService } from './chunk-G7S6B6WB.js';
|
|
9
|
+
export { ClientEncryptionService, FeatureFlagManager, TokenExtension, TokenProgram, createDiscriminatorErrorMessage, createMigrationPlan, createMigrationReport, deriveAssociatedTokenAddress, deriveMultisigPda, deriveProposalPda, deriveSplTokenAssociatedTokenAddress, deriveToken2022AssociatedTokenAddress, detectTokenProgram, diagnoseAccountFromChain, diagnoseBatchFromChain, exportDiagnosticReport, extractLegacyData, formatTokenAmount, generateLocalPrivacyProof, getAllAssociatedTokenAddresses, getAssociatedTokenAccount, getConfidentialTransferConfig, getFeatureFlags, getInterestBearingConfig, getMigrationInstructions, getTokenProgramAddress, getTokenProgramFromAddress, getTokenProgramType, getTransferFeeConfig, hasConfidentialTransferExtension, hasInterestBearingExtension, hasTransferFeeExtension, inspectAccountData, isFeatureEnabled, isToken2022Mint, parseTokenAmount, runAccountDiagnostics, runBatchDiagnostics, safeDecodeAgent, simulateMigration, validateAccountDiscriminator, validateAssociatedTokenAddress, verifyLocalPrivacyProof } from './chunk-G7S6B6WB.js';
|
|
10
|
+
export { deriveAgentPda, deriveAgentVerificationPda, deriveUserRegistryPda, findProgramDerivedAddress } from './chunk-BF3IQ35I.js';
|
|
11
|
+
export { createAuthorizationMessage, createSignedAuthorization, deserializeAuthorization, generateNonce, getAuthorizationId, isAuthorizationExhausted, isAuthorizationExpired, serializeAuthorization, signAuthorizationMessage, validateAuthorizationNetwork, verifyAuthorizationSignature } from './chunk-BQDGRTVP.js';
|
|
12
|
+
import { init_reputation_tag_engine, init_reputation_tags } from './chunk-C5CDA3WX.js';
|
|
13
|
+
export { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, BadgeType, BehaviorTag, ComplianceTag, DEFAULT_TAG_DECAY, GhostSpeakSDKError, INSTRUCTION_MAPPINGS, IPFSClient, InstructionValidationError, NATIVE_MINT_ADDRESS, PRIVACY_CONSTANTS, PrivacyMode, PrivacyPresets, REPUTATION_CONSTANTS, ReputationTagEngine, ReputationTier, ScoreRange, SkillTag, TAG_CONSTANTS, TOKEN_2022_PROGRAM_ADDRESS, TOKEN_PROGRAM_ADDRESS, TagCategory, TagConfidenceLevel, VisibilityLevel, createAccountMismatchError, createErrorContext, debugInstructionCall, enhanceErrorMessage, enhanceTransactionError, extractInstructionName, generateAccountValidationError, getAccountRequirements, getInstructionMapping, getPDAAccounts, getRequiredSigners, getWritableAccounts, isKnownInstruction, logEnhancedError, validateInstructionAccounts, validatePreconditions, withEnhancedErrors, withEnhancedErrorsSync } from './chunk-C5CDA3WX.js';
|
|
14
|
+
export { getApproveDeliveryInstruction, getArbitrateDisputeInstruction, getCreateDidDocumentInstructionAsync, getCreateEscrowInstructionAsync, getDeactivateDidDocumentInstructionAsync, getFileDisputeInstruction, getGhostProtectEscrowDecoder, getInitializeStakingConfigInstructionAsync, getRegisterAgentInstructionAsync, getResolveDidDocumentInstructionAsync, getSlashStakeInstructionAsync, getStakeGhostInstructionAsync, getStakingAccountDecoder, getStakingConfigDecoder, getSubmitDeliveryInstruction, getUnstakeGhostInstructionAsync, getUpdateDidDocumentInstructionAsync, getUpdateReputationTagsInstructionAsync } from './chunk-S74EH3KD.js';
|
|
15
|
+
export { ProposalStatus, getAgentDecoder } from './chunk-IHVDQ4YI.js';
|
|
16
|
+
import './chunk-SFTSZ3LC.js';
|
|
17
|
+
import './chunk-JV2SWONF.js';
|
|
18
|
+
import './chunk-KB6CKIUK.js';
|
|
19
|
+
export { GHOSTSPEAK_MARKETPLACE_PROGRAM_ADDRESS } from './chunk-AL3HQN73.js';
|
|
20
|
+
export { AgentType, ChannelType, ErrorCode, EscrowStatus, MessageType, ParticipantType, isError, isSuccess, unwrap } from './chunk-SRS2SKFS.js';
|
|
19
21
|
export { CrossmintVCClient, GHOSTSPEAK_CREDENTIAL_TYPES } from './chunk-RIZZPLLB.js';
|
|
20
|
-
import {
|
|
21
|
-
import { createSolanaRpc, lamports, pipe, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, getBase64EncodedWireTransaction, createKeyPairSignerFromBytes, generateKeyPairSigner
|
|
22
|
+
import { __require } from './chunk-UP2VWCW5.js';
|
|
23
|
+
import { createSolanaRpc, lamports, pipe, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, getBase64EncodedWireTransaction, createKeyPairSignerFromBytes, generateKeyPairSigner } from '@solana/kit';
|
|
22
24
|
export { address, createKeyPairSignerFromBytes, createSolanaRpc, generateKeyPairSigner } from '@solana/kit';
|
|
23
|
-
import { address } from '@solana/addresses';
|
|
24
|
-
import { pipe as pipe$1 } from '@solana/functional';
|
|
25
25
|
import { EventEmitter } from 'events';
|
|
26
|
-
import {
|
|
27
|
-
import { drizzle } from 'drizzle-orm/libsql';
|
|
28
|
-
import { createClient } from '@libsql/client';
|
|
29
|
-
import { sqliteTable, integer, text, index, real } from 'drizzle-orm/sqlite-core';
|
|
26
|
+
import { timingSafeEqual, createHmac } from 'crypto';
|
|
30
27
|
import { getTransferSolInstruction } from '@solana-program/system';
|
|
31
28
|
import { promises } from 'fs';
|
|
32
29
|
import process2 from 'process';
|
|
@@ -35,3625 +32,696 @@ import tty from 'tty';
|
|
|
35
32
|
import bs58 from 'bs58';
|
|
36
33
|
import { sha256 } from '@noble/hashes/sha256';
|
|
37
34
|
import { bytesToHex, hexToBytes } from '@noble/curves/abstract/utils';
|
|
35
|
+
import { address } from '@solana/addresses';
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
const recipient = this.wallet.address;
|
|
54
|
-
return {
|
|
55
|
-
recipient,
|
|
56
|
-
amount: params.amount,
|
|
57
|
-
token: params.token,
|
|
58
|
-
description: params.description,
|
|
59
|
-
metadata: params.metadata,
|
|
60
|
-
expiresAt: params.expiresAt ?? Date.now() + 3e5,
|
|
61
|
-
// 5 minutes default
|
|
62
|
-
requiresReceipt: true
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Create HTTP 402 response headers
|
|
67
|
-
*/
|
|
68
|
-
createPaymentHeaders(request) {
|
|
69
|
-
const headers = {
|
|
70
|
-
"X-Payment-Address": request.recipient,
|
|
71
|
-
"X-Payment-Amount": request.amount.toString(),
|
|
72
|
-
"X-Payment-Token": request.token,
|
|
73
|
-
"X-Payment-Blockchain": "solana"
|
|
74
|
-
};
|
|
75
|
-
if (request.description) {
|
|
76
|
-
headers["X-Payment-Description"] = request.description;
|
|
77
|
-
}
|
|
78
|
-
if (request.expiresAt) {
|
|
79
|
-
headers["X-Payment-Expires-At"] = request.expiresAt.toString();
|
|
80
|
-
}
|
|
81
|
-
return headers;
|
|
37
|
+
init_reputation_tag_engine();
|
|
38
|
+
|
|
39
|
+
// src/modules/indexer/X402TransactionIndexer.ts
|
|
40
|
+
var X402TransactionIndexer = class {
|
|
41
|
+
rpc;
|
|
42
|
+
facilitatorAddress;
|
|
43
|
+
network;
|
|
44
|
+
batchSize;
|
|
45
|
+
constructor(config) {
|
|
46
|
+
this.rpc = config.rpc;
|
|
47
|
+
this.facilitatorAddress = config.facilitatorAddress;
|
|
48
|
+
this.network = config.network || "solana";
|
|
49
|
+
this.batchSize = config.batchSize || 100;
|
|
82
50
|
}
|
|
51
|
+
// =====================================================
|
|
52
|
+
// PUBLIC METHODS
|
|
53
|
+
// =====================================================
|
|
83
54
|
/**
|
|
84
|
-
*
|
|
55
|
+
* Poll for new x402 transactions since last sync
|
|
56
|
+
*
|
|
57
|
+
* @param lastSignature - Last processed signature (for pagination)
|
|
58
|
+
* @param limit - Maximum transactions to fetch
|
|
59
|
+
* @returns Array of parsed x402 payment data
|
|
85
60
|
*/
|
|
86
|
-
async
|
|
87
|
-
if (!this.wallet) {
|
|
88
|
-
throw new Error("Wallet required to make payment");
|
|
89
|
-
}
|
|
90
|
-
if (request.amount <= 0n) {
|
|
91
|
-
throw new Error("Payment amount must be greater than zero");
|
|
92
|
-
}
|
|
93
|
-
if (!request.recipient) {
|
|
94
|
-
throw new Error("Payment recipient address is required");
|
|
95
|
-
}
|
|
96
|
-
if (!request.token) {
|
|
97
|
-
throw new Error("Payment token address is required");
|
|
98
|
-
}
|
|
99
|
-
this.emit("payment_created", {
|
|
100
|
-
type: "payment_created",
|
|
101
|
-
request,
|
|
102
|
-
timestamp: Date.now()
|
|
103
|
-
});
|
|
61
|
+
async pollTransactions(lastSignature, limit) {
|
|
104
62
|
try {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
`Failed to fetch latest blockhash: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
120
|
-
);
|
|
121
|
-
});
|
|
122
|
-
const feePayerAddress = this.wallet.address;
|
|
123
|
-
const message = pipe$1(
|
|
124
|
-
createTransactionMessage({ version: 0 }),
|
|
125
|
-
(m) => setTransactionMessageFeePayer(feePayerAddress, m),
|
|
126
|
-
(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
|
|
127
|
-
(m) => appendTransactionMessageInstruction(transferIx, m),
|
|
128
|
-
(m) => appendTransactionMessageInstruction(memoIx, m)
|
|
129
|
-
);
|
|
130
|
-
const signedTransaction = await signTransactionMessageWithSigners(message).catch((error) => {
|
|
131
|
-
throw new Error(
|
|
132
|
-
`Failed to sign transaction: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
133
|
-
);
|
|
134
|
-
});
|
|
135
|
-
const signature = await this.sendAndConfirmTransactionManually(signedTransaction).catch((error) => {
|
|
136
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
137
|
-
if (errorMessage.includes("insufficient funds")) {
|
|
138
|
-
throw new Error("Insufficient funds to complete payment");
|
|
139
|
-
}
|
|
140
|
-
if (errorMessage.includes("Blockhash not found")) {
|
|
141
|
-
throw new Error("Transaction expired. Please retry the payment.");
|
|
142
|
-
}
|
|
143
|
-
if (errorMessage.includes("InvalidAccountOwner")) {
|
|
144
|
-
throw new Error(
|
|
145
|
-
"Associated Token Account not found or invalid. Please ensure the token account exists for both sender and recipient."
|
|
146
|
-
);
|
|
63
|
+
const signatures = await this.getSignatures(lastSignature, limit);
|
|
64
|
+
if (signatures.length === 0) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
console.log(`[X402 Indexer] Found ${signatures.length} new transactions`);
|
|
68
|
+
const payments = [];
|
|
69
|
+
for (const sig of signatures) {
|
|
70
|
+
try {
|
|
71
|
+
const payment = await this.parseTransaction(sig.signature);
|
|
72
|
+
if (payment) {
|
|
73
|
+
payments.push(payment);
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error(`[X402 Indexer] Failed to parse transaction ${sig.signature}:`, error);
|
|
147
77
|
}
|
|
148
|
-
throw new Error(
|
|
149
|
-
`Transaction failed: ${errorMessage}`
|
|
150
|
-
);
|
|
151
|
-
});
|
|
152
|
-
this.emit("payment_sent", {
|
|
153
|
-
type: "payment_sent",
|
|
154
|
-
signature,
|
|
155
|
-
request,
|
|
156
|
-
timestamp: Date.now()
|
|
157
|
-
});
|
|
158
|
-
const tx = await this.rpc.getTransaction(signature, {
|
|
159
|
-
encoding: "jsonParsed",
|
|
160
|
-
maxSupportedTransactionVersion: 0
|
|
161
|
-
}).send().catch((error) => {
|
|
162
|
-
console.warn("Payment succeeded but failed to fetch transaction details:", error);
|
|
163
|
-
return null;
|
|
164
|
-
});
|
|
165
|
-
const receipt = {
|
|
166
|
-
signature,
|
|
167
|
-
recipient: request.recipient,
|
|
168
|
-
amount: request.amount,
|
|
169
|
-
token: request.token,
|
|
170
|
-
timestamp: Date.now(),
|
|
171
|
-
metadata: request.metadata,
|
|
172
|
-
blockTime: tx?.blockTime ? Number(tx.blockTime) : void 0,
|
|
173
|
-
slot: tx?.slot ?? void 0
|
|
174
|
-
};
|
|
175
|
-
this.emit("payment_confirmed", {
|
|
176
|
-
type: "payment_confirmed",
|
|
177
|
-
signature,
|
|
178
|
-
request,
|
|
179
|
-
receipt,
|
|
180
|
-
timestamp: Date.now()
|
|
181
|
-
});
|
|
182
|
-
return receipt;
|
|
183
|
-
} catch (error) {
|
|
184
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
185
|
-
this.emit("payment_failed", {
|
|
186
|
-
type: "payment_failed",
|
|
187
|
-
request,
|
|
188
|
-
error: errorMessage,
|
|
189
|
-
timestamp: Date.now()
|
|
190
|
-
});
|
|
191
|
-
if (error instanceof Error) {
|
|
192
|
-
throw error;
|
|
193
78
|
}
|
|
194
|
-
|
|
79
|
+
console.log(`[X402 Indexer] Parsed ${payments.length} x402 payments`);
|
|
80
|
+
return payments;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error("[X402 Indexer] Failed to poll transactions:", error);
|
|
83
|
+
throw error;
|
|
195
84
|
}
|
|
196
85
|
}
|
|
197
86
|
/**
|
|
198
|
-
*
|
|
87
|
+
* Parse a specific transaction signature
|
|
88
|
+
*
|
|
89
|
+
* @param signature - Transaction signature to parse
|
|
90
|
+
* @returns Parsed x402 payment data or null if not an x402 payment
|
|
199
91
|
*/
|
|
200
|
-
async
|
|
92
|
+
async parseTransaction(sig) {
|
|
201
93
|
try {
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
maxSupportedTransactionVersion: 0
|
|
94
|
+
const txSignature = typeof sig === "string" ? sig : sig;
|
|
95
|
+
const response = await this.rpc.getTransaction(txSignature, {
|
|
96
|
+
maxSupportedTransactionVersion: 0,
|
|
97
|
+
encoding: "jsonParsed"
|
|
205
98
|
}).send();
|
|
206
|
-
if (!
|
|
207
|
-
return
|
|
208
|
-
valid: false,
|
|
209
|
-
error: "Transaction not found"
|
|
210
|
-
};
|
|
99
|
+
if (!response || !response.transaction) {
|
|
100
|
+
return null;
|
|
211
101
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
error: "Transaction failed"
|
|
216
|
-
};
|
|
102
|
+
const isX402 = this.isX402Payment(response);
|
|
103
|
+
if (!isX402) {
|
|
104
|
+
return null;
|
|
217
105
|
}
|
|
218
|
-
|
|
219
|
-
return {
|
|
220
|
-
valid: true,
|
|
221
|
-
receipt
|
|
222
|
-
};
|
|
106
|
+
return this.extractPaymentData(response, typeof sig === "string" ? sig : String(sig));
|
|
223
107
|
} catch (error) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Verify payment for specific recipient and amount
|
|
232
|
-
*/
|
|
233
|
-
async verifyPaymentDetails(params) {
|
|
234
|
-
const result = await this.verifyPayment(params.signature);
|
|
235
|
-
if (!result.valid || !result.receipt) {
|
|
236
|
-
return result;
|
|
237
|
-
}
|
|
238
|
-
if (result.receipt.recipient !== params.expectedRecipient) {
|
|
239
|
-
return {
|
|
240
|
-
valid: false,
|
|
241
|
-
error: "Recipient mismatch"
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
if (result.receipt.amount !== params.expectedAmount) {
|
|
245
|
-
return {
|
|
246
|
-
valid: false,
|
|
247
|
-
error: "Amount mismatch"
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
if (result.receipt.token !== params.expectedToken) {
|
|
251
|
-
return {
|
|
252
|
-
valid: false,
|
|
253
|
-
error: "Token mismatch"
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
valid: true,
|
|
258
|
-
receipt: result.receipt
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Get payment status
|
|
263
|
-
*/
|
|
264
|
-
async getPaymentStatus(signature) {
|
|
265
|
-
try {
|
|
266
|
-
const status = await this.rpc.getSignatureStatuses([signature]).send();
|
|
267
|
-
if (!status.value[0]) {
|
|
268
|
-
return { status: "not_found" };
|
|
269
|
-
}
|
|
270
|
-
const txStatus = status.value[0];
|
|
271
|
-
if (txStatus.err) {
|
|
272
|
-
return { status: "failed" };
|
|
273
|
-
}
|
|
274
|
-
if (txStatus.confirmationStatus === "finalized") {
|
|
275
|
-
return { status: "finalized", confirmations: 32 };
|
|
276
|
-
}
|
|
277
|
-
if (txStatus.confirmationStatus === "confirmed") {
|
|
278
|
-
return { status: "confirmed", confirmations: 1 };
|
|
279
|
-
}
|
|
280
|
-
return { status: "pending" };
|
|
281
|
-
} catch {
|
|
282
|
-
return { status: "not_found" };
|
|
108
|
+
console.error(`[X402 Indexer] Failed to fetch transaction ${sig}:`, error);
|
|
109
|
+
return null;
|
|
283
110
|
}
|
|
284
111
|
}
|
|
285
112
|
// =====================================================
|
|
286
|
-
// PRIVATE
|
|
113
|
+
// PRIVATE METHODS
|
|
287
114
|
// =====================================================
|
|
288
|
-
async createTransferInstruction(recipient, amount, token) {
|
|
289
|
-
if (!this.wallet) {
|
|
290
|
-
throw new Error("Wallet required");
|
|
291
|
-
}
|
|
292
|
-
const sourceAccount = await this.getAssociatedTokenAddress(
|
|
293
|
-
this.wallet.address,
|
|
294
|
-
token
|
|
295
|
-
);
|
|
296
|
-
const destinationAccount = await this.getAssociatedTokenAddress(
|
|
297
|
-
recipient,
|
|
298
|
-
token
|
|
299
|
-
);
|
|
300
|
-
const TOKEN_PROGRAM_ID = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
301
|
-
const keys = [
|
|
302
|
-
{ address: sourceAccount, role: 1, isWritable: true, isSigner: false },
|
|
303
|
-
{ address: destinationAccount, role: 1, isWritable: true, isSigner: false },
|
|
304
|
-
{ address: this.wallet.address, role: 2, isWritable: false, isSigner: true }
|
|
305
|
-
];
|
|
306
|
-
const data = new Uint8Array(9);
|
|
307
|
-
data[0] = 3;
|
|
308
|
-
const amountBytes = new BigUint64Array([amount]);
|
|
309
|
-
data.set(new Uint8Array(amountBytes.buffer), 1);
|
|
310
|
-
return {
|
|
311
|
-
programAddress: TOKEN_PROGRAM_ID,
|
|
312
|
-
accounts: keys,
|
|
313
|
-
data
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
createMemoInstruction(memo) {
|
|
317
|
-
const MEMO_PROGRAM_ID = address("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
|
|
318
|
-
return {
|
|
319
|
-
programAddress: MEMO_PROGRAM_ID,
|
|
320
|
-
accounts: [],
|
|
321
|
-
data: new TextEncoder().encode(memo)
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
async getAssociatedTokenAddress(owner, token) {
|
|
325
|
-
try {
|
|
326
|
-
const response = await this.rpc.getTokenAccountsByOwner(
|
|
327
|
-
owner,
|
|
328
|
-
{ mint: token },
|
|
329
|
-
{ encoding: "jsonParsed" }
|
|
330
|
-
).send();
|
|
331
|
-
if (response.value.length > 0) {
|
|
332
|
-
return response.value[0].pubkey;
|
|
333
|
-
}
|
|
334
|
-
throw new Error(
|
|
335
|
-
`Associated Token Account not found for owner ${owner} and mint ${token}. Please ensure the ATA is initialized before making payments. Use createAssociatedTokenAccountInstruction to create it.`
|
|
336
|
-
);
|
|
337
|
-
} catch (error) {
|
|
338
|
-
if (error instanceof Error && error.message.includes("Associated Token Account not found")) {
|
|
339
|
-
throw error;
|
|
340
|
-
}
|
|
341
|
-
throw new Error(
|
|
342
|
-
`Failed to get Associated Token Account: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
parseTransactionReceipt(tx, signature) {
|
|
347
|
-
const instructions = tx.transaction?.message?.instructions ?? [];
|
|
348
|
-
const transferInstruction = instructions.find((ix) => {
|
|
349
|
-
const parsed2 = typeof ix.parsed === "object" ? ix.parsed : null;
|
|
350
|
-
return ix.program === "spl-token" && parsed2?.type === "transfer";
|
|
351
|
-
});
|
|
352
|
-
if (!transferInstruction) {
|
|
353
|
-
throw new Error("No SPL token transfer found in transaction");
|
|
354
|
-
}
|
|
355
|
-
const parsed = typeof transferInstruction.parsed === "object" ? transferInstruction.parsed : null;
|
|
356
|
-
const transferInfo = parsed?.info;
|
|
357
|
-
if (!transferInfo) {
|
|
358
|
-
throw new Error("Failed to parse SPL token transfer instruction");
|
|
359
|
-
}
|
|
360
|
-
const recipient = address(transferInfo.destination ?? transferInfo.account ?? "");
|
|
361
|
-
const amount = BigInt(transferInfo.amount ?? transferInfo.tokenAmount?.amount ?? "0");
|
|
362
|
-
let tokenMint;
|
|
363
|
-
const tokenBalances = tx.meta?.preTokenBalances ?? [];
|
|
364
|
-
if (tokenBalances.length > 0) {
|
|
365
|
-
tokenMint = address(String(tokenBalances[0].mint));
|
|
366
|
-
} else {
|
|
367
|
-
throw new Error("Failed to extract token mint from transaction");
|
|
368
|
-
}
|
|
369
|
-
const memoInstruction = instructions.find((ix) => {
|
|
370
|
-
const programIdStr = typeof ix.programId === "string" ? ix.programId : ix.programId?.toString() ?? "";
|
|
371
|
-
return ix.program === "spl-memo" || programIdStr === "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr";
|
|
372
|
-
});
|
|
373
|
-
let metadata;
|
|
374
|
-
if (memoInstruction) {
|
|
375
|
-
try {
|
|
376
|
-
const parsedMemo = typeof memoInstruction.parsed === "string" ? memoInstruction.parsed : typeof memoInstruction.parsed === "object" ? memoInstruction.parsed?.data ?? "" : "";
|
|
377
|
-
const memoText = parsedMemo || (memoInstruction.data ? new TextDecoder().decode(Buffer.from(memoInstruction.data, "base64")) : "");
|
|
378
|
-
if (typeof memoText === "string" && memoText.startsWith("x402:")) {
|
|
379
|
-
const parts = memoText.split(":");
|
|
380
|
-
if (parts.length >= 3) {
|
|
381
|
-
try {
|
|
382
|
-
metadata = JSON.parse(parts[2]);
|
|
383
|
-
} catch {
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
} catch {
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
const blockTimeNum = tx.blockTime != null ? typeof tx.blockTime === "bigint" ? Number(tx.blockTime) : tx.blockTime : void 0;
|
|
391
|
-
return {
|
|
392
|
-
signature,
|
|
393
|
-
recipient,
|
|
394
|
-
amount,
|
|
395
|
-
token: tokenMint,
|
|
396
|
-
timestamp: blockTimeNum != null ? blockTimeNum * 1e3 : Date.now(),
|
|
397
|
-
metadata,
|
|
398
|
-
blockTime: blockTimeNum,
|
|
399
|
-
slot: tx.slot
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Send and confirm transaction manually (simplified implementation)
|
|
404
|
-
* This is a workaround for the missing rpcSubscriptions dependency
|
|
405
|
-
*/
|
|
406
|
-
async sendAndConfirmTransactionManually(signedTransaction) {
|
|
407
|
-
const signatures = Object.values(signedTransaction.signatures);
|
|
408
|
-
if (signatures.length === 0) {
|
|
409
|
-
throw new Error("Transaction has no signatures");
|
|
410
|
-
}
|
|
411
|
-
const firstSig = signatures[0];
|
|
412
|
-
if (!firstSig) {
|
|
413
|
-
throw new Error("Transaction signature is null");
|
|
414
|
-
}
|
|
415
|
-
const signature = firstSig;
|
|
416
|
-
const wireTransaction = getBase64EncodedWireTransaction(signedTransaction);
|
|
417
|
-
await this.rpc.sendTransaction(wireTransaction).send();
|
|
418
|
-
for (let i = 0; i < 30; i++) {
|
|
419
|
-
const status = await this.rpc.getSignatureStatuses([signature]).send();
|
|
420
|
-
if (status.value[0]?.confirmationStatus === "confirmed" || status.value[0]?.confirmationStatus === "finalized") {
|
|
421
|
-
return signature;
|
|
422
|
-
}
|
|
423
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
424
|
-
}
|
|
425
|
-
throw new Error("Transaction confirmation timeout");
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
function createX402Client(rpcUrl, wallet) {
|
|
429
|
-
const rpc = createSolanaRpc(rpcUrl);
|
|
430
|
-
return new X402Client(rpc, wallet);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// src/database/config.ts
|
|
434
|
-
var DEFAULT_CONFIG = {
|
|
435
|
-
mode: process.env.NODE_ENV === "production" ? "production" : "development",
|
|
436
|
-
pool: {
|
|
437
|
-
max: 10,
|
|
438
|
-
min: 2,
|
|
439
|
-
connectionTimeoutMs: 5e3,
|
|
440
|
-
idleTimeoutMs: 3e4
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
function getTursoConfig() {
|
|
444
|
-
const url = process.env.TURSO_DATABASE_URL ?? null;
|
|
445
|
-
const authToken = process.env.TURSO_AUTH_TOKEN ?? null;
|
|
446
|
-
const enabled = Boolean(url && authToken);
|
|
447
|
-
if (!enabled && (url ?? authToken)) {
|
|
448
|
-
console.warn(
|
|
449
|
-
"[GhostSpeak Database] Incomplete Turso configuration detected. Both TURSO_DATABASE_URL and TURSO_AUTH_TOKEN are required. Falling back to on-chain only mode."
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
return {
|
|
453
|
-
url,
|
|
454
|
-
authToken,
|
|
455
|
-
enabled,
|
|
456
|
-
...DEFAULT_CONFIG
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
function validateConfig(config) {
|
|
460
|
-
if (!config.enabled) {
|
|
461
|
-
return false;
|
|
462
|
-
}
|
|
463
|
-
if (!config.url || !config.authToken) {
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
if (!config.url.startsWith("libsql://") && !config.url.startsWith("http://") && !config.url.startsWith("https://")) {
|
|
467
|
-
console.error("[GhostSpeak Database] Invalid TURSO_DATABASE_URL format. Expected libsql://, http://, or https://");
|
|
468
|
-
return false;
|
|
469
|
-
}
|
|
470
|
-
return true;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// src/database/connection.ts
|
|
474
|
-
var DEFAULT_RETRY_CONFIG = {
|
|
475
|
-
maxRetries: 3,
|
|
476
|
-
initialDelayMs: 1e3,
|
|
477
|
-
maxDelayMs: 1e4,
|
|
478
|
-
backoffMultiplier: 2
|
|
479
|
-
};
|
|
480
|
-
var connectionState = {
|
|
481
|
-
client: null,
|
|
482
|
-
initialized: false,
|
|
483
|
-
healthy: false,
|
|
484
|
-
lastError: null,
|
|
485
|
-
retryCount: 0,
|
|
486
|
-
lastConnectAttempt: 0
|
|
487
|
-
};
|
|
488
|
-
function sleep(ms) {
|
|
489
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
490
|
-
}
|
|
491
|
-
function getRetryDelay(retryCount, config) {
|
|
492
|
-
const delay = Math.min(
|
|
493
|
-
config.initialDelayMs * Math.pow(config.backoffMultiplier, retryCount),
|
|
494
|
-
config.maxDelayMs
|
|
495
|
-
);
|
|
496
|
-
return delay;
|
|
497
|
-
}
|
|
498
|
-
async function initializeConnection(config, retryConfig = DEFAULT_RETRY_CONFIG) {
|
|
499
|
-
let lastError = null;
|
|
500
|
-
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
|
|
501
|
-
try {
|
|
502
|
-
connectionState.lastConnectAttempt = Date.now();
|
|
503
|
-
if (attempt > 0) {
|
|
504
|
-
const delay = getRetryDelay(attempt - 1, retryConfig);
|
|
505
|
-
console.log(`[GhostSpeak Database] Retrying connection (attempt ${attempt + 1}/${retryConfig.maxRetries + 1}) after ${delay}ms...`);
|
|
506
|
-
await sleep(delay);
|
|
507
|
-
}
|
|
508
|
-
const client = createClient({
|
|
509
|
-
url: config.url,
|
|
510
|
-
authToken: config.authToken
|
|
511
|
-
});
|
|
512
|
-
await client.execute("SELECT 1");
|
|
513
|
-
console.log("[GhostSpeak Database] Connection established successfully");
|
|
514
|
-
connectionState.retryCount = attempt;
|
|
515
|
-
connectionState.healthy = true;
|
|
516
|
-
connectionState.lastError = null;
|
|
517
|
-
return client;
|
|
518
|
-
} catch (error) {
|
|
519
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
520
|
-
console.error(`[GhostSpeak Database] Connection attempt ${attempt + 1} failed:`, lastError.message);
|
|
521
|
-
if (attempt === retryConfig.maxRetries) {
|
|
522
|
-
connectionState.healthy = false;
|
|
523
|
-
connectionState.lastError = lastError;
|
|
524
|
-
break;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
throw new Error(
|
|
529
|
-
`Failed to connect to Turso database after ${retryConfig.maxRetries + 1} attempts. Last error: ${lastError?.message ?? "Unknown error"}`
|
|
530
|
-
);
|
|
531
|
-
}
|
|
532
|
-
async function getConnection() {
|
|
533
|
-
const config = getTursoConfig();
|
|
534
|
-
if (!config.enabled) {
|
|
535
|
-
throw new Error(
|
|
536
|
-
"Turso database not configured. Set TURSO_DATABASE_URL and TURSO_AUTH_TOKEN environment variables."
|
|
537
|
-
);
|
|
538
|
-
}
|
|
539
|
-
if (!validateConfig(config)) {
|
|
540
|
-
throw new Error("Invalid Turso database configuration");
|
|
541
|
-
}
|
|
542
|
-
if (connectionState.client && connectionState.healthy) {
|
|
543
|
-
return connectionState.client;
|
|
544
|
-
}
|
|
545
|
-
if (!connectionState.initialized || !connectionState.healthy) {
|
|
546
|
-
connectionState.initialized = true;
|
|
547
|
-
connectionState.client = await initializeConnection(config);
|
|
548
|
-
}
|
|
549
|
-
return connectionState.client;
|
|
550
|
-
}
|
|
551
|
-
async function isAvailable() {
|
|
552
|
-
const config = getTursoConfig();
|
|
553
|
-
if (!config.enabled) {
|
|
554
|
-
return false;
|
|
555
|
-
}
|
|
556
|
-
try {
|
|
557
|
-
await getConnection();
|
|
558
|
-
return true;
|
|
559
|
-
} catch {
|
|
560
|
-
return false;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// src/database/schema/agents.ts
|
|
565
|
-
var agents_exports = {};
|
|
566
|
-
__export(agents_exports, {
|
|
567
|
-
agentCapabilities: () => agentCapabilities,
|
|
568
|
-
agentPricing: () => agentPricing,
|
|
569
|
-
agents: () => agents
|
|
570
|
-
});
|
|
571
|
-
var agents = sqliteTable("agents", {
|
|
572
|
-
// Primary key - Solana address
|
|
573
|
-
agentAddress: text("agent_address").primaryKey(),
|
|
574
|
-
// Basic info
|
|
575
|
-
owner: text("owner").notNull(),
|
|
576
|
-
name: text("name").notNull(),
|
|
577
|
-
description: text("description").notNull(),
|
|
578
|
-
// Reputation & stats
|
|
579
|
-
reputationScore: integer("reputation_score").notNull().default(0),
|
|
580
|
-
totalJobsCompleted: integer("total_jobs_completed").notNull().default(0),
|
|
581
|
-
totalEarnings: text("total_earnings").notNull().default("0"),
|
|
582
|
-
// Stored as text for u64
|
|
583
|
-
// Status
|
|
584
|
-
isActive: integer("is_active", { mode: "boolean" }).notNull().default(true),
|
|
585
|
-
isVerified: integer("is_verified", { mode: "boolean" }).notNull().default(false),
|
|
586
|
-
verificationTimestamp: integer("verification_timestamp").notNull().default(0),
|
|
587
|
-
// Pricing
|
|
588
|
-
originalPrice: text("original_price").notNull().default("0"),
|
|
589
|
-
replicationFee: text("replication_fee").notNull().default("0"),
|
|
590
|
-
// Configuration
|
|
591
|
-
genomeHash: text("genome_hash").notNull().default(""),
|
|
592
|
-
isReplicable: integer("is_replicable", { mode: "boolean" }).notNull().default(false),
|
|
593
|
-
serviceEndpoint: text("service_endpoint").notNull().default(""),
|
|
594
|
-
metadataUri: text("metadata_uri").notNull().default(""),
|
|
595
|
-
// Framework & lineage
|
|
596
|
-
frameworkOrigin: text("framework_origin").notNull().default(""),
|
|
597
|
-
cnftMint: text("cnft_mint"),
|
|
598
|
-
// Nullable
|
|
599
|
-
merkleTree: text("merkle_tree"),
|
|
600
|
-
// Nullable
|
|
601
|
-
supportsA2a: integer("supports_a2a", { mode: "boolean" }).notNull().default(false),
|
|
602
|
-
transferHook: text("transfer_hook"),
|
|
603
|
-
// Nullable
|
|
604
|
-
parentAgent: text("parent_agent"),
|
|
605
|
-
// Nullable
|
|
606
|
-
generation: integer("generation").notNull().default(0),
|
|
607
|
-
// x402 Payment Protocol
|
|
608
|
-
x402Enabled: integer("x402_enabled", { mode: "boolean" }).notNull().default(false),
|
|
609
|
-
x402PaymentAddress: text("x402_payment_address").notNull(),
|
|
610
|
-
x402PricePerCall: text("x402_price_per_call").notNull().default("0"),
|
|
611
|
-
x402ServiceEndpoint: text("x402_service_endpoint").notNull().default(""),
|
|
612
|
-
x402TotalPayments: text("x402_total_payments").notNull().default("0"),
|
|
613
|
-
x402TotalCalls: text("x402_total_calls").notNull().default("0"),
|
|
614
|
-
lastPaymentTimestamp: integer("last_payment_timestamp").notNull().default(0),
|
|
615
|
-
// API Schema
|
|
616
|
-
apiSpecUri: text("api_spec_uri").notNull().default(""),
|
|
617
|
-
apiVersion: text("api_version").notNull().default(""),
|
|
618
|
-
// Timestamps
|
|
619
|
-
createdAt: integer("created_at").notNull(),
|
|
620
|
-
updatedAt: integer("updated_at").notNull(),
|
|
621
|
-
cachedAt: integer("cached_at").notNull(),
|
|
622
|
-
// When cached from chain
|
|
623
|
-
// Bump seed for PDA
|
|
624
|
-
bump: integer("bump").notNull()
|
|
625
|
-
}, (table) => ({
|
|
626
|
-
// Indexes for common queries
|
|
627
|
-
ownerIdx: index("idx_agents_owner").on(table.owner),
|
|
628
|
-
x402EnabledIdx: index("idx_agents_x402_enabled").on(table.x402Enabled),
|
|
629
|
-
reputationIdx: index("idx_agents_reputation").on(table.reputationScore),
|
|
630
|
-
frameworkIdx: index("idx_agents_framework").on(table.frameworkOrigin),
|
|
631
|
-
verifiedIdx: index("idx_agents_verified").on(table.isVerified),
|
|
632
|
-
cachedAtIdx: index("idx_agents_cached_at").on(table.cachedAt)
|
|
633
|
-
}));
|
|
634
|
-
var agentCapabilities = sqliteTable("agent_capabilities", {
|
|
635
|
-
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
636
|
-
agentAddress: text("agent_address").notNull().references(() => agents.agentAddress, { onDelete: "cascade" }),
|
|
637
|
-
capability: text("capability").notNull()
|
|
638
|
-
}, (table) => ({
|
|
639
|
-
// Composite index for lookups
|
|
640
|
-
agentCapabilityIdx: index("idx_agent_capability").on(table.agentAddress, table.capability)
|
|
641
|
-
}));
|
|
642
|
-
var agentPricing = sqliteTable("agent_pricing", {
|
|
643
|
-
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
644
|
-
agentAddress: text("agent_address").notNull().references(() => agents.agentAddress, { onDelete: "cascade" }),
|
|
645
|
-
tokenAddress: text("token_address").notNull(),
|
|
646
|
-
// SPL token mint address
|
|
647
|
-
decimals: integer("decimals").notNull(),
|
|
648
|
-
symbol: text("symbol").notNull()
|
|
649
|
-
}, (table) => ({
|
|
650
|
-
// Index for token lookups
|
|
651
|
-
agentTokenIdx: index("idx_agent_token").on(table.agentAddress, table.tokenAddress)
|
|
652
|
-
}));
|
|
653
|
-
|
|
654
|
-
// src/database/schema/transactions.ts
|
|
655
|
-
var transactions_exports = {};
|
|
656
|
-
__export(transactions_exports, {
|
|
657
|
-
x402Transactions: () => x402Transactions
|
|
658
|
-
});
|
|
659
|
-
var x402Transactions = sqliteTable("x402_transactions", {
|
|
660
|
-
// Primary key - Solana transaction signature
|
|
661
|
-
signature: text("signature").primaryKey(),
|
|
662
|
-
// Addresses
|
|
663
|
-
agentAddress: text("agent_address").notNull(),
|
|
664
|
-
// Agent that received payment
|
|
665
|
-
payerAddress: text("payer_address").notNull(),
|
|
666
|
-
// Who paid
|
|
667
|
-
recipientAddress: text("recipient_address").notNull(),
|
|
668
|
-
// Payment recipient
|
|
669
|
-
// Payment details
|
|
670
|
-
amount: text("amount").notNull(),
|
|
671
|
-
// Amount in token's smallest unit (stored as text for bigint)
|
|
672
|
-
tokenMint: text("token_mint").notNull(),
|
|
673
|
-
// Token used for payment
|
|
674
|
-
tokenDecimals: integer("token_decimals").notNull(),
|
|
675
|
-
// Transaction status
|
|
676
|
-
status: text("status").notNull().default("confirmed"),
|
|
677
|
-
// 'confirmed', 'finalized', 'failed'
|
|
678
|
-
// Timing
|
|
679
|
-
blockTime: integer("block_time").notNull(),
|
|
680
|
-
// Unix timestamp from Solana
|
|
681
|
-
responseTimeMs: integer("response_time_ms"),
|
|
682
|
-
// API response time (nullable)
|
|
683
|
-
// Metadata
|
|
684
|
-
metadataHash: text("metadata_hash"),
|
|
685
|
-
// IPFS hash if applicable (nullable)
|
|
686
|
-
// Timestamps
|
|
687
|
-
createdAt: integer("created_at").notNull(),
|
|
688
|
-
// When indexed
|
|
689
|
-
updatedAt: integer("updated_at").notNull()
|
|
690
|
-
// Last update
|
|
691
|
-
}, (table) => ({
|
|
692
|
-
// Indexes for common queries
|
|
693
|
-
agentIdx: index("idx_tx_agent").on(table.agentAddress),
|
|
694
|
-
payerIdx: index("idx_tx_payer").on(table.payerAddress),
|
|
695
|
-
blockTimeIdx: index("idx_tx_block_time").on(table.blockTime),
|
|
696
|
-
statusIdx: index("idx_tx_status").on(table.status),
|
|
697
|
-
tokenIdx: index("idx_tx_token").on(table.tokenMint),
|
|
698
|
-
// Composite index for agent + time range queries
|
|
699
|
-
agentTimeIdx: index("idx_tx_agent_time").on(table.agentAddress, table.blockTime)
|
|
700
|
-
}));
|
|
701
|
-
|
|
702
|
-
// src/database/schema/analytics.ts
|
|
703
|
-
var analytics_exports = {};
|
|
704
|
-
__export(analytics_exports, {
|
|
705
|
-
agentAnalytics: () => agentAnalytics,
|
|
706
|
-
dailyMetrics: () => dailyMetrics,
|
|
707
|
-
marketAnalytics: () => marketAnalytics
|
|
708
|
-
});
|
|
709
|
-
var agentAnalytics = sqliteTable("agent_analytics", {
|
|
710
|
-
// Primary key - agent address
|
|
711
|
-
agentAddress: text("agent_address").primaryKey(),
|
|
712
|
-
// Revenue metrics
|
|
713
|
-
totalRevenue: text("total_revenue").notNull().default("0"),
|
|
714
|
-
// Stored as text for bigint
|
|
715
|
-
// Transaction metrics
|
|
716
|
-
totalTransactions: integer("total_transactions").notNull().default(0),
|
|
717
|
-
successfulTransactions: integer("successful_transactions").notNull().default(0),
|
|
718
|
-
successRate: real("success_rate").notNull().default(0),
|
|
719
|
-
// Percentage 0-100
|
|
720
|
-
// Quality metrics
|
|
721
|
-
averageRating: real("average_rating").notNull().default(0),
|
|
722
|
-
averageResponseTimeMs: integer("average_response_time_ms").notNull().default(0),
|
|
723
|
-
// Activity
|
|
724
|
-
lastTransactionAt: integer("last_transaction_at"),
|
|
725
|
-
// Unix timestamp (nullable)
|
|
726
|
-
// Timestamp
|
|
727
|
-
updatedAt: integer("updated_at").notNull()
|
|
728
|
-
}, (table) => ({
|
|
729
|
-
updatedAtIdx: index("idx_agent_analytics_updated").on(table.updatedAt)
|
|
730
|
-
}));
|
|
731
|
-
var marketAnalytics = sqliteTable("market_analytics", {
|
|
732
|
-
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
733
|
-
// Date for this snapshot
|
|
734
|
-
metricDate: text("metric_date").notNull().unique(),
|
|
735
|
-
// ISO date string (YYYY-MM-DD)
|
|
736
|
-
// Volume metrics
|
|
737
|
-
totalVolume: text("total_volume").notNull().default("0"),
|
|
738
|
-
// Stored as text for bigint
|
|
739
|
-
totalTransactions: integer("total_transactions").notNull().default(0),
|
|
740
|
-
// Agent metrics
|
|
741
|
-
activeAgentsCount: integer("active_agents_count").notNull().default(0),
|
|
742
|
-
// Price metrics
|
|
743
|
-
averagePrice: text("average_price").notNull().default("0"),
|
|
744
|
-
// Average transaction amount
|
|
745
|
-
// User metrics
|
|
746
|
-
uniquePayers: integer("unique_payers").notNull().default(0),
|
|
747
|
-
// Timestamp
|
|
748
|
-
updatedAt: integer("updated_at").notNull()
|
|
749
|
-
}, (table) => ({
|
|
750
|
-
dateIdx: index("idx_market_analytics_date").on(table.metricDate)
|
|
751
|
-
}));
|
|
752
|
-
var dailyMetrics = sqliteTable("daily_metrics", {
|
|
753
|
-
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
754
|
-
// Date and metric type
|
|
755
|
-
metricDate: text("metric_date").notNull(),
|
|
756
|
-
// ISO date (YYYY-MM-DD)
|
|
757
|
-
metricType: text("metric_type").notNull(),
|
|
758
|
-
// 'agent_revenue', 'market_volume', etc.
|
|
759
|
-
// Optional agent address (null for market-wide metrics)
|
|
760
|
-
agentAddress: text("agent_address"),
|
|
761
|
-
// Nullable
|
|
762
|
-
// Metric value and metadata
|
|
763
|
-
value: text("value").notNull(),
|
|
764
|
-
// Metric value as text (supports bigint)
|
|
765
|
-
metadata: text("metadata"),
|
|
766
|
-
// JSON string for additional data (nullable)
|
|
767
|
-
// Timestamp
|
|
768
|
-
createdAt: integer("created_at").notNull()
|
|
769
|
-
}, (table) => ({
|
|
770
|
-
// Composite index for date + type queries
|
|
771
|
-
dateTypeIdx: index("idx_daily_metrics_date_type").on(table.metricDate, table.metricType),
|
|
772
|
-
// Index for agent-specific queries
|
|
773
|
-
agentIdx: index("idx_daily_metrics_agent").on(table.agentAddress),
|
|
774
|
-
// Index for time-series queries
|
|
775
|
-
dateIdx: index("idx_daily_metrics_date").on(table.metricDate)
|
|
776
|
-
}));
|
|
777
|
-
|
|
778
|
-
// src/database/schema/facilitators.ts
|
|
779
|
-
var facilitators_exports = {};
|
|
780
|
-
__export(facilitators_exports, {
|
|
781
|
-
facilitatorApiKeys: () => facilitatorApiKeys,
|
|
782
|
-
facilitatorHealthHistory: () => facilitatorHealthHistory,
|
|
783
|
-
facilitators: () => facilitators
|
|
784
|
-
});
|
|
785
|
-
var facilitators = sqliteTable(
|
|
786
|
-
"facilitators",
|
|
787
|
-
{
|
|
788
|
-
// Primary key - unique facilitator ID
|
|
789
|
-
id: text("id").primaryKey(),
|
|
790
|
-
// Basic info
|
|
791
|
-
name: text("name").notNull(),
|
|
792
|
-
description: text("description"),
|
|
793
|
-
logo: text("logo"),
|
|
794
|
-
website: text("website"),
|
|
795
|
-
// Network configuration (JSON serialized)
|
|
796
|
-
networks: text("networks").notNull(),
|
|
797
|
-
// JSON array of Network enum values
|
|
798
|
-
// Address configuration (JSON serialized)
|
|
799
|
-
addresses: text("addresses").notNull(),
|
|
800
|
-
// JSON Record<Network, FacilitatorAddress[]>
|
|
801
|
-
// Endpoints
|
|
802
|
-
discoveryUrl: text("discovery_url"),
|
|
803
|
-
settleUrl: text("settle_url").notNull(),
|
|
804
|
-
verifyUrl: text("verify_url").notNull(),
|
|
805
|
-
// API configuration
|
|
806
|
-
requiresApiKey: integer("requires_api_key", { mode: "boolean" }).notNull().default(false),
|
|
807
|
-
apiKeyHeader: text("api_key_header"),
|
|
808
|
-
// Status
|
|
809
|
-
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
810
|
-
// Health metrics
|
|
811
|
-
lastHealthCheck: integer("last_health_check"),
|
|
812
|
-
// Unix timestamp
|
|
813
|
-
healthStatus: text("health_status").default("unknown"),
|
|
814
|
-
// healthy, degraded, unhealthy, unknown
|
|
815
|
-
latencyMs: real("latency_ms"),
|
|
816
|
-
uptimePercent: real("uptime_percent"),
|
|
817
|
-
totalRequests: integer("total_requests").notNull().default(0),
|
|
818
|
-
successfulRequests: integer("successful_requests").notNull().default(0),
|
|
819
|
-
failedRequests: integer("failed_requests").notNull().default(0),
|
|
820
|
-
// Timestamps
|
|
821
|
-
createdAt: integer("created_at").notNull().default(0),
|
|
822
|
-
updatedAt: integer("updated_at").notNull().default(0)
|
|
823
|
-
},
|
|
824
|
-
(table) => ({
|
|
825
|
-
enabledIdx: index("facilitators_enabled_idx").on(table.enabled),
|
|
826
|
-
healthStatusIdx: index("facilitators_health_status_idx").on(table.healthStatus)
|
|
827
|
-
})
|
|
828
|
-
);
|
|
829
|
-
var facilitatorApiKeys = sqliteTable(
|
|
830
|
-
"facilitator_api_keys",
|
|
831
|
-
{
|
|
832
|
-
// Composite key
|
|
833
|
-
id: text("id").primaryKey(),
|
|
834
|
-
// UUID or generated
|
|
835
|
-
// Foreign keys
|
|
836
|
-
facilitatorId: text("facilitator_id").notNull().references(() => facilitators.id),
|
|
837
|
-
userId: text("user_id").notNull(),
|
|
838
|
-
// User or wallet address
|
|
839
|
-
// Key storage (encrypted)
|
|
840
|
-
encryptedApiKey: text("encrypted_api_key").notNull(),
|
|
841
|
-
keyLabel: text("key_label"),
|
|
842
|
-
// User-friendly label
|
|
843
|
-
// Metadata
|
|
844
|
-
lastUsed: integer("last_used"),
|
|
845
|
-
// Unix timestamp
|
|
846
|
-
usageCount: integer("usage_count").notNull().default(0),
|
|
847
|
-
// Timestamps
|
|
848
|
-
createdAt: integer("created_at").notNull().default(0),
|
|
849
|
-
updatedAt: integer("updated_at").notNull().default(0)
|
|
850
|
-
},
|
|
851
|
-
(table) => ({
|
|
852
|
-
facilitatorUserIdx: index("api_keys_facilitator_user_idx").on(
|
|
853
|
-
table.facilitatorId,
|
|
854
|
-
table.userId
|
|
855
|
-
)
|
|
856
|
-
})
|
|
857
|
-
);
|
|
858
|
-
var facilitatorHealthHistory = sqliteTable(
|
|
859
|
-
"facilitator_health_history",
|
|
860
|
-
{
|
|
861
|
-
// Primary key
|
|
862
|
-
id: text("id").primaryKey(),
|
|
863
|
-
// UUID
|
|
864
|
-
// Foreign key
|
|
865
|
-
facilitatorId: text("facilitator_id").notNull().references(() => facilitators.id),
|
|
866
|
-
// Health check result
|
|
867
|
-
status: text("status").notNull(),
|
|
868
|
-
// healthy, degraded, unhealthy
|
|
869
|
-
latencyMs: real("latency_ms").notNull(),
|
|
870
|
-
errorMessage: text("error_message"),
|
|
871
|
-
// Network-specific results (JSON serialized)
|
|
872
|
-
networkResults: text("network_results"),
|
|
873
|
-
// JSON array of network health
|
|
874
|
-
// Timestamp
|
|
875
|
-
checkedAt: integer("checked_at").notNull()
|
|
876
|
-
},
|
|
877
|
-
(table) => ({
|
|
878
|
-
facilitatorTimeIdx: index("health_history_facilitator_time_idx").on(
|
|
879
|
-
table.facilitatorId,
|
|
880
|
-
table.checkedAt
|
|
881
|
-
)
|
|
882
|
-
})
|
|
883
|
-
);
|
|
884
|
-
|
|
885
|
-
// src/database/schema/resources.ts
|
|
886
|
-
var resources_exports = {};
|
|
887
|
-
__export(resources_exports, {
|
|
888
|
-
resourceAccepts: () => resourceAccepts,
|
|
889
|
-
resourceOrigins: () => resourceOrigins,
|
|
890
|
-
resourcePingHistory: () => resourcePingHistory,
|
|
891
|
-
resourceTags: () => resourceTags,
|
|
892
|
-
resources: () => resources
|
|
893
|
-
});
|
|
894
|
-
var resourceOrigins = sqliteTable(
|
|
895
|
-
"resource_origins",
|
|
896
|
-
{
|
|
897
|
-
// Primary key - UUID
|
|
898
|
-
id: text("id").primaryKey(),
|
|
899
|
-
// Origin URL (e.g., https://api.example.com)
|
|
900
|
-
origin: text("origin").notNull().unique(),
|
|
901
|
-
// Metadata from scraping
|
|
902
|
-
name: text("name"),
|
|
903
|
-
description: text("description"),
|
|
904
|
-
faviconUrl: text("favicon_url"),
|
|
905
|
-
ogImageUrl: text("og_image_url"),
|
|
906
|
-
ogTitle: text("og_title"),
|
|
907
|
-
ogDescription: text("og_description"),
|
|
908
|
-
// Contact/attribution
|
|
909
|
-
contactEmail: text("contact_email"),
|
|
910
|
-
twitterHandle: text("twitter_handle"),
|
|
911
|
-
githubRepo: text("github_repo"),
|
|
912
|
-
// Verification
|
|
913
|
-
isVerified: integer("is_verified", { mode: "boolean" }).notNull().default(false),
|
|
914
|
-
verifiedAt: integer("verified_at"),
|
|
915
|
-
verifiedBy: text("verified_by"),
|
|
916
|
-
// Timestamps
|
|
917
|
-
createdAt: integer("created_at").notNull().default(0),
|
|
918
|
-
updatedAt: integer("updated_at").notNull().default(0),
|
|
919
|
-
lastScrapedAt: integer("last_scraped_at")
|
|
920
|
-
},
|
|
921
|
-
(table) => ({
|
|
922
|
-
originIdx: index("resource_origins_origin_idx").on(table.origin)
|
|
923
|
-
})
|
|
924
|
-
);
|
|
925
|
-
var resources = sqliteTable(
|
|
926
|
-
"resources",
|
|
927
|
-
{
|
|
928
|
-
// Primary key - UUID
|
|
929
|
-
id: text("id").primaryKey(),
|
|
930
|
-
// Resource URL (full endpoint URL)
|
|
931
|
-
url: text("url").notNull().unique(),
|
|
932
|
-
// Type of resource
|
|
933
|
-
type: text("type").notNull().default("http"),
|
|
934
|
-
// http, websocket, grpc
|
|
935
|
-
// x402 protocol version
|
|
936
|
-
x402Version: text("x402_version"),
|
|
937
|
-
// Foreign keys
|
|
938
|
-
originId: text("origin_id").references(() => resourceOrigins.id),
|
|
939
|
-
facilitatorId: text("facilitator_id").references(() => facilitators.id),
|
|
940
|
-
// Payment requirements (JSON serialized PaymentRequirement[])
|
|
941
|
-
accepts: text("accepts").notNull(),
|
|
942
|
-
// JSON array
|
|
943
|
-
// Pricing
|
|
944
|
-
maxAmount: text("max_amount"),
|
|
945
|
-
// bigint as string
|
|
946
|
-
minAmount: text("min_amount"),
|
|
947
|
-
// bigint as string
|
|
948
|
-
currency: text("currency").default("USDC"),
|
|
949
|
-
// Enhanced schema for AI integration (JSON Schema)
|
|
950
|
-
inputSchema: text("input_schema"),
|
|
951
|
-
// JSON Schema for input
|
|
952
|
-
outputSchema: text("output_schema"),
|
|
953
|
-
// JSON Schema for output
|
|
954
|
-
examplesJson: text("examples_json"),
|
|
955
|
-
// JSON array of { input, output }
|
|
956
|
-
// Metadata
|
|
957
|
-
name: text("name"),
|
|
958
|
-
description: text("description"),
|
|
959
|
-
tags: text("tags"),
|
|
960
|
-
// JSON array of strings
|
|
961
|
-
capabilities: text("capabilities"),
|
|
962
|
-
// JSON array of capabilities
|
|
963
|
-
category: text("category"),
|
|
964
|
-
// AI category
|
|
965
|
-
// HTTP metadata
|
|
966
|
-
httpMethod: text("http_method").default("POST"),
|
|
967
|
-
contentType: text("content_type").default("application/json"),
|
|
968
|
-
authType: text("auth_type"),
|
|
969
|
-
// none, bearer, api_key, x402
|
|
970
|
-
// Status
|
|
971
|
-
isActive: integer("is_active", { mode: "boolean" }).notNull().default(true),
|
|
972
|
-
isVerified: integer("is_verified", { mode: "boolean" }).notNull().default(false),
|
|
973
|
-
lastPingAt: integer("last_ping_at"),
|
|
974
|
-
lastPingStatus: integer("last_ping_status"),
|
|
975
|
-
// HTTP status code
|
|
976
|
-
lastPingLatencyMs: real("last_ping_latency_ms"),
|
|
977
|
-
consecutiveFailures: integer("consecutive_failures").notNull().default(0),
|
|
978
|
-
// Discovery source
|
|
979
|
-
discoveredFrom: text("discovered_from"),
|
|
980
|
-
// facilitator_id or 'manual'
|
|
981
|
-
discoveredAt: integer("discovered_at"),
|
|
982
|
-
// AI labeling
|
|
983
|
-
aiGeneratedTags: text("ai_generated_tags"),
|
|
984
|
-
// JSON array
|
|
985
|
-
aiLabeledAt: integer("ai_labeled_at"),
|
|
986
|
-
// Timestamps
|
|
987
|
-
createdAt: integer("created_at").notNull().default(0),
|
|
988
|
-
updatedAt: integer("updated_at").notNull().default(0)
|
|
989
|
-
},
|
|
990
|
-
(table) => ({
|
|
991
|
-
originIdx: index("resources_origin_idx").on(table.originId),
|
|
992
|
-
facilitatorIdx: index("resources_facilitator_idx").on(table.facilitatorId),
|
|
993
|
-
activeIdx: index("resources_active_idx").on(table.isActive),
|
|
994
|
-
categoryIdx: index("resources_category_idx").on(table.category)
|
|
995
|
-
})
|
|
996
|
-
);
|
|
997
|
-
var resourceTags = sqliteTable(
|
|
998
|
-
"resource_tags",
|
|
999
|
-
{
|
|
1000
|
-
// Composite key
|
|
1001
|
-
id: text("id").primaryKey(),
|
|
1002
|
-
// UUID
|
|
1003
|
-
// Foreign key
|
|
1004
|
-
resourceId: text("resource_id").notNull().references(() => resources.id),
|
|
1005
|
-
// Tag
|
|
1006
|
-
tag: text("tag").notNull(),
|
|
1007
|
-
isAiGenerated: integer("is_ai_generated", { mode: "boolean" }).notNull().default(false),
|
|
1008
|
-
// Timestamps
|
|
1009
|
-
createdAt: integer("created_at").notNull().default(0)
|
|
1010
|
-
},
|
|
1011
|
-
(table) => ({
|
|
1012
|
-
resourceIdx: index("resource_tags_resource_idx").on(table.resourceId),
|
|
1013
|
-
tagIdx: index("resource_tags_tag_idx").on(table.tag)
|
|
1014
|
-
})
|
|
1015
|
-
);
|
|
1016
|
-
var resourcePingHistory = sqliteTable(
|
|
1017
|
-
"resource_ping_history",
|
|
1018
|
-
{
|
|
1019
|
-
// Primary key - UUID
|
|
1020
|
-
id: text("id").primaryKey(),
|
|
1021
|
-
// Foreign key
|
|
1022
|
-
resourceId: text("resource_id").notNull().references(() => resources.id),
|
|
1023
|
-
// Ping result
|
|
1024
|
-
statusCode: integer("status_code"),
|
|
1025
|
-
latencyMs: real("latency_ms"),
|
|
1026
|
-
success: integer("success", { mode: "boolean" }).notNull(),
|
|
1027
|
-
errorMessage: text("error_message"),
|
|
1028
|
-
// x402 response validation
|
|
1029
|
-
hasValidX402: integer("has_valid_x402", { mode: "boolean" }),
|
|
1030
|
-
x402ParseError: text("x402_parse_error"),
|
|
1031
|
-
// Response details
|
|
1032
|
-
responseBodySize: integer("response_body_size"),
|
|
1033
|
-
contentType: text("content_type"),
|
|
1034
|
-
// Timestamp
|
|
1035
|
-
pingedAt: integer("pinged_at").notNull()
|
|
1036
|
-
},
|
|
1037
|
-
(table) => ({
|
|
1038
|
-
resourceTimeIdx: index("ping_history_resource_time_idx").on(
|
|
1039
|
-
table.resourceId,
|
|
1040
|
-
table.pingedAt
|
|
1041
|
-
)
|
|
1042
|
-
})
|
|
1043
|
-
);
|
|
1044
|
-
var resourceAccepts = sqliteTable(
|
|
1045
|
-
"resource_accepts",
|
|
1046
|
-
{
|
|
1047
|
-
// Primary key - UUID
|
|
1048
|
-
id: text("id").primaryKey(),
|
|
1049
|
-
// Foreign key
|
|
1050
|
-
resourceId: text("resource_id").notNull().references(() => resources.id),
|
|
1051
|
-
// Payment requirement fields
|
|
1052
|
-
scheme: text("scheme").notNull(),
|
|
1053
|
-
// exact, upto, base, tiered
|
|
1054
|
-
network: text("network").notNull(),
|
|
1055
|
-
// solana, base, polygon
|
|
1056
|
-
maxAmountRequired: text("max_amount_required").notNull(),
|
|
1057
|
-
// bigint as string
|
|
1058
|
-
payTo: text("pay_to").notNull(),
|
|
1059
|
-
// facilitator address
|
|
1060
|
-
asset: text("asset").notNull(),
|
|
1061
|
-
// token address
|
|
1062
|
-
maxTimeoutSeconds: integer("max_timeout_seconds"),
|
|
1063
|
-
description: text("description"),
|
|
1064
|
-
mimeType: text("mime_type"),
|
|
1065
|
-
// Extra fields (JSON)
|
|
1066
|
-
extra: text("extra"),
|
|
1067
|
-
// Timestamps
|
|
1068
|
-
createdAt: integer("created_at").notNull().default(0),
|
|
1069
|
-
updatedAt: integer("updated_at").notNull().default(0)
|
|
1070
|
-
},
|
|
1071
|
-
(table) => ({
|
|
1072
|
-
resourceIdx: index("resource_accepts_resource_idx").on(table.resourceId),
|
|
1073
|
-
networkIdx: index("resource_accepts_network_idx").on(table.network),
|
|
1074
|
-
assetIdx: index("resource_accepts_asset_idx").on(table.asset)
|
|
1075
|
-
})
|
|
1076
|
-
);
|
|
1077
|
-
|
|
1078
|
-
// src/database/schema/resourceMetrics.ts
|
|
1079
|
-
var resourceMetrics_exports = {};
|
|
1080
|
-
__export(resourceMetrics_exports, {
|
|
1081
|
-
TIME_WINDOWS: () => TIME_WINDOWS,
|
|
1082
|
-
globalMetrics: () => globalMetrics,
|
|
1083
|
-
resourceMetrics: () => resourceMetrics,
|
|
1084
|
-
resourceOriginMetrics: () => resourceOriginMetrics
|
|
1085
|
-
});
|
|
1086
|
-
var TIME_WINDOWS = ["1h", "6h", "24h", "3d", "7d", "15d", "30d", "all"];
|
|
1087
|
-
var resourceMetrics = sqliteTable(
|
|
1088
|
-
"resource_metrics",
|
|
1089
|
-
{
|
|
1090
|
-
// Primary key
|
|
1091
|
-
id: text("id").primaryKey(),
|
|
1092
|
-
// Foreign key to resource
|
|
1093
|
-
resourceId: text("resource_id").notNull().references(() => resources.id),
|
|
1094
|
-
// Snapshot timestamp
|
|
1095
|
-
createdAt: integer("created_at").notNull(),
|
|
1096
|
-
// =========================================================
|
|
1097
|
-
// REQUEST COUNTS (8 time windows each)
|
|
1098
|
-
// =========================================================
|
|
1099
|
-
// Total requests
|
|
1100
|
-
totalRequests1h: integer("total_requests_1h").notNull().default(0),
|
|
1101
|
-
totalRequests6h: integer("total_requests_6h").notNull().default(0),
|
|
1102
|
-
totalRequests24h: integer("total_requests_24h").notNull().default(0),
|
|
1103
|
-
totalRequests3d: integer("total_requests_3d").notNull().default(0),
|
|
1104
|
-
totalRequests7d: integer("total_requests_7d").notNull().default(0),
|
|
1105
|
-
totalRequests15d: integer("total_requests_15d").notNull().default(0),
|
|
1106
|
-
totalRequests30d: integer("total_requests_30d").notNull().default(0),
|
|
1107
|
-
totalRequestsAll: integer("total_requests_all").notNull().default(0),
|
|
1108
|
-
// Successful requests (2xx)
|
|
1109
|
-
successCount1h: integer("success_count_1h").notNull().default(0),
|
|
1110
|
-
successCount6h: integer("success_count_6h").notNull().default(0),
|
|
1111
|
-
successCount24h: integer("success_count_24h").notNull().default(0),
|
|
1112
|
-
successCount3d: integer("success_count_3d").notNull().default(0),
|
|
1113
|
-
successCount7d: integer("success_count_7d").notNull().default(0),
|
|
1114
|
-
successCount15d: integer("success_count_15d").notNull().default(0),
|
|
1115
|
-
successCount30d: integer("success_count_30d").notNull().default(0),
|
|
1116
|
-
successCountAll: integer("success_count_all").notNull().default(0),
|
|
1117
|
-
// Failed requests (4xx, 5xx)
|
|
1118
|
-
failureCount1h: integer("failure_count_1h").notNull().default(0),
|
|
1119
|
-
failureCount6h: integer("failure_count_6h").notNull().default(0),
|
|
1120
|
-
failureCount24h: integer("failure_count_24h").notNull().default(0),
|
|
1121
|
-
failureCount3d: integer("failure_count_3d").notNull().default(0),
|
|
1122
|
-
failureCount7d: integer("failure_count_7d").notNull().default(0),
|
|
1123
|
-
failureCount15d: integer("failure_count_15d").notNull().default(0),
|
|
1124
|
-
failureCount30d: integer("failure_count_30d").notNull().default(0),
|
|
1125
|
-
failureCountAll: integer("failure_count_all").notNull().default(0),
|
|
1126
|
-
// =========================================================
|
|
1127
|
-
// LATENCY PERCENTILES (p50, p90, p99 for each window)
|
|
1128
|
-
// =========================================================
|
|
1129
|
-
// P50 (median) latency in ms
|
|
1130
|
-
latencyP50_1h: real("latency_p50_1h"),
|
|
1131
|
-
latencyP50_6h: real("latency_p50_6h"),
|
|
1132
|
-
latencyP50_24h: real("latency_p50_24h"),
|
|
1133
|
-
latencyP50_3d: real("latency_p50_3d"),
|
|
1134
|
-
latencyP50_7d: real("latency_p50_7d"),
|
|
1135
|
-
latencyP50_15d: real("latency_p50_15d"),
|
|
1136
|
-
latencyP50_30d: real("latency_p50_30d"),
|
|
1137
|
-
latencyP50All: real("latency_p50_all"),
|
|
1138
|
-
// P90 latency in ms
|
|
1139
|
-
latencyP90_1h: real("latency_p90_1h"),
|
|
1140
|
-
latencyP90_6h: real("latency_p90_6h"),
|
|
1141
|
-
latencyP90_24h: real("latency_p90_24h"),
|
|
1142
|
-
latencyP90_3d: real("latency_p90_3d"),
|
|
1143
|
-
latencyP90_7d: real("latency_p90_7d"),
|
|
1144
|
-
latencyP90_15d: real("latency_p90_15d"),
|
|
1145
|
-
latencyP90_30d: real("latency_p90_30d"),
|
|
1146
|
-
latencyP90All: real("latency_p90_all"),
|
|
1147
|
-
// P99 latency in ms
|
|
1148
|
-
latencyP99_1h: real("latency_p99_1h"),
|
|
1149
|
-
latencyP99_6h: real("latency_p99_6h"),
|
|
1150
|
-
latencyP99_24h: real("latency_p99_24h"),
|
|
1151
|
-
latencyP99_3d: real("latency_p99_3d"),
|
|
1152
|
-
latencyP99_7d: real("latency_p99_7d"),
|
|
1153
|
-
latencyP99_15d: real("latency_p99_15d"),
|
|
1154
|
-
latencyP99_30d: real("latency_p99_30d"),
|
|
1155
|
-
latencyP99All: real("latency_p99_all"),
|
|
1156
|
-
// =========================================================
|
|
1157
|
-
// STATUS CODE DISTRIBUTION
|
|
1158
|
-
// =========================================================
|
|
1159
|
-
// 2xx responses
|
|
1160
|
-
status2xx1h: integer("status_2xx_1h").notNull().default(0),
|
|
1161
|
-
status2xx24h: integer("status_2xx_24h").notNull().default(0),
|
|
1162
|
-
status2xx7d: integer("status_2xx_7d").notNull().default(0),
|
|
1163
|
-
status2xxAll: integer("status_2xx_all").notNull().default(0),
|
|
1164
|
-
// 3xx responses
|
|
1165
|
-
status3xx1h: integer("status_3xx_1h").notNull().default(0),
|
|
1166
|
-
status3xx24h: integer("status_3xx_24h").notNull().default(0),
|
|
1167
|
-
status3xx7d: integer("status_3xx_7d").notNull().default(0),
|
|
1168
|
-
status3xxAll: integer("status_3xx_all").notNull().default(0),
|
|
1169
|
-
// 4xx responses
|
|
1170
|
-
status4xx1h: integer("status_4xx_1h").notNull().default(0),
|
|
1171
|
-
status4xx24h: integer("status_4xx_24h").notNull().default(0),
|
|
1172
|
-
status4xx7d: integer("status_4xx_7d").notNull().default(0),
|
|
1173
|
-
status4xxAll: integer("status_4xx_all").notNull().default(0),
|
|
1174
|
-
// 5xx responses
|
|
1175
|
-
status5xx1h: integer("status_5xx_1h").notNull().default(0),
|
|
1176
|
-
status5xx24h: integer("status_5xx_24h").notNull().default(0),
|
|
1177
|
-
status5xx7d: integer("status_5xx_7d").notNull().default(0),
|
|
1178
|
-
status5xxAll: integer("status_5xx_all").notNull().default(0),
|
|
1179
|
-
// =========================================================
|
|
1180
|
-
// UPTIME & AVAILABILITY
|
|
1181
|
-
// =========================================================
|
|
1182
|
-
// Uptime percentage (0-100)
|
|
1183
|
-
uptimePercent1h: real("uptime_percent_1h"),
|
|
1184
|
-
uptimePercent24h: real("uptime_percent_24h"),
|
|
1185
|
-
uptimePercent7d: real("uptime_percent_7d"),
|
|
1186
|
-
uptimePercent30d: real("uptime_percent_30d"),
|
|
1187
|
-
uptimePercentAll: real("uptime_percent_all"),
|
|
1188
|
-
// Number of outages
|
|
1189
|
-
outageCount1h: integer("outage_count_1h").notNull().default(0),
|
|
1190
|
-
outageCount24h: integer("outage_count_24h").notNull().default(0),
|
|
1191
|
-
outageCount7d: integer("outage_count_7d").notNull().default(0),
|
|
1192
|
-
outageCount30d: integer("outage_count_30d").notNull().default(0),
|
|
1193
|
-
// =========================================================
|
|
1194
|
-
// PAYMENT METRICS
|
|
1195
|
-
// =========================================================
|
|
1196
|
-
// Total payment volume (stored as text for bigint)
|
|
1197
|
-
paymentVolume1h: text("payment_volume_1h").notNull().default("0"),
|
|
1198
|
-
paymentVolume24h: text("payment_volume_24h").notNull().default("0"),
|
|
1199
|
-
paymentVolume7d: text("payment_volume_7d").notNull().default("0"),
|
|
1200
|
-
paymentVolume30d: text("payment_volume_30d").notNull().default("0"),
|
|
1201
|
-
paymentVolumeAll: text("payment_volume_all").notNull().default("0"),
|
|
1202
|
-
// Number of payments
|
|
1203
|
-
paymentCount1h: integer("payment_count_1h").notNull().default(0),
|
|
1204
|
-
paymentCount24h: integer("payment_count_24h").notNull().default(0),
|
|
1205
|
-
paymentCount7d: integer("payment_count_7d").notNull().default(0),
|
|
1206
|
-
paymentCount30d: integer("payment_count_30d").notNull().default(0),
|
|
1207
|
-
paymentCountAll: integer("payment_count_all").notNull().default(0),
|
|
1208
|
-
// Average payment amount
|
|
1209
|
-
avgPayment1h: text("avg_payment_1h"),
|
|
1210
|
-
avgPayment24h: text("avg_payment_24h"),
|
|
1211
|
-
avgPayment7d: text("avg_payment_7d"),
|
|
1212
|
-
avgPaymentAll: text("avg_payment_all"),
|
|
1213
|
-
// Unique payers
|
|
1214
|
-
uniquePayers1h: integer("unique_payers_1h").notNull().default(0),
|
|
1215
|
-
uniquePayers24h: integer("unique_payers_24h").notNull().default(0),
|
|
1216
|
-
uniquePayers7d: integer("unique_payers_7d").notNull().default(0),
|
|
1217
|
-
uniquePayersAll: integer("unique_payers_all").notNull().default(0)
|
|
1218
|
-
},
|
|
1219
|
-
(table) => ({
|
|
1220
|
-
resourceIdx: index("resource_metrics_resource_idx").on(table.resourceId),
|
|
1221
|
-
createdAtIdx: index("resource_metrics_created_at_idx").on(table.createdAt),
|
|
1222
|
-
resourceCreatedIdx: index("resource_metrics_resource_created_idx").on(
|
|
1223
|
-
table.resourceId,
|
|
1224
|
-
table.createdAt
|
|
1225
|
-
)
|
|
1226
|
-
})
|
|
1227
|
-
);
|
|
1228
|
-
var resourceOriginMetrics = sqliteTable(
|
|
1229
|
-
"resource_origin_metrics",
|
|
1230
|
-
{
|
|
1231
|
-
// Primary key
|
|
1232
|
-
id: text("id").primaryKey(),
|
|
1233
|
-
// Foreign key to origin
|
|
1234
|
-
originId: text("origin_id").notNull().references(() => resourceOrigins.id),
|
|
1235
|
-
// Snapshot timestamp
|
|
1236
|
-
createdAt: integer("created_at").notNull(),
|
|
1237
|
-
// Resource count
|
|
1238
|
-
resourceCount: integer("resource_count").notNull().default(0),
|
|
1239
|
-
activeResourceCount: integer("active_resource_count").notNull().default(0),
|
|
1240
|
-
// Aggregated request counts
|
|
1241
|
-
totalRequests1h: integer("total_requests_1h").notNull().default(0),
|
|
1242
|
-
totalRequests24h: integer("total_requests_24h").notNull().default(0),
|
|
1243
|
-
totalRequests7d: integer("total_requests_7d").notNull().default(0),
|
|
1244
|
-
totalRequestsAll: integer("total_requests_all").notNull().default(0),
|
|
1245
|
-
// Aggregated success counts
|
|
1246
|
-
successCount1h: integer("success_count_1h").notNull().default(0),
|
|
1247
|
-
successCount24h: integer("success_count_24h").notNull().default(0),
|
|
1248
|
-
successCount7d: integer("success_count_7d").notNull().default(0),
|
|
1249
|
-
successCountAll: integer("success_count_all").notNull().default(0),
|
|
1250
|
-
// Aggregated failure counts
|
|
1251
|
-
failureCount1h: integer("failure_count_1h").notNull().default(0),
|
|
1252
|
-
failureCount24h: integer("failure_count_24h").notNull().default(0),
|
|
1253
|
-
failureCount7d: integer("failure_count_7d").notNull().default(0),
|
|
1254
|
-
failureCountAll: integer("failure_count_all").notNull().default(0),
|
|
1255
|
-
// Average latency across resources
|
|
1256
|
-
avgLatencyP50_1h: real("avg_latency_p50_1h"),
|
|
1257
|
-
avgLatencyP50_24h: real("avg_latency_p50_24h"),
|
|
1258
|
-
avgLatencyP50_7d: real("avg_latency_p50_7d"),
|
|
1259
|
-
avgLatencyP50All: real("avg_latency_p50_all"),
|
|
1260
|
-
avgLatencyP90_1h: real("avg_latency_p90_1h"),
|
|
1261
|
-
avgLatencyP90_24h: real("avg_latency_p90_24h"),
|
|
1262
|
-
avgLatencyP90_7d: real("avg_latency_p90_7d"),
|
|
1263
|
-
avgLatencyP90All: real("avg_latency_p90_all"),
|
|
1264
|
-
// Aggregated uptime
|
|
1265
|
-
avgUptimePercent1h: real("avg_uptime_percent_1h"),
|
|
1266
|
-
avgUptimePercent24h: real("avg_uptime_percent_24h"),
|
|
1267
|
-
avgUptimePercent7d: real("avg_uptime_percent_7d"),
|
|
1268
|
-
avgUptimePercentAll: real("avg_uptime_percent_all"),
|
|
1269
|
-
// Aggregated payment metrics
|
|
1270
|
-
paymentVolume1h: text("payment_volume_1h").notNull().default("0"),
|
|
1271
|
-
paymentVolume24h: text("payment_volume_24h").notNull().default("0"),
|
|
1272
|
-
paymentVolume7d: text("payment_volume_7d").notNull().default("0"),
|
|
1273
|
-
paymentVolumeAll: text("payment_volume_all").notNull().default("0"),
|
|
1274
|
-
paymentCount1h: integer("payment_count_1h").notNull().default(0),
|
|
1275
|
-
paymentCount24h: integer("payment_count_24h").notNull().default(0),
|
|
1276
|
-
paymentCount7d: integer("payment_count_7d").notNull().default(0),
|
|
1277
|
-
paymentCountAll: integer("payment_count_all").notNull().default(0)
|
|
1278
|
-
},
|
|
1279
|
-
(table) => ({
|
|
1280
|
-
originIdx: index("origin_metrics_origin_idx").on(table.originId),
|
|
1281
|
-
createdAtIdx: index("origin_metrics_created_at_idx").on(table.createdAt)
|
|
1282
|
-
})
|
|
1283
|
-
);
|
|
1284
|
-
var globalMetrics = sqliteTable("global_metrics", {
|
|
1285
|
-
// Primary key (single row, use 'global')
|
|
1286
|
-
id: text("id").primaryKey(),
|
|
1287
|
-
// Snapshot timestamp
|
|
1288
|
-
createdAt: integer("created_at").notNull(),
|
|
1289
|
-
// Resource counts
|
|
1290
|
-
totalResources: integer("total_resources").notNull().default(0),
|
|
1291
|
-
activeResources: integer("active_resources").notNull().default(0),
|
|
1292
|
-
totalOrigins: integer("total_origins").notNull().default(0),
|
|
1293
|
-
totalFacilitators: integer("total_facilitators").notNull().default(0),
|
|
1294
|
-
// Request aggregates
|
|
1295
|
-
totalRequests1h: integer("total_requests_1h").notNull().default(0),
|
|
1296
|
-
totalRequests24h: integer("total_requests_24h").notNull().default(0),
|
|
1297
|
-
totalRequests7d: integer("total_requests_7d").notNull().default(0),
|
|
1298
|
-
totalRequestsAll: integer("total_requests_all").notNull().default(0),
|
|
1299
|
-
// Payment aggregates
|
|
1300
|
-
paymentVolume1h: text("payment_volume_1h").notNull().default("0"),
|
|
1301
|
-
paymentVolume24h: text("payment_volume_24h").notNull().default("0"),
|
|
1302
|
-
paymentVolume7d: text("payment_volume_7d").notNull().default("0"),
|
|
1303
|
-
paymentVolumeAll: text("payment_volume_all").notNull().default("0"),
|
|
1304
|
-
paymentCount1h: integer("payment_count_1h").notNull().default(0),
|
|
1305
|
-
paymentCount24h: integer("payment_count_24h").notNull().default(0),
|
|
1306
|
-
paymentCount7d: integer("payment_count_7d").notNull().default(0),
|
|
1307
|
-
paymentCountAll: integer("payment_count_all").notNull().default(0),
|
|
1308
|
-
// Unique users
|
|
1309
|
-
uniquePayers1h: integer("unique_payers_1h").notNull().default(0),
|
|
1310
|
-
uniquePayers24h: integer("unique_payers_24h").notNull().default(0),
|
|
1311
|
-
uniquePayers7d: integer("unique_payers_7d").notNull().default(0),
|
|
1312
|
-
uniquePayersAll: integer("unique_payers_all").notNull().default(0),
|
|
1313
|
-
// Network breakdown (JSON)
|
|
1314
|
-
networkBreakdown: text("network_breakdown"),
|
|
1315
|
-
// JSON object
|
|
1316
|
-
// Top performers (JSON arrays)
|
|
1317
|
-
topResourcesByVolume: text("top_resources_by_volume"),
|
|
1318
|
-
// JSON array
|
|
1319
|
-
topResourcesByRequests: text("top_resources_by_requests"),
|
|
1320
|
-
// JSON array
|
|
1321
|
-
topOriginsByVolume: text("top_origins_by_volume")
|
|
1322
|
-
// JSON array
|
|
1323
|
-
});
|
|
1324
|
-
|
|
1325
|
-
// src/database/schema/index.ts
|
|
1326
|
-
var schema = {
|
|
1327
|
-
...agents_exports,
|
|
1328
|
-
...transactions_exports,
|
|
1329
|
-
...analytics_exports,
|
|
1330
|
-
...facilitators_exports,
|
|
1331
|
-
...resources_exports,
|
|
1332
|
-
...resourceMetrics_exports
|
|
1333
|
-
};
|
|
1334
|
-
|
|
1335
|
-
// src/database/db.ts
|
|
1336
|
-
async function getDb() {
|
|
1337
|
-
const client = await getConnection();
|
|
1338
|
-
return drizzle(client, { schema });
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
// src/database/services/AgentCacheService.ts
|
|
1342
|
-
var AgentCacheService = class _AgentCacheService {
|
|
1343
|
-
static instance = null;
|
|
1344
|
-
defaultMaxAge = 5 * 60 * 1e3;
|
|
1345
|
-
// 5 minutes
|
|
1346
|
-
constructor() {
|
|
1347
|
-
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Get singleton instance
|
|
1350
|
-
*/
|
|
1351
|
-
static getInstance() {
|
|
1352
|
-
if (!_AgentCacheService.instance) {
|
|
1353
|
-
_AgentCacheService.instance = new _AgentCacheService();
|
|
1354
|
-
}
|
|
1355
|
-
return _AgentCacheService.instance;
|
|
1356
|
-
}
|
|
1357
115
|
/**
|
|
1358
|
-
*
|
|
116
|
+
* Fetch transaction signatures for the facilitator address
|
|
1359
117
|
*/
|
|
1360
|
-
async
|
|
1361
|
-
return await isAvailable();
|
|
1362
|
-
}
|
|
1363
|
-
/**
|
|
1364
|
-
* Get agent from cache
|
|
1365
|
-
*
|
|
1366
|
-
* @param agentAddress - Agent's Solana address
|
|
1367
|
-
* @param options - Cache options
|
|
1368
|
-
* @returns Agent data or null if not found/expired
|
|
1369
|
-
*/
|
|
1370
|
-
async getAgent(agentAddress, options = {}) {
|
|
1371
|
-
if (!await this.isCacheAvailable()) {
|
|
1372
|
-
return null;
|
|
1373
|
-
}
|
|
1374
|
-
const { maxAge = this.defaultMaxAge, forceRefresh = false } = options;
|
|
1375
|
-
if (forceRefresh) {
|
|
1376
|
-
return null;
|
|
1377
|
-
}
|
|
118
|
+
async getSignatures(before, limit) {
|
|
1378
119
|
try {
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
const
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
120
|
+
const config = {
|
|
121
|
+
limit: limit || this.batchSize
|
|
122
|
+
};
|
|
123
|
+
if (before) {
|
|
124
|
+
config.before = before;
|
|
125
|
+
}
|
|
126
|
+
const response = await this.rpc.getSignaturesForAddress(this.facilitatorAddress, config).send();
|
|
127
|
+
return response.map((sig) => ({
|
|
128
|
+
signature: sig.signature,
|
|
129
|
+
slot: sig.slot,
|
|
130
|
+
blockTime: sig.blockTime,
|
|
131
|
+
err: sig.err ?? null
|
|
132
|
+
}));
|
|
1390
133
|
} catch (error) {
|
|
1391
|
-
console.
|
|
1392
|
-
|
|
134
|
+
console.error("[X402 Indexer] Failed to fetch signatures:", error);
|
|
135
|
+
throw error;
|
|
1393
136
|
}
|
|
1394
137
|
}
|
|
1395
138
|
/**
|
|
1396
|
-
*
|
|
1397
|
-
*
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
*
|
|
139
|
+
* Check if transaction is an x402 payment
|
|
140
|
+
*
|
|
141
|
+
* x402 payments are characterized by:
|
|
142
|
+
* - SPL token transfer (TokenProgram or Token2022Program)
|
|
143
|
+
* - Transfer TO the facilitator address
|
|
144
|
+
* - Optional memo instruction with payment metadata
|
|
1401
145
|
*/
|
|
1402
|
-
|
|
1403
|
-
if (!await this.isCacheAvailable() || agentAddresses.length === 0) {
|
|
1404
|
-
return /* @__PURE__ */ new Map();
|
|
1405
|
-
}
|
|
1406
|
-
const { maxAge = this.defaultMaxAge } = options;
|
|
146
|
+
isX402Payment(transaction) {
|
|
1407
147
|
try {
|
|
1408
|
-
const
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
148
|
+
const instructions = transaction.transaction?.message?.instructions || [];
|
|
149
|
+
const hasTokenTransfer = instructions.some((ix) => {
|
|
150
|
+
const programId = ix.programId?.toString();
|
|
151
|
+
const isTokenProgram = programId === "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" || // SPL Token
|
|
152
|
+
programId === "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb";
|
|
153
|
+
if (!isTokenProgram) return false;
|
|
154
|
+
const parsed = ix.parsed;
|
|
155
|
+
if (parsed?.type === "transfer" || parsed?.type === "transferChecked") {
|
|
156
|
+
const destination = parsed.info?.destination;
|
|
157
|
+
return destination === this.facilitatorAddress.toString();
|
|
1416
158
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
159
|
+
return false;
|
|
160
|
+
});
|
|
161
|
+
return hasTokenTransfer;
|
|
1419
162
|
} catch (error) {
|
|
1420
|
-
console.
|
|
1421
|
-
return
|
|
163
|
+
console.error("[X402 Indexer] Error checking if x402 payment:", error);
|
|
164
|
+
return false;
|
|
1422
165
|
}
|
|
1423
166
|
}
|
|
1424
167
|
/**
|
|
1425
|
-
*
|
|
1426
|
-
*
|
|
1427
|
-
* @param agent - Agent data to cache
|
|
1428
|
-
* @param capabilities - Agent capabilities
|
|
1429
|
-
* @param pricing - Agent pricing info
|
|
168
|
+
* Extract payment data from transaction
|
|
1430
169
|
*/
|
|
1431
|
-
|
|
1432
|
-
if (!await this.isCacheAvailable()) {
|
|
1433
|
-
return;
|
|
1434
|
-
}
|
|
170
|
+
extractPaymentData(transaction, signature) {
|
|
1435
171
|
try {
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
}).onConflictDoUpdate({
|
|
1441
|
-
target: agents.agentAddress,
|
|
1442
|
-
set: {
|
|
1443
|
-
...agent,
|
|
1444
|
-
cachedAt: Date.now(),
|
|
1445
|
-
updatedAt: Date.now()
|
|
1446
|
-
}
|
|
172
|
+
const instructions = transaction.transaction?.message?.instructions || [];
|
|
173
|
+
const transferIx = instructions.find((ix) => {
|
|
174
|
+
const parsed = ix.parsed;
|
|
175
|
+
return parsed?.type === "transfer" || parsed?.type === "transferChecked";
|
|
1447
176
|
});
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
await db.insert(agentCapabilities).values(
|
|
1451
|
-
capabilities.map((cap) => ({
|
|
1452
|
-
agentAddress: agent.agentAddress,
|
|
1453
|
-
capability: cap
|
|
1454
|
-
}))
|
|
1455
|
-
);
|
|
177
|
+
if (!transferIx) {
|
|
178
|
+
return null;
|
|
1456
179
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
} catch (error) {
|
|
1467
|
-
console.error("[AgentCacheService] Failed to cache agent:", error);
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
/**
|
|
1471
|
-
* List agents with filters
|
|
1472
|
-
*
|
|
1473
|
-
* @param filters - Query filters
|
|
1474
|
-
* @returns Array of agents
|
|
1475
|
-
*/
|
|
1476
|
-
async listAgents(filters = {}) {
|
|
1477
|
-
if (!await this.isCacheAvailable()) {
|
|
1478
|
-
return [];
|
|
1479
|
-
}
|
|
1480
|
-
try {
|
|
1481
|
-
const db = await getDb();
|
|
1482
|
-
const {
|
|
1483
|
-
x402Enabled,
|
|
1484
|
-
framework,
|
|
1485
|
-
isVerified,
|
|
1486
|
-
minReputation,
|
|
1487
|
-
limit = 50,
|
|
1488
|
-
offset = 0
|
|
1489
|
-
} = filters;
|
|
1490
|
-
const conditions = [];
|
|
1491
|
-
if (x402Enabled !== void 0) {
|
|
1492
|
-
conditions.push(eq(agents.x402Enabled, x402Enabled));
|
|
1493
|
-
}
|
|
1494
|
-
if (framework) {
|
|
1495
|
-
conditions.push(eq(agents.frameworkOrigin, framework));
|
|
1496
|
-
}
|
|
1497
|
-
if (isVerified !== void 0) {
|
|
1498
|
-
conditions.push(eq(agents.isVerified, isVerified));
|
|
1499
|
-
}
|
|
1500
|
-
if (minReputation !== void 0) {
|
|
1501
|
-
conditions.push(sql`${agents.reputationScore} >= ${minReputation}`);
|
|
1502
|
-
}
|
|
1503
|
-
if (conditions.length > 0) {
|
|
1504
|
-
const results = await db.select().from(agents).where(and(...conditions)).orderBy(desc(agents.reputationScore)).limit(limit).offset(offset);
|
|
1505
|
-
return results;
|
|
1506
|
-
} else {
|
|
1507
|
-
const results = await db.select().from(agents).orderBy(desc(agents.reputationScore)).limit(limit).offset(offset);
|
|
1508
|
-
return results;
|
|
1509
|
-
}
|
|
1510
|
-
} catch (error) {
|
|
1511
|
-
console.warn("[AgentCacheService] Failed to list agents:", error);
|
|
1512
|
-
return [];
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
/**
|
|
1516
|
-
* Invalidate cached agent
|
|
1517
|
-
*
|
|
1518
|
-
* @param agentAddress - Agent address to invalidate
|
|
1519
|
-
*/
|
|
1520
|
-
async invalidateAgent(agentAddress) {
|
|
1521
|
-
if (!await this.isCacheAvailable()) {
|
|
1522
|
-
return;
|
|
1523
|
-
}
|
|
1524
|
-
try {
|
|
1525
|
-
const db = await getDb();
|
|
1526
|
-
await db.delete(agents).where(eq(agents.agentAddress, agentAddress));
|
|
1527
|
-
} catch (error) {
|
|
1528
|
-
console.warn("[AgentCacheService] Failed to invalidate agent:", error);
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
/**
|
|
1532
|
-
* Clear all cached agents
|
|
1533
|
-
*/
|
|
1534
|
-
async clearCache() {
|
|
1535
|
-
if (!await this.isCacheAvailable()) {
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
try {
|
|
1539
|
-
const db = await getDb();
|
|
1540
|
-
await db.delete(agents);
|
|
1541
|
-
} catch (error) {
|
|
1542
|
-
console.error("[AgentCacheService] Failed to clear cache:", error);
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
/**
|
|
1546
|
-
* Get cache statistics
|
|
1547
|
-
*/
|
|
1548
|
-
async getCacheStats() {
|
|
1549
|
-
if (!await this.isCacheAvailable()) {
|
|
1550
|
-
return null;
|
|
1551
|
-
}
|
|
1552
|
-
try {
|
|
1553
|
-
const db = await getDb();
|
|
1554
|
-
const result = await db.select({
|
|
1555
|
-
count: sql`count(*)`,
|
|
1556
|
-
avgAge: sql`avg(${Date.now()} - ${agents.cachedAt})`,
|
|
1557
|
-
oldestCache: sql`min(${agents.cachedAt})`
|
|
1558
|
-
}).from(agents);
|
|
1559
|
-
return {
|
|
1560
|
-
totalAgents: result[0].count,
|
|
1561
|
-
averageAge: result[0].avgAge,
|
|
1562
|
-
oldestCache: result[0].oldestCache
|
|
1563
|
-
};
|
|
1564
|
-
} catch (error) {
|
|
1565
|
-
console.warn("[AgentCacheService] Failed to get cache stats:", error);
|
|
1566
|
-
return null;
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
};
|
|
1570
|
-
AgentCacheService.getInstance();
|
|
1571
|
-
|
|
1572
|
-
// src/x402/AgentDiscoveryClient.ts
|
|
1573
|
-
var REPUTATION_BASIS_POINTS_PER_STAR = 2e3;
|
|
1574
|
-
var AgentDiscoveryClient = class {
|
|
1575
|
-
rpc;
|
|
1576
|
-
programId;
|
|
1577
|
-
commitment;
|
|
1578
|
-
cache = /* @__PURE__ */ new Map();
|
|
1579
|
-
cacheEnabled;
|
|
1580
|
-
cacheTTL;
|
|
1581
|
-
useDatabaseCache;
|
|
1582
|
-
dbCache;
|
|
1583
|
-
constructor(options) {
|
|
1584
|
-
this.rpc = createSolanaRpc(options.rpcEndpoint);
|
|
1585
|
-
this.programId = options.programId;
|
|
1586
|
-
this.commitment = options.commitment ?? "confirmed";
|
|
1587
|
-
this.cacheEnabled = options.cacheEnabled ?? true;
|
|
1588
|
-
this.cacheTTL = options.cacheTTL ?? 300;
|
|
1589
|
-
this.useDatabaseCache = options.useDatabaseCache ?? true;
|
|
1590
|
-
this.dbCache = AgentCacheService.getInstance();
|
|
1591
|
-
}
|
|
1592
|
-
/**
|
|
1593
|
-
* Search for agents with filters
|
|
1594
|
-
*/
|
|
1595
|
-
async searchAgents(params = {}) {
|
|
1596
|
-
const cacheKey = this.getCacheKey("search", params);
|
|
1597
|
-
if (this.cacheEnabled) {
|
|
1598
|
-
const cached = this.getFromCache(cacheKey);
|
|
1599
|
-
if (cached) return cached;
|
|
1600
|
-
}
|
|
1601
|
-
const page = params.page ?? 1;
|
|
1602
|
-
const limit = Math.min(params.limit ?? 20, 100);
|
|
1603
|
-
const sortBy = params.sort_by ?? "reputation";
|
|
1604
|
-
const sortOrder = params.sort_order ?? "desc";
|
|
1605
|
-
const allAgents = await this.fetchAgentsFromChain(params);
|
|
1606
|
-
let filteredAgents = this.applyFilters(allAgents, params);
|
|
1607
|
-
filteredAgents = this.sortAgents(filteredAgents, sortBy, sortOrder);
|
|
1608
|
-
const total = filteredAgents.length;
|
|
1609
|
-
const totalPages = Math.ceil(total / limit);
|
|
1610
|
-
const startIndex = (page - 1) * limit;
|
|
1611
|
-
const endIndex = startIndex + limit;
|
|
1612
|
-
const paginatedAgents = filteredAgents.slice(startIndex, endIndex);
|
|
1613
|
-
const response = {
|
|
1614
|
-
agents: paginatedAgents,
|
|
1615
|
-
pagination: {
|
|
1616
|
-
page,
|
|
1617
|
-
limit,
|
|
1618
|
-
total,
|
|
1619
|
-
totalPages
|
|
1620
|
-
},
|
|
1621
|
-
filters: params
|
|
1622
|
-
};
|
|
1623
|
-
if (this.cacheEnabled) {
|
|
1624
|
-
this.setCache(cacheKey, response);
|
|
1625
|
-
}
|
|
1626
|
-
return response;
|
|
1627
|
-
}
|
|
1628
|
-
/**
|
|
1629
|
-
* Get agent by address with database cache support
|
|
1630
|
-
*
|
|
1631
|
-
* Cache hierarchy:
|
|
1632
|
-
* 1. Check Turso database cache (if enabled)
|
|
1633
|
-
* 2. Fallback to RPC fetch
|
|
1634
|
-
* 3. Cache result in database for future requests
|
|
1635
|
-
*/
|
|
1636
|
-
async getAgent(address6, forceRefresh = false) {
|
|
1637
|
-
const cacheKey = this.getCacheKey("agent", { address: address6 });
|
|
1638
|
-
if (this.useDatabaseCache && !forceRefresh) {
|
|
1639
|
-
const cachedAgent = await this.dbCache.getAgent(address6, {
|
|
1640
|
-
maxAge: this.cacheTTL * 1e3,
|
|
1641
|
-
forceRefresh
|
|
1642
|
-
});
|
|
1643
|
-
if (cachedAgent) {
|
|
1644
|
-
console.log(`[AgentDiscovery] Cache HIT for agent ${address6}`);
|
|
1645
|
-
return this.transformCachedAgent(cachedAgent);
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
if (this.cacheEnabled && !forceRefresh) {
|
|
1649
|
-
const cached = this.getFromCache(cacheKey);
|
|
1650
|
-
if (cached) {
|
|
1651
|
-
console.log(`[AgentDiscovery] Memory cache HIT for agent ${address6}`);
|
|
1652
|
-
return cached;
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
console.log(`[AgentDiscovery] Cache MISS for agent ${address6}, fetching from RPC`);
|
|
1656
|
-
try {
|
|
1657
|
-
const result = await this.rpc.getAccountInfo(address6, {
|
|
1658
|
-
commitment: this.commitment,
|
|
1659
|
-
encoding: "base64"
|
|
1660
|
-
}).send();
|
|
1661
|
-
const accountInfo = result;
|
|
1662
|
-
if (!accountInfo.value) return null;
|
|
1663
|
-
const agent = this.parseAgentAccount(address6, accountInfo.value.data);
|
|
1664
|
-
if (agent) {
|
|
1665
|
-
if (this.useDatabaseCache) {
|
|
1666
|
-
this.cacheAgentInDatabase(agent).catch(
|
|
1667
|
-
(err) => console.warn("[AgentDiscovery] Failed to cache agent in database:", err)
|
|
1668
|
-
);
|
|
1669
|
-
}
|
|
1670
|
-
if (this.cacheEnabled) {
|
|
1671
|
-
this.setCache(cacheKey, agent);
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
return agent;
|
|
1675
|
-
} catch (error) {
|
|
1676
|
-
console.error(`Failed to fetch agent ${address6}:`, error);
|
|
1677
|
-
return null;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
/**
|
|
1681
|
-
* Helper to cache agent in database
|
|
1682
|
-
*/
|
|
1683
|
-
async cacheAgentInDatabase(agent) {
|
|
1684
|
-
await this.dbCache.cacheAgent(
|
|
1685
|
-
{
|
|
1686
|
-
agentAddress: agent.address,
|
|
1687
|
-
owner: agent.owner,
|
|
1688
|
-
name: agent.name,
|
|
1689
|
-
description: agent.description,
|
|
1690
|
-
reputationScore: agent.reputation_score,
|
|
1691
|
-
totalJobsCompleted: Number(agent.total_jobs),
|
|
1692
|
-
totalEarnings: agent.total_earnings.toString(),
|
|
1693
|
-
x402Enabled: agent.x402_enabled,
|
|
1694
|
-
x402PaymentAddress: agent.x402_payment_address,
|
|
1695
|
-
x402PricePerCall: agent.x402_price_per_call.toString(),
|
|
1696
|
-
x402ServiceEndpoint: agent.x402_service_endpoint,
|
|
1697
|
-
x402TotalPayments: agent.x402_total_payments.toString(),
|
|
1698
|
-
x402TotalCalls: agent.x402_total_calls.toString(),
|
|
1699
|
-
lastPaymentTimestamp: Number(agent.last_payment_timestamp),
|
|
1700
|
-
metadataUri: agent.metadata_uri,
|
|
1701
|
-
frameworkOrigin: agent.framework_origin,
|
|
1702
|
-
isVerified: agent.is_verified,
|
|
1703
|
-
createdAt: Number(agent.created_at),
|
|
1704
|
-
updatedAt: Date.now(),
|
|
1705
|
-
cachedAt: Date.now(),
|
|
1706
|
-
bump: 0
|
|
1707
|
-
// Placeholder
|
|
1708
|
-
},
|
|
1709
|
-
agent.capabilities,
|
|
1710
|
-
[]
|
|
1711
|
-
// Pricing info would be added here
|
|
1712
|
-
);
|
|
1713
|
-
}
|
|
1714
|
-
/**
|
|
1715
|
-
* Transform cached database agent to Agent interface
|
|
1716
|
-
*/
|
|
1717
|
-
transformCachedAgent(cachedAgent) {
|
|
1718
|
-
return {
|
|
1719
|
-
address: cachedAgent.agentAddress,
|
|
1720
|
-
owner: cachedAgent.owner,
|
|
1721
|
-
name: cachedAgent.name,
|
|
1722
|
-
description: cachedAgent.description,
|
|
1723
|
-
capabilities: [],
|
|
1724
|
-
// Would need to join from agentCapabilities table
|
|
1725
|
-
x402_enabled: Boolean(cachedAgent.x402Enabled),
|
|
1726
|
-
x402_payment_address: cachedAgent.x402PaymentAddress,
|
|
1727
|
-
x402_accepted_tokens: [],
|
|
1728
|
-
// Would join from agentPricing table
|
|
1729
|
-
x402_price_per_call: BigInt(cachedAgent.x402PricePerCall),
|
|
1730
|
-
x402_service_endpoint: cachedAgent.x402ServiceEndpoint,
|
|
1731
|
-
x402_total_payments: BigInt(cachedAgent.x402TotalPayments),
|
|
1732
|
-
x402_total_calls: BigInt(cachedAgent.x402TotalCalls),
|
|
1733
|
-
last_payment_timestamp: BigInt(cachedAgent.lastPaymentTimestamp),
|
|
1734
|
-
reputation_score: cachedAgent.reputationScore,
|
|
1735
|
-
total_jobs: BigInt(cachedAgent.totalJobsCompleted),
|
|
1736
|
-
successful_jobs: BigInt(cachedAgent.totalJobsCompleted),
|
|
1737
|
-
total_earnings: BigInt(cachedAgent.totalEarnings),
|
|
1738
|
-
average_rating: cachedAgent.reputationScore / REPUTATION_BASIS_POINTS_PER_STAR,
|
|
1739
|
-
created_at: BigInt(cachedAgent.createdAt),
|
|
1740
|
-
metadata_uri: cachedAgent.metadataUri,
|
|
1741
|
-
framework_origin: cachedAgent.frameworkOrigin,
|
|
1742
|
-
is_verified: Boolean(cachedAgent.isVerified)
|
|
1743
|
-
};
|
|
1744
|
-
}
|
|
1745
|
-
/**
|
|
1746
|
-
* Get agent pricing information
|
|
1747
|
-
*/
|
|
1748
|
-
async getAgentPricing(address6) {
|
|
1749
|
-
const agent = await this.getAgent(address6);
|
|
1750
|
-
if (!agent?.x402_enabled) return null;
|
|
1751
|
-
return {
|
|
1752
|
-
price_per_call: agent.x402_price_per_call,
|
|
1753
|
-
accepted_tokens: await this.getTokenInfo(agent.x402_accepted_tokens),
|
|
1754
|
-
payment_address: agent.x402_payment_address,
|
|
1755
|
-
service_endpoint: agent.x402_service_endpoint
|
|
1756
|
-
};
|
|
1757
|
-
}
|
|
1758
|
-
/**
|
|
1759
|
-
* Get recommended agents based on criteria
|
|
1760
|
-
*/
|
|
1761
|
-
async getRecommendedAgents(capability, limit = 10) {
|
|
1762
|
-
const params = {
|
|
1763
|
-
capability,
|
|
1764
|
-
x402_enabled: true,
|
|
1765
|
-
sort_by: "reputation",
|
|
1766
|
-
sort_order: "desc",
|
|
1767
|
-
limit,
|
|
1768
|
-
min_reputation: 7e3
|
|
1769
|
-
// Only recommend highly rated agents (7.0+)
|
|
1770
|
-
};
|
|
1771
|
-
const response = await this.searchAgents(params);
|
|
1772
|
-
return response.agents;
|
|
1773
|
-
}
|
|
1774
|
-
/**
|
|
1775
|
-
* Get agents by capability
|
|
1776
|
-
*/
|
|
1777
|
-
async getAgentsByCapability(capability) {
|
|
1778
|
-
const response = await this.searchAgents({
|
|
1779
|
-
capability,
|
|
1780
|
-
x402_enabled: true
|
|
1781
|
-
});
|
|
1782
|
-
return response.agents;
|
|
1783
|
-
}
|
|
1784
|
-
/**
|
|
1785
|
-
* Get agents accepting specific token
|
|
1786
|
-
*/
|
|
1787
|
-
async getAgentsByToken(tokenAddress) {
|
|
1788
|
-
const response = await this.searchAgents({
|
|
1789
|
-
accepted_tokens: [tokenAddress],
|
|
1790
|
-
x402_enabled: true
|
|
1791
|
-
});
|
|
1792
|
-
return response.agents;
|
|
1793
|
-
}
|
|
1794
|
-
/**
|
|
1795
|
-
* Get agents within price range
|
|
1796
|
-
*/
|
|
1797
|
-
async getAgentsByPriceRange(maxPrice, capability) {
|
|
1798
|
-
const response = await this.searchAgents({
|
|
1799
|
-
max_price: maxPrice,
|
|
1800
|
-
capability,
|
|
1801
|
-
x402_enabled: true,
|
|
1802
|
-
sort_by: "price",
|
|
1803
|
-
sort_order: "asc"
|
|
1804
|
-
});
|
|
1805
|
-
return response.agents;
|
|
1806
|
-
}
|
|
1807
|
-
/**
|
|
1808
|
-
* Search agents by text query
|
|
1809
|
-
*/
|
|
1810
|
-
async searchByQuery(query) {
|
|
1811
|
-
const response = await this.searchAgents({
|
|
1812
|
-
query,
|
|
1813
|
-
x402_enabled: true
|
|
1814
|
-
});
|
|
1815
|
-
return response.agents;
|
|
1816
|
-
}
|
|
1817
|
-
// Private helper methods
|
|
1818
|
-
async fetchAgentsFromChain(params) {
|
|
1819
|
-
try {
|
|
1820
|
-
const filters = [];
|
|
1821
|
-
filters.push({ dataSize: 359n });
|
|
1822
|
-
const accounts = await this.rpc.getProgramAccounts(this.programId, {
|
|
1823
|
-
commitment: this.commitment,
|
|
1824
|
-
encoding: "base64",
|
|
1825
|
-
filters
|
|
1826
|
-
}).send();
|
|
1827
|
-
const agents2 = [];
|
|
1828
|
-
for (const { pubkey, account } of accounts) {
|
|
1829
|
-
const agent = this.parseAgentAccount(pubkey, account.data);
|
|
1830
|
-
if (agent) {
|
|
1831
|
-
agents2.push(agent);
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
return agents2;
|
|
1835
|
-
} catch (error) {
|
|
1836
|
-
console.error("Failed to fetch agents from chain:", error);
|
|
1837
|
-
return [];
|
|
1838
|
-
}
|
|
1839
|
-
}
|
|
1840
|
-
parseAgentAccount(address6, data) {
|
|
1841
|
-
try {
|
|
1842
|
-
let dataBytes;
|
|
1843
|
-
if (typeof data === "string") {
|
|
1844
|
-
dataBytes = Uint8Array.from(Buffer.from(data, "base64"));
|
|
1845
|
-
} else if (Array.isArray(data)) {
|
|
1846
|
-
if (data.length === 2 && typeof data[0] === "string" && typeof data[1] === "string" && data[1] === "base64") {
|
|
1847
|
-
dataBytes = Uint8Array.from(Buffer.from(data[0], "base64"));
|
|
1848
|
-
} else {
|
|
1849
|
-
dataBytes = Uint8Array.from(data);
|
|
1850
|
-
}
|
|
1851
|
-
} else if (data instanceof Uint8Array) {
|
|
1852
|
-
dataBytes = data;
|
|
1853
|
-
} else {
|
|
1854
|
-
throw new Error("Invalid data format");
|
|
1855
|
-
}
|
|
1856
|
-
const decoder = getAgentDecoder();
|
|
1857
|
-
const decodedData = decoder.decode(dataBytes);
|
|
1858
|
-
const decodedAgent = Array.isArray(decodedData) ? decodedData[0] : decodedData;
|
|
1859
|
-
return {
|
|
1860
|
-
address: address6,
|
|
1861
|
-
owner: decodedAgent.owner,
|
|
1862
|
-
name: decodedAgent.name,
|
|
1863
|
-
description: decodedAgent.description,
|
|
1864
|
-
capabilities: decodedAgent.capabilities,
|
|
1865
|
-
// x402 fields
|
|
1866
|
-
x402_enabled: decodedAgent.x402Enabled,
|
|
1867
|
-
x402_payment_address: decodedAgent.x402PaymentAddress,
|
|
1868
|
-
x402_accepted_tokens: decodedAgent.x402AcceptedTokens,
|
|
1869
|
-
x402_price_per_call: decodedAgent.x402PricePerCall,
|
|
1870
|
-
x402_service_endpoint: decodedAgent.x402ServiceEndpoint,
|
|
1871
|
-
x402_total_payments: decodedAgent.x402TotalPayments,
|
|
1872
|
-
x402_total_calls: decodedAgent.x402TotalCalls,
|
|
1873
|
-
last_payment_timestamp: decodedAgent.lastPaymentTimestamp,
|
|
1874
|
-
// Standard fields
|
|
1875
|
-
reputation_score: decodedAgent.reputationScore,
|
|
1876
|
-
total_jobs: BigInt(decodedAgent.totalJobsCompleted),
|
|
1877
|
-
successful_jobs: BigInt(decodedAgent.totalJobsCompleted),
|
|
1878
|
-
// Assuming successful = completed
|
|
1879
|
-
total_earnings: decodedAgent.totalEarnings,
|
|
1880
|
-
average_rating: decodedAgent.reputationScore / REPUTATION_BASIS_POINTS_PER_STAR,
|
|
1881
|
-
// Convert from basis points to 1-5 scale
|
|
1882
|
-
created_at: decodedAgent.createdAt,
|
|
1883
|
-
metadata_uri: decodedAgent.metadataUri,
|
|
1884
|
-
framework_origin: decodedAgent.frameworkOrigin,
|
|
1885
|
-
is_verified: decodedAgent.isVerified
|
|
1886
|
-
};
|
|
1887
|
-
} catch (error) {
|
|
1888
|
-
console.error("Failed to parse agent account:", error);
|
|
1889
|
-
return null;
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
applyFilters(agents2, params) {
|
|
1893
|
-
let filtered = agents2;
|
|
1894
|
-
if (params.x402_enabled !== void 0) {
|
|
1895
|
-
filtered = filtered.filter((a) => a.x402_enabled === params.x402_enabled);
|
|
1896
|
-
}
|
|
1897
|
-
if (params.capability) {
|
|
1898
|
-
filtered = filtered.filter(
|
|
1899
|
-
(a) => a.capabilities.some(
|
|
1900
|
-
(c) => c.toLowerCase().includes(params.capability.toLowerCase())
|
|
1901
|
-
)
|
|
1902
|
-
);
|
|
1903
|
-
}
|
|
1904
|
-
if (params.accepted_tokens?.length) {
|
|
1905
|
-
filtered = filtered.filter(
|
|
1906
|
-
(a) => params.accepted_tokens.some(
|
|
1907
|
-
(token) => a.x402_accepted_tokens.includes(token)
|
|
1908
|
-
)
|
|
1909
|
-
);
|
|
1910
|
-
}
|
|
1911
|
-
if (params.min_reputation !== void 0) {
|
|
1912
|
-
filtered = filtered.filter((a) => a.reputation_score >= params.min_reputation);
|
|
1913
|
-
}
|
|
1914
|
-
if (params.max_price !== void 0) {
|
|
1915
|
-
filtered = filtered.filter((a) => a.x402_price_per_call <= params.max_price);
|
|
1916
|
-
}
|
|
1917
|
-
if (params.framework_origin) {
|
|
1918
|
-
filtered = filtered.filter(
|
|
1919
|
-
(a) => a.framework_origin.toLowerCase() === params.framework_origin.toLowerCase()
|
|
180
|
+
const transferInfo = transferIx.parsed.info;
|
|
181
|
+
const merchant = transferInfo.destination;
|
|
182
|
+
const payer = transferInfo.source;
|
|
183
|
+
const amount = transferInfo.amount || transferInfo.tokenAmount?.amount || "0";
|
|
184
|
+
const success = transaction.meta?.err === null;
|
|
185
|
+
const blockTime = transaction.blockTime;
|
|
186
|
+
const timestamp = blockTime ? new Date(blockTime * 1e3) : /* @__PURE__ */ new Date();
|
|
187
|
+
const memoIx = instructions.find(
|
|
188
|
+
(ix) => ix.programId?.toString()?.includes("Memo")
|
|
1920
189
|
);
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
}
|
|
1925
|
-
if (params.query) {
|
|
1926
|
-
const query = params.query.toLowerCase();
|
|
1927
|
-
filtered = filtered.filter(
|
|
1928
|
-
(a) => a.name.toLowerCase().includes(query) || a.description.toLowerCase().includes(query)
|
|
1929
|
-
);
|
|
1930
|
-
}
|
|
1931
|
-
return filtered;
|
|
1932
|
-
}
|
|
1933
|
-
sortAgents(agents2, sortBy, sortOrder) {
|
|
1934
|
-
const sorted = [...agents2];
|
|
1935
|
-
const order = sortOrder === "asc" ? 1 : -1;
|
|
1936
|
-
sorted.sort((a, b) => {
|
|
1937
|
-
let comparison = 0;
|
|
1938
|
-
switch (sortBy) {
|
|
1939
|
-
case "reputation":
|
|
1940
|
-
comparison = a.reputation_score - b.reputation_score;
|
|
1941
|
-
break;
|
|
1942
|
-
case "price":
|
|
1943
|
-
comparison = Number(a.x402_price_per_call - b.x402_price_per_call);
|
|
1944
|
-
break;
|
|
1945
|
-
case "total_jobs":
|
|
1946
|
-
comparison = Number(a.total_jobs - b.total_jobs);
|
|
1947
|
-
break;
|
|
1948
|
-
case "created_at":
|
|
1949
|
-
comparison = Number(a.created_at - b.created_at);
|
|
1950
|
-
break;
|
|
1951
|
-
}
|
|
1952
|
-
return comparison * order;
|
|
1953
|
-
});
|
|
1954
|
-
return sorted;
|
|
1955
|
-
}
|
|
1956
|
-
async getTokenInfo(tokens) {
|
|
1957
|
-
const tokenInfo = [];
|
|
1958
|
-
for (const token of tokens) {
|
|
1959
|
-
try {
|
|
1960
|
-
const result = await this.rpc.getAccountInfo(token, {
|
|
1961
|
-
commitment: this.commitment,
|
|
1962
|
-
encoding: "base64"
|
|
1963
|
-
}).send();
|
|
1964
|
-
const mintInfo = result;
|
|
1965
|
-
if (mintInfo.value) {
|
|
1966
|
-
const data = Buffer.from(mintInfo.value.data[0], "base64");
|
|
1967
|
-
const decimals = data[44];
|
|
1968
|
-
tokenInfo.push({
|
|
1969
|
-
token,
|
|
1970
|
-
decimals,
|
|
1971
|
-
symbol: "USDC"
|
|
1972
|
-
// Placeholder - would look up from token registry or metadata
|
|
1973
|
-
});
|
|
1974
|
-
}
|
|
1975
|
-
} catch (error) {
|
|
1976
|
-
console.error(`Failed to fetch token info for ${token}:`, error);
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
return tokenInfo;
|
|
1980
|
-
}
|
|
1981
|
-
getCacheKey(prefix, params) {
|
|
1982
|
-
return `${prefix}:${JSON.stringify(params)}`;
|
|
1983
|
-
}
|
|
1984
|
-
getFromCache(key) {
|
|
1985
|
-
const cached = this.cache.get(key);
|
|
1986
|
-
if (!cached) return null;
|
|
1987
|
-
if (Date.now() > cached.expiry) {
|
|
1988
|
-
this.cache.delete(key);
|
|
1989
|
-
return null;
|
|
1990
|
-
}
|
|
1991
|
-
return cached.data;
|
|
1992
|
-
}
|
|
1993
|
-
setCache(key, data) {
|
|
1994
|
-
const expiry = Date.now() + this.cacheTTL * 1e3;
|
|
1995
|
-
this.cache.set(key, { data, expiry });
|
|
1996
|
-
if (this.cache.size > 1e3) {
|
|
1997
|
-
const entries = Array.from(this.cache.entries());
|
|
1998
|
-
entries.sort((a, b) => a[1].expiry - b[1].expiry);
|
|
1999
|
-
for (let i = 0; i < 100; i++) {
|
|
2000
|
-
this.cache.delete(entries[i][0]);
|
|
2001
|
-
}
|
|
2002
|
-
}
|
|
2003
|
-
}
|
|
2004
|
-
/**
|
|
2005
|
-
* Clear all cached data
|
|
2006
|
-
*/
|
|
2007
|
-
clearCache() {
|
|
2008
|
-
this.cache.clear();
|
|
2009
|
-
}
|
|
2010
|
-
/**
|
|
2011
|
-
* Get cache statistics
|
|
2012
|
-
*/
|
|
2013
|
-
getCacheStats() {
|
|
2014
|
-
return {
|
|
2015
|
-
size: this.cache.size,
|
|
2016
|
-
enabled: this.cacheEnabled,
|
|
2017
|
-
ttl: this.cacheTTL
|
|
2018
|
-
};
|
|
2019
|
-
}
|
|
2020
|
-
};
|
|
2021
|
-
function createAgentDiscoveryClient(options) {
|
|
2022
|
-
return new AgentDiscoveryClient(options);
|
|
2023
|
-
}
|
|
2024
|
-
function toSignature(value) {
|
|
2025
|
-
if (typeof value === "string") {
|
|
2026
|
-
return value;
|
|
2027
|
-
}
|
|
2028
|
-
return value;
|
|
2029
|
-
}
|
|
2030
|
-
var SignatureCache = class {
|
|
2031
|
-
cache = /* @__PURE__ */ new Map();
|
|
2032
|
-
ttl;
|
|
2033
|
-
cleanupInterval = null;
|
|
2034
|
-
constructor(ttl = 3600) {
|
|
2035
|
-
this.ttl = ttl * 1e3;
|
|
2036
|
-
this.cleanupInterval = setInterval(() => this.cleanup(), 3e5);
|
|
2037
|
-
if (this.cleanupInterval.unref) {
|
|
2038
|
-
this.cleanupInterval.unref();
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
has(signature) {
|
|
2042
|
-
const entry = this.cache.get(signature);
|
|
2043
|
-
if (!entry) return false;
|
|
2044
|
-
if (Date.now() > entry.expiresAt) {
|
|
2045
|
-
this.cache.delete(signature);
|
|
2046
|
-
return false;
|
|
2047
|
-
}
|
|
2048
|
-
return true;
|
|
2049
|
-
}
|
|
2050
|
-
add(signature) {
|
|
2051
|
-
this.cache.set(signature, {
|
|
2052
|
-
timestamp: Date.now(),
|
|
2053
|
-
expiresAt: Date.now() + this.ttl
|
|
2054
|
-
});
|
|
2055
|
-
}
|
|
2056
|
-
cleanup() {
|
|
2057
|
-
const now = Date.now();
|
|
2058
|
-
for (const [signature, entry] of this.cache.entries()) {
|
|
2059
|
-
if (now > entry.expiresAt) {
|
|
2060
|
-
this.cache.delete(signature);
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
getSize() {
|
|
2065
|
-
return this.cache.size;
|
|
2066
|
-
}
|
|
2067
|
-
/**
|
|
2068
|
-
* Destroy the cache and clear the cleanup interval
|
|
2069
|
-
* Call this when shutting down to prevent memory leaks
|
|
2070
|
-
*/
|
|
2071
|
-
destroy() {
|
|
2072
|
-
if (this.cleanupInterval) {
|
|
2073
|
-
clearInterval(this.cleanupInterval);
|
|
2074
|
-
this.cleanupInterval = null;
|
|
2075
|
-
}
|
|
2076
|
-
this.cache.clear();
|
|
2077
|
-
}
|
|
2078
|
-
};
|
|
2079
|
-
var globalSignatureCache = null;
|
|
2080
|
-
function createX402Middleware(options) {
|
|
2081
|
-
const preventDuplicates = options.preventDuplicatePayments ?? true;
|
|
2082
|
-
const enforceExpiration = options.enforceExpiration ?? true;
|
|
2083
|
-
const cacheTTL = options.signatureCacheTTL ?? 3600;
|
|
2084
|
-
const signatureCache = preventDuplicates ? options.signatureCacheTTL ? new SignatureCache(cacheTTL) : globalSignatureCache : null;
|
|
2085
|
-
return async (req, res, next) => {
|
|
2086
|
-
const requestStartTime = Date.now();
|
|
2087
|
-
const paymentSignature = req.headers["x-payment-signature"];
|
|
2088
|
-
const paymentExpiresAt = req.headers["x-payment-expires-at"];
|
|
2089
|
-
if (options.allowBypass && shouldBypass(req, options)) {
|
|
2090
|
-
return next();
|
|
2091
|
-
}
|
|
2092
|
-
const paymentRequest = options.x402Client.createPaymentRequest({
|
|
2093
|
-
amount: options.requiredPayment,
|
|
2094
|
-
token: options.token,
|
|
2095
|
-
description: options.description
|
|
2096
|
-
});
|
|
2097
|
-
if (!paymentSignature) {
|
|
2098
|
-
const headers = options.x402Client.createPaymentHeaders(paymentRequest);
|
|
2099
|
-
res.status(402).set(headers).json({
|
|
2100
|
-
error: "Payment Required",
|
|
2101
|
-
message: "This endpoint requires x402 payment",
|
|
2102
|
-
code: "PAYMENT_REQUIRED",
|
|
2103
|
-
paymentDetails: {
|
|
2104
|
-
address: paymentRequest.recipient,
|
|
2105
|
-
amount: paymentRequest.amount.toString(),
|
|
2106
|
-
token: paymentRequest.token,
|
|
2107
|
-
blockchain: "solana",
|
|
2108
|
-
description: options.description,
|
|
2109
|
-
expiresAt: paymentRequest.expiresAt
|
|
2110
|
-
},
|
|
2111
|
-
documentation: "https://docs.ghostspeak.ai/x402"
|
|
2112
|
-
});
|
|
2113
|
-
return;
|
|
2114
|
-
}
|
|
2115
|
-
if (preventDuplicates && signatureCache?.has(paymentSignature)) {
|
|
2116
|
-
if (options.onPaymentFailed) {
|
|
2117
|
-
await options.onPaymentFailed("Payment signature already used", req);
|
|
2118
|
-
}
|
|
2119
|
-
res.status(402).json({
|
|
2120
|
-
error: "Payment Already Used",
|
|
2121
|
-
message: "This payment signature has already been used. Please create a new payment.",
|
|
2122
|
-
code: "PAYMENT_DUPLICATE"
|
|
2123
|
-
});
|
|
2124
|
-
return;
|
|
2125
|
-
}
|
|
2126
|
-
if (enforceExpiration && paymentExpiresAt) {
|
|
2127
|
-
const now = Date.now();
|
|
2128
|
-
const expiresAtTimestamp = parseInt(paymentExpiresAt, 10);
|
|
2129
|
-
if (isNaN(expiresAtTimestamp)) {
|
|
2130
|
-
if (options.onPaymentFailed) {
|
|
2131
|
-
await options.onPaymentFailed("Invalid expiration timestamp", req);
|
|
2132
|
-
}
|
|
2133
|
-
res.status(402).json({
|
|
2134
|
-
error: "Invalid Payment Expiration",
|
|
2135
|
-
message: "The x-payment-expires-at header contains an invalid timestamp.",
|
|
2136
|
-
code: "PAYMENT_INVALID_EXPIRY"
|
|
2137
|
-
});
|
|
2138
|
-
return;
|
|
2139
|
-
}
|
|
2140
|
-
if (now > expiresAtTimestamp) {
|
|
2141
|
-
if (options.onPaymentFailed) {
|
|
2142
|
-
await options.onPaymentFailed("Payment request expired", req);
|
|
2143
|
-
}
|
|
2144
|
-
res.status(402).json({
|
|
2145
|
-
error: "Payment Expired",
|
|
2146
|
-
message: "This payment request has expired. Please create a new payment.",
|
|
2147
|
-
code: "PAYMENT_EXPIRED",
|
|
2148
|
-
expiresAt: expiresAtTimestamp,
|
|
2149
|
-
currentTime: now
|
|
2150
|
-
});
|
|
2151
|
-
return;
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
try {
|
|
2155
|
-
const verification = await options.x402Client.verifyPaymentDetails({
|
|
2156
|
-
signature: toSignature(paymentSignature),
|
|
2157
|
-
expectedRecipient: paymentRequest.recipient,
|
|
2158
|
-
expectedAmount: options.requiredPayment,
|
|
2159
|
-
expectedToken: options.token
|
|
2160
|
-
});
|
|
2161
|
-
if (!verification.valid) {
|
|
2162
|
-
if (options.onPaymentFailed) {
|
|
2163
|
-
await options.onPaymentFailed(verification.error ?? "Unknown error", req);
|
|
2164
|
-
}
|
|
2165
|
-
res.status(402).json({
|
|
2166
|
-
error: "Payment Verification Failed",
|
|
2167
|
-
message: verification.error ?? "Invalid payment",
|
|
2168
|
-
code: "PAYMENT_INVALID"
|
|
2169
|
-
});
|
|
2170
|
-
return;
|
|
2171
|
-
}
|
|
2172
|
-
const responseTimeMs = Date.now() - requestStartTime;
|
|
2173
|
-
let callerIdentity;
|
|
2174
|
-
if (options.lookupCallerIdentity) {
|
|
190
|
+
let responseTimeMs;
|
|
191
|
+
let metadata;
|
|
192
|
+
if (memoIx && memoIx.data) {
|
|
2175
193
|
try {
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
} catch (identityError) {
|
|
2182
|
-
console.warn("[x402] Failed to lookup caller identity:", identityError);
|
|
194
|
+
const memoText = Buffer.from(memoIx.data, "base64").toString("utf-8");
|
|
195
|
+
const memoData = JSON.parse(memoText);
|
|
196
|
+
responseTimeMs = memoData.responseTimeMs;
|
|
197
|
+
metadata = memoData;
|
|
198
|
+
} catch {
|
|
2183
199
|
}
|
|
2184
200
|
}
|
|
2185
|
-
req.x402Payment = {
|
|
2186
|
-
signature: paymentSignature,
|
|
2187
|
-
verified: true,
|
|
2188
|
-
amount: verification.receipt.amount,
|
|
2189
|
-
token: verification.receipt.token,
|
|
2190
|
-
responseTimeMs,
|
|
2191
|
-
timestamp: Date.now(),
|
|
2192
|
-
caller: callerIdentity
|
|
2193
|
-
};
|
|
2194
|
-
if (preventDuplicates && signatureCache) {
|
|
2195
|
-
signatureCache.add(paymentSignature);
|
|
2196
|
-
}
|
|
2197
|
-
if (options.recordPaymentOnChain && options.agentId) {
|
|
2198
|
-
try {
|
|
2199
|
-
console.log(`[x402] Would record payment on-chain for agent ${options.agentId}`);
|
|
2200
|
-
} catch (reputationError) {
|
|
2201
|
-
if (options.onReputationUpdateFailed && reputationError instanceof Error) {
|
|
2202
|
-
options.onReputationUpdateFailed(reputationError);
|
|
2203
|
-
} else {
|
|
2204
|
-
console.error("[x402] Failed to record payment on-chain:", reputationError);
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
}
|
|
2208
|
-
if (options.onPaymentVerified) {
|
|
2209
|
-
await options.onPaymentVerified(paymentSignature, req);
|
|
2210
|
-
}
|
|
2211
|
-
next();
|
|
2212
|
-
} catch (error) {
|
|
2213
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2214
|
-
if (options.onPaymentFailed) {
|
|
2215
|
-
await options.onPaymentFailed(errorMessage, req);
|
|
2216
|
-
}
|
|
2217
|
-
res.status(500).json({
|
|
2218
|
-
error: "Payment Verification Error",
|
|
2219
|
-
message: errorMessage,
|
|
2220
|
-
code: "PAYMENT_VERIFICATION_ERROR"
|
|
2221
|
-
});
|
|
2222
|
-
}
|
|
2223
|
-
};
|
|
2224
|
-
}
|
|
2225
|
-
function x402FastifyPlugin(fastify, options) {
|
|
2226
|
-
fastify.addHook("preHandler", async (request, reply) => {
|
|
2227
|
-
const route = request.url ? options.routes[request.url] : void 0;
|
|
2228
|
-
if (!route) {
|
|
2229
|
-
return;
|
|
2230
|
-
}
|
|
2231
|
-
const paymentSignature = request.headers["x-payment-signature"];
|
|
2232
|
-
const paymentRequest = options.x402Client.createPaymentRequest({
|
|
2233
|
-
amount: route.payment,
|
|
2234
|
-
token: route.token,
|
|
2235
|
-
description: route.description
|
|
2236
|
-
});
|
|
2237
|
-
if (!paymentSignature) {
|
|
2238
|
-
const headers = options.x402Client.createPaymentHeaders(paymentRequest);
|
|
2239
|
-
reply.code(402).headers(headers).send({
|
|
2240
|
-
error: "Payment Required",
|
|
2241
|
-
paymentDetails: {
|
|
2242
|
-
address: paymentRequest.recipient,
|
|
2243
|
-
amount: paymentRequest.amount.toString(),
|
|
2244
|
-
token: paymentRequest.token
|
|
2245
|
-
}
|
|
2246
|
-
});
|
|
2247
|
-
return;
|
|
2248
|
-
}
|
|
2249
|
-
const verification = await options.x402Client.verifyPaymentDetails({
|
|
2250
|
-
signature: toSignature(paymentSignature),
|
|
2251
|
-
expectedRecipient: paymentRequest.recipient,
|
|
2252
|
-
expectedAmount: route.payment,
|
|
2253
|
-
expectedToken: route.token
|
|
2254
|
-
});
|
|
2255
|
-
if (!verification.valid) {
|
|
2256
|
-
reply.code(402).send({
|
|
2257
|
-
error: "Payment Verification Failed",
|
|
2258
|
-
message: verification.error
|
|
2259
|
-
});
|
|
2260
|
-
return;
|
|
2261
|
-
}
|
|
2262
|
-
request.x402Payment = {
|
|
2263
|
-
signature: paymentSignature,
|
|
2264
|
-
verified: true,
|
|
2265
|
-
amount: verification.receipt.amount,
|
|
2266
|
-
token: verification.receipt.token
|
|
2267
|
-
};
|
|
2268
|
-
});
|
|
2269
|
-
}
|
|
2270
|
-
function shouldBypass(req, options) {
|
|
2271
|
-
const clientAddress = req.headers["x-client-address"];
|
|
2272
|
-
if (clientAddress && options.bypassAddresses) {
|
|
2273
|
-
return options.bypassAddresses.includes(clientAddress);
|
|
2274
|
-
}
|
|
2275
|
-
return false;
|
|
2276
|
-
}
|
|
2277
|
-
async function lookupCallerIdentity(sourceAddress, rpcEndpoint, programId) {
|
|
2278
|
-
if (!rpcEndpoint) {
|
|
2279
|
-
return {
|
|
2280
|
-
type: "unknown",
|
|
2281
|
-
address: sourceAddress
|
|
2282
|
-
};
|
|
2283
|
-
}
|
|
2284
|
-
try {
|
|
2285
|
-
const rpc = createSolanaRpc(rpcEndpoint);
|
|
2286
|
-
const maybeAgent = await fetchMaybeAgent(rpc, sourceAddress);
|
|
2287
|
-
if (!maybeAgent.exists) {
|
|
2288
201
|
return {
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
capabilities: agent.capabilities,
|
|
2303
|
-
framework_origin: agent.frameworkOrigin,
|
|
2304
|
-
x402_enabled: true
|
|
2305
|
-
// Assume all agents support x402 (no explicit flag in schema)
|
|
2306
|
-
}
|
|
2307
|
-
};
|
|
2308
|
-
} catch (error) {
|
|
2309
|
-
console.warn("[x402] Agent lookup failed:", error);
|
|
2310
|
-
return {
|
|
2311
|
-
type: "unknown",
|
|
2312
|
-
address: sourceAddress
|
|
2313
|
-
};
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
function withX402RateLimit(options) {
|
|
2317
|
-
const requests = /* @__PURE__ */ new Map();
|
|
2318
|
-
return (target, propertyKey, descriptor) => {
|
|
2319
|
-
const originalMethod = descriptor.value;
|
|
2320
|
-
descriptor.value = async function(...args) {
|
|
2321
|
-
const req = args[0];
|
|
2322
|
-
const res = args[1];
|
|
2323
|
-
const forwardedFor = req.headers["x-forwarded-for"];
|
|
2324
|
-
const clientIp = forwardedFor?.split(",")[0]?.trim() ?? req.ip ?? "unknown";
|
|
2325
|
-
const clientId = req.x402Payment?.signature ?? clientIp;
|
|
2326
|
-
const now = Date.now();
|
|
2327
|
-
const userRequests = requests.get(clientId) ?? [];
|
|
2328
|
-
const recentRequests = userRequests.filter(
|
|
2329
|
-
(timestamp) => now - timestamp < 6e4
|
|
2330
|
-
);
|
|
2331
|
-
if (recentRequests.length >= options.maxRequestsPerMinute) {
|
|
2332
|
-
res.status(429).json({
|
|
2333
|
-
error: "Rate Limit Exceeded",
|
|
2334
|
-
message: `Maximum ${options.maxRequestsPerMinute} requests per minute`,
|
|
2335
|
-
paymentRequired: options.paymentRequired.toString()
|
|
2336
|
-
});
|
|
2337
|
-
return;
|
|
2338
|
-
}
|
|
2339
|
-
recentRequests.push(now);
|
|
2340
|
-
requests.set(clientId, recentRequests);
|
|
2341
|
-
return originalMethod.apply(this, args);
|
|
2342
|
-
};
|
|
2343
|
-
};
|
|
2344
|
-
}
|
|
2345
|
-
|
|
2346
|
-
// src/x402/schemas/enhanced-x402.ts
|
|
2347
|
-
function validatePaymentRequirement(req) {
|
|
2348
|
-
const errors = [];
|
|
2349
|
-
const r = req;
|
|
2350
|
-
if (!r || typeof r !== "object") {
|
|
2351
|
-
return { valid: false, errors: ["Payment requirement must be an object"] };
|
|
2352
|
-
}
|
|
2353
|
-
if (!r.network || typeof r.network !== "string") {
|
|
2354
|
-
errors.push('Missing or invalid "network" field');
|
|
2355
|
-
}
|
|
2356
|
-
if (!r.maxAmountRequired && !r.max_amount_required && !r.amount) {
|
|
2357
|
-
errors.push('Missing "maxAmountRequired" field');
|
|
2358
|
-
}
|
|
2359
|
-
if (!r.payTo && !r.pay_to && !r.recipient) {
|
|
2360
|
-
errors.push('Missing "payTo" field');
|
|
2361
|
-
}
|
|
2362
|
-
if (!r.asset && !r.token) {
|
|
2363
|
-
errors.push('Missing "asset" field');
|
|
2364
|
-
}
|
|
2365
|
-
if (errors.length > 0) {
|
|
2366
|
-
return { valid: false, errors };
|
|
2367
|
-
}
|
|
2368
|
-
const requirement = {
|
|
2369
|
-
scheme: r.scheme ?? "exact",
|
|
2370
|
-
network: r.network ?? r.chain,
|
|
2371
|
-
maxAmountRequired: String(r.maxAmountRequired ?? r.max_amount_required ?? r.amount),
|
|
2372
|
-
resource: r.resource,
|
|
2373
|
-
description: r.description,
|
|
2374
|
-
mimeType: r.mimeType ?? r.mime_type,
|
|
2375
|
-
payTo: String(r.payTo ?? r.pay_to ?? r.recipient),
|
|
2376
|
-
maxTimeoutSeconds: r.maxTimeoutSeconds ?? r.max_timeout_seconds,
|
|
2377
|
-
asset: String(r.asset ?? r.token),
|
|
2378
|
-
extra: r.extra
|
|
2379
|
-
};
|
|
2380
|
-
return { valid: true, requirement };
|
|
2381
|
-
}
|
|
2382
|
-
function validateX402Response(response) {
|
|
2383
|
-
const errors = [];
|
|
2384
|
-
const r = response;
|
|
2385
|
-
if (!r || typeof r !== "object") {
|
|
2386
|
-
return { valid: false, errors: ["Response must be an object"] };
|
|
2387
|
-
}
|
|
2388
|
-
const version = r.x402Version ?? r.x402_version;
|
|
2389
|
-
if (!version) {
|
|
2390
|
-
errors.push('Missing "x402Version" field');
|
|
2391
|
-
}
|
|
2392
|
-
const accepts = r.accepts ?? r.payment_requirements;
|
|
2393
|
-
if (!accepts || !Array.isArray(accepts)) {
|
|
2394
|
-
errors.push('Missing or invalid "accepts" array');
|
|
2395
|
-
} else if (accepts.length === 0) {
|
|
2396
|
-
errors.push('"accepts" array must not be empty');
|
|
2397
|
-
} else {
|
|
2398
|
-
for (let i = 0; i < accepts.length; i++) {
|
|
2399
|
-
const result = validatePaymentRequirement(accepts[i]);
|
|
2400
|
-
if (!result.valid) {
|
|
2401
|
-
errors.push(`accepts[${i}]: ${result.errors.join(", ")}`);
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
}
|
|
2405
|
-
if (errors.length > 0) {
|
|
2406
|
-
return { valid: false, errors };
|
|
2407
|
-
}
|
|
2408
|
-
const enhanced = {
|
|
2409
|
-
x402Version: "1.0",
|
|
2410
|
-
accepts: accepts.map((a) => {
|
|
2411
|
-
const result = validatePaymentRequirement(a);
|
|
2412
|
-
return result.valid ? result.requirement : {};
|
|
2413
|
-
}),
|
|
2414
|
-
error: r.error,
|
|
2415
|
-
inputSchema: r.inputSchema ?? r.input_schema,
|
|
2416
|
-
outputSchema: r.outputSchema ?? r.output_schema,
|
|
2417
|
-
description: r.description,
|
|
2418
|
-
name: r.name,
|
|
2419
|
-
tags: r.tags,
|
|
2420
|
-
category: r.category,
|
|
2421
|
-
examples: r.examples,
|
|
2422
|
-
apiVersion: r.apiVersion ?? r.api_version,
|
|
2423
|
-
methods: r.methods,
|
|
2424
|
-
rateLimit: r.rateLimit ?? r.rate_limit,
|
|
2425
|
-
authRequired: r.authRequired ?? r.auth_required,
|
|
2426
|
-
sla: r.sla,
|
|
2427
|
-
termsUrl: r.termsUrl ?? r.terms_url,
|
|
2428
|
-
privacyUrl: r.privacyUrl ?? r.privacy_url,
|
|
2429
|
-
docsUrl: r.docsUrl ?? r.docs_url,
|
|
2430
|
-
support: r.support
|
|
2431
|
-
};
|
|
2432
|
-
return { valid: true, response: enhanced };
|
|
2433
|
-
}
|
|
2434
|
-
function parseX402Response(body) {
|
|
2435
|
-
try {
|
|
2436
|
-
const parsed = JSON.parse(body);
|
|
2437
|
-
return validateX402Response(parsed);
|
|
2438
|
-
} catch (error) {
|
|
2439
|
-
return {
|
|
2440
|
-
valid: false,
|
|
2441
|
-
errors: [`Failed to parse JSON: ${error instanceof Error ? error.message : "Unknown error"}`]
|
|
2442
|
-
};
|
|
2443
|
-
}
|
|
2444
|
-
}
|
|
2445
|
-
var FetchWithPaymentClient = class extends EventEmitter {
|
|
2446
|
-
wallet;
|
|
2447
|
-
createPaymentHeader;
|
|
2448
|
-
maxPayment;
|
|
2449
|
-
preferredNetwork;
|
|
2450
|
-
preferredToken;
|
|
2451
|
-
timeout;
|
|
2452
|
-
retries;
|
|
2453
|
-
userAgent;
|
|
2454
|
-
constructor(options) {
|
|
2455
|
-
super();
|
|
2456
|
-
this.wallet = options.wallet;
|
|
2457
|
-
this.createPaymentHeader = options.createPaymentHeader;
|
|
2458
|
-
this.maxPayment = options.maxPayment ?? BigInt("1000000000");
|
|
2459
|
-
this.preferredNetwork = options.preferredNetwork;
|
|
2460
|
-
this.preferredToken = options.preferredToken;
|
|
2461
|
-
this.timeout = options.timeout ?? 3e4;
|
|
2462
|
-
this.retries = options.retries ?? 1;
|
|
2463
|
-
this.userAgent = options.userAgent ?? "GhostSpeak-x402-Fetch/1.0";
|
|
2464
|
-
}
|
|
2465
|
-
/**
|
|
2466
|
-
* Make an x402-enabled fetch request
|
|
2467
|
-
*/
|
|
2468
|
-
async fetch(url, options) {
|
|
2469
|
-
const mergedOptions = {
|
|
2470
|
-
...options,
|
|
2471
|
-
maxPayment: options?.maxPayment ?? this.maxPayment,
|
|
2472
|
-
preferredNetwork: options?.preferredNetwork ?? this.preferredNetwork,
|
|
2473
|
-
preferredToken: options?.preferredToken ?? this.preferredToken,
|
|
2474
|
-
timeout: options?.timeout ?? this.timeout,
|
|
2475
|
-
retries: options?.retries ?? this.retries
|
|
2476
|
-
};
|
|
2477
|
-
return this.fetchWithPayment(url, mergedOptions);
|
|
2478
|
-
}
|
|
2479
|
-
/**
|
|
2480
|
-
* Internal fetch with payment logic
|
|
2481
|
-
*/
|
|
2482
|
-
async fetchWithPayment(url, options) {
|
|
2483
|
-
const headers = new Headers(options.headers);
|
|
2484
|
-
headers.set("User-Agent", this.userAgent);
|
|
2485
|
-
const initialResponse = await fetch(url, {
|
|
2486
|
-
...options,
|
|
2487
|
-
headers,
|
|
2488
|
-
signal: options.timeout ? AbortSignal.timeout(options.timeout) : void 0
|
|
2489
|
-
});
|
|
2490
|
-
if (initialResponse.status !== 402) {
|
|
2491
|
-
return initialResponse;
|
|
2492
|
-
}
|
|
2493
|
-
const body = await initialResponse.text();
|
|
2494
|
-
const parseResult = parseX402Response(body);
|
|
2495
|
-
if (!parseResult.valid) {
|
|
2496
|
-
const errorResponse = new Response(body, {
|
|
2497
|
-
status: 402,
|
|
2498
|
-
statusText: "Payment Required - Invalid x402 Response",
|
|
2499
|
-
headers: initialResponse.headers
|
|
2500
|
-
});
|
|
2501
|
-
errorResponse.x402Requirements = [];
|
|
2502
|
-
return errorResponse;
|
|
2503
|
-
}
|
|
2504
|
-
const x402Response = parseResult.response;
|
|
2505
|
-
this.emit("paymentRequired", x402Response.accepts);
|
|
2506
|
-
options.onPaymentRequired?.(x402Response.accepts);
|
|
2507
|
-
if (options.dryRun) {
|
|
2508
|
-
const dryRunResponse = new Response(body, {
|
|
2509
|
-
status: 402,
|
|
2510
|
-
statusText: "Payment Required",
|
|
2511
|
-
headers: initialResponse.headers
|
|
2512
|
-
});
|
|
2513
|
-
dryRunResponse.x402Requirements = x402Response.accepts;
|
|
2514
|
-
return dryRunResponse;
|
|
2515
|
-
}
|
|
2516
|
-
const selectedRequirement = this.selectPaymentRequirement(
|
|
2517
|
-
x402Response.accepts,
|
|
2518
|
-
options
|
|
2519
|
-
);
|
|
2520
|
-
if (!selectedRequirement) {
|
|
2521
|
-
throw new X402PaymentError(
|
|
2522
|
-
"No suitable payment option found within max payment limit",
|
|
2523
|
-
x402Response.accepts
|
|
2524
|
-
);
|
|
2525
|
-
}
|
|
2526
|
-
const amount = BigInt(selectedRequirement.maxAmountRequired);
|
|
2527
|
-
if (amount > (options.maxPayment ?? this.maxPayment)) {
|
|
2528
|
-
throw new X402PaymentError(
|
|
2529
|
-
`Payment amount ${amount} exceeds max payment ${options.maxPayment ?? this.maxPayment}`,
|
|
2530
|
-
x402Response.accepts
|
|
2531
|
-
);
|
|
2532
|
-
}
|
|
2533
|
-
let paymentHeader;
|
|
2534
|
-
try {
|
|
2535
|
-
paymentHeader = await this.createPaymentHeader(selectedRequirement, this.wallet);
|
|
2536
|
-
} catch (error) {
|
|
2537
|
-
throw new X402PaymentError(
|
|
2538
|
-
`Failed to create payment header: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2539
|
-
x402Response.accepts
|
|
2540
|
-
);
|
|
2541
|
-
}
|
|
2542
|
-
const retryHeaders = new Headers(headers);
|
|
2543
|
-
retryHeaders.set("X-Payment", paymentHeader);
|
|
2544
|
-
for (let attempt = 0; attempt <= (options.retries ?? this.retries); attempt++) {
|
|
2545
|
-
try {
|
|
2546
|
-
const paidResponse = await fetch(url, {
|
|
2547
|
-
...options,
|
|
2548
|
-
headers: retryHeaders,
|
|
2549
|
-
signal: options.timeout ? AbortSignal.timeout(options.timeout) : void 0
|
|
2550
|
-
});
|
|
2551
|
-
const paymentInfo = {
|
|
2552
|
-
signature: paymentHeader.split(":")[0] ?? "",
|
|
2553
|
-
amount,
|
|
2554
|
-
recipient: selectedRequirement.payTo,
|
|
2555
|
-
token: selectedRequirement.asset,
|
|
2556
|
-
network: selectedRequirement.network,
|
|
2557
|
-
timestamp: Date.now()
|
|
2558
|
-
};
|
|
2559
|
-
const response = paidResponse;
|
|
2560
|
-
response.paymentInfo = paymentInfo;
|
|
2561
|
-
response.x402Requirements = x402Response.accepts;
|
|
2562
|
-
this.emit("payment", paymentInfo);
|
|
2563
|
-
options.onPayment?.(paymentInfo);
|
|
2564
|
-
return response;
|
|
2565
|
-
} catch (error) {
|
|
2566
|
-
if (attempt === (options.retries ?? this.retries)) {
|
|
2567
|
-
throw error;
|
|
2568
|
-
}
|
|
2569
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
|
|
2570
|
-
}
|
|
2571
|
-
}
|
|
2572
|
-
throw new Error("Failed to complete payment request after retries");
|
|
2573
|
-
}
|
|
2574
|
-
/**
|
|
2575
|
-
* Select the best payment requirement based on options
|
|
2576
|
-
*/
|
|
2577
|
-
selectPaymentRequirement(accepts, options) {
|
|
2578
|
-
let candidates = [...accepts];
|
|
2579
|
-
if (options.preferredNetwork) {
|
|
2580
|
-
const networkMatches = candidates.filter(
|
|
2581
|
-
(a) => a.network === options.preferredNetwork
|
|
2582
|
-
);
|
|
2583
|
-
if (networkMatches.length > 0) {
|
|
2584
|
-
candidates = networkMatches;
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
if (options.preferredToken) {
|
|
2588
|
-
const tokenMatches = candidates.filter(
|
|
2589
|
-
(a) => a.asset.toLowerCase() === options.preferredToken.toLowerCase()
|
|
2590
|
-
);
|
|
2591
|
-
if (tokenMatches.length > 0) {
|
|
2592
|
-
candidates = tokenMatches;
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
const maxPayment = options.maxPayment ?? this.maxPayment;
|
|
2596
|
-
candidates = candidates.filter((a) => BigInt(a.maxAmountRequired) <= maxPayment);
|
|
2597
|
-
if (candidates.length === 0) {
|
|
2598
|
-
return null;
|
|
2599
|
-
}
|
|
2600
|
-
return candidates.sort(
|
|
2601
|
-
(a, b) => Number(BigInt(a.maxAmountRequired) - BigInt(b.maxAmountRequired))
|
|
2602
|
-
)[0];
|
|
2603
|
-
}
|
|
2604
|
-
};
|
|
2605
|
-
var X402PaymentError = class extends Error {
|
|
2606
|
-
constructor(message, requirements) {
|
|
2607
|
-
super(message);
|
|
2608
|
-
this.requirements = requirements;
|
|
2609
|
-
this.name = "X402PaymentError";
|
|
2610
|
-
}
|
|
2611
|
-
};
|
|
2612
|
-
async function fetchWithX402Payment(url, options, wallet, createPaymentHeader, paymentOptions) {
|
|
2613
|
-
const client = new FetchWithPaymentClient({
|
|
2614
|
-
wallet,
|
|
2615
|
-
createPaymentHeader,
|
|
2616
|
-
maxPayment: paymentOptions?.maxPayment,
|
|
2617
|
-
preferredNetwork: paymentOptions?.preferredNetwork,
|
|
2618
|
-
preferredToken: paymentOptions?.preferredToken,
|
|
2619
|
-
timeout: paymentOptions?.timeout,
|
|
2620
|
-
retries: paymentOptions?.retries
|
|
2621
|
-
});
|
|
2622
|
-
return client.fetch(url, {
|
|
2623
|
-
...options,
|
|
2624
|
-
...paymentOptions
|
|
2625
|
-
});
|
|
2626
|
-
}
|
|
2627
|
-
function wrapFetchWithPayment(wallet, createPaymentHeader, defaultOptions) {
|
|
2628
|
-
const client = new FetchWithPaymentClient({
|
|
2629
|
-
wallet,
|
|
2630
|
-
createPaymentHeader,
|
|
2631
|
-
...defaultOptions
|
|
2632
|
-
});
|
|
2633
|
-
return (url, options) => client.fetch(url, options);
|
|
2634
|
-
}
|
|
2635
|
-
var X402AnalyticsTracker = class extends EventEmitter {
|
|
2636
|
-
constructor(options = {}) {
|
|
2637
|
-
super();
|
|
2638
|
-
this.options = options;
|
|
2639
|
-
}
|
|
2640
|
-
paymentEvents = [];
|
|
2641
|
-
agentEarnings = /* @__PURE__ */ new Map();
|
|
2642
|
-
agentCallCounts = /* @__PURE__ */ new Map();
|
|
2643
|
-
tokenVolumes = /* @__PURE__ */ new Map();
|
|
2644
|
-
metricsTimer;
|
|
2645
|
-
isActive = false;
|
|
2646
|
-
// Integration with existing analytics system
|
|
2647
|
-
analyticsStreamer;
|
|
2648
|
-
analyticsAggregator;
|
|
2649
|
-
/**
|
|
2650
|
-
* Start analytics tracking
|
|
2651
|
-
*/
|
|
2652
|
-
start() {
|
|
2653
|
-
if (this.isActive) {
|
|
2654
|
-
console.warn("x402 analytics tracker is already active");
|
|
2655
|
-
return;
|
|
2656
|
-
}
|
|
2657
|
-
this.isActive = true;
|
|
2658
|
-
if (this.options.metricsInterval) {
|
|
2659
|
-
this.metricsTimer = setInterval(() => {
|
|
2660
|
-
this.aggregateAndEmitMetrics();
|
|
2661
|
-
}, this.options.metricsInterval);
|
|
2662
|
-
}
|
|
2663
|
-
this.emit("started");
|
|
2664
|
-
}
|
|
2665
|
-
/**
|
|
2666
|
-
* Stop analytics tracking
|
|
2667
|
-
*/
|
|
2668
|
-
stop() {
|
|
2669
|
-
this.isActive = false;
|
|
2670
|
-
if (this.metricsTimer) {
|
|
2671
|
-
clearInterval(this.metricsTimer);
|
|
2672
|
-
this.metricsTimer = void 0;
|
|
2673
|
-
}
|
|
2674
|
-
this.emit("stopped");
|
|
2675
|
-
}
|
|
2676
|
-
/**
|
|
2677
|
-
* Record a new x402 payment event
|
|
2678
|
-
*/
|
|
2679
|
-
recordPayment(event) {
|
|
2680
|
-
if (!this.isActive) return;
|
|
2681
|
-
this.paymentEvents.push(event);
|
|
2682
|
-
if (event.agent && event.status === "confirmed") {
|
|
2683
|
-
const currentEarnings = this.agentEarnings.get(event.agent) ?? 0n;
|
|
2684
|
-
this.agentEarnings.set(event.agent, currentEarnings + event.amount);
|
|
2685
|
-
const currentCalls = this.agentCallCounts.get(event.agent) ?? 0;
|
|
2686
|
-
this.agentCallCounts.set(event.agent, currentCalls + 1);
|
|
2687
|
-
}
|
|
2688
|
-
if (event.status === "confirmed") {
|
|
2689
|
-
const currentVolume = this.tokenVolumes.get(event.token) ?? 0n;
|
|
2690
|
-
this.tokenVolumes.set(event.token, currentVolume + event.amount);
|
|
2691
|
-
}
|
|
2692
|
-
this.emit("payment", event);
|
|
2693
|
-
if (this.options.onPayment) {
|
|
2694
|
-
this.options.onPayment(event);
|
|
2695
|
-
}
|
|
2696
|
-
this.pruneOldEvents();
|
|
2697
|
-
}
|
|
2698
|
-
/**
|
|
2699
|
-
* Record payment from transaction signature
|
|
2700
|
-
*/
|
|
2701
|
-
async recordPaymentFromSignature(signature, payer, recipient, amount, token, agent) {
|
|
2702
|
-
const event = {
|
|
2703
|
-
signature,
|
|
2704
|
-
timestamp: BigInt(Date.now()),
|
|
2705
|
-
payer,
|
|
2706
|
-
recipient,
|
|
2707
|
-
amount,
|
|
2708
|
-
token,
|
|
2709
|
-
agent,
|
|
2710
|
-
status: "pending"
|
|
2711
|
-
};
|
|
2712
|
-
this.recordPayment(event);
|
|
2713
|
-
}
|
|
2714
|
-
/**
|
|
2715
|
-
* Update payment status
|
|
2716
|
-
*/
|
|
2717
|
-
updatePaymentStatus(signature, status, metadata) {
|
|
2718
|
-
const event = this.paymentEvents.find((e) => e.signature === signature);
|
|
2719
|
-
if (event) {
|
|
2720
|
-
event.status = status;
|
|
2721
|
-
if (metadata) {
|
|
2722
|
-
event.metadata = { ...event.metadata, ...metadata };
|
|
2723
|
-
}
|
|
2724
|
-
this.emit("paymentStatusChanged", event);
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
/**
|
|
2728
|
-
* Get current metrics
|
|
2729
|
-
*/
|
|
2730
|
-
getMetrics(periodSeconds) {
|
|
2731
|
-
const now = BigInt(Date.now());
|
|
2732
|
-
const startTime = periodSeconds ? now - BigInt(periodSeconds * 1e3) : this.getOldestEventTimestamp();
|
|
2733
|
-
const periodEvents = this.paymentEvents.filter((e) => e.timestamp >= startTime);
|
|
2734
|
-
const total = periodEvents.length;
|
|
2735
|
-
const successful = periodEvents.filter((e) => e.status === "confirmed").length;
|
|
2736
|
-
const failed = periodEvents.filter((e) => e.status === "failed").length;
|
|
2737
|
-
const pending = periodEvents.filter((e) => e.status === "pending").length;
|
|
2738
|
-
const successfulEvents = periodEvents.filter((e) => e.status === "confirmed");
|
|
2739
|
-
const totalVolume = successfulEvents.reduce((sum, e) => sum + e.amount, 0n);
|
|
2740
|
-
const averageVolume = successful > 0 ? totalVolume / BigInt(successful) : 0n;
|
|
2741
|
-
const volumeByToken = /* @__PURE__ */ new Map();
|
|
2742
|
-
for (const event of successfulEvents) {
|
|
2743
|
-
const current = volumeByToken.get(event.token) ?? 0n;
|
|
2744
|
-
volumeByToken.set(event.token, current + event.amount);
|
|
2745
|
-
}
|
|
2746
|
-
const activeAgents = new Set(
|
|
2747
|
-
periodEvents.filter((e) => e.agent).map((e) => e.agent)
|
|
2748
|
-
);
|
|
2749
|
-
const topEarners = Array.from(this.agentEarnings.entries()).map(([agent, earnings]) => ({
|
|
2750
|
-
agent,
|
|
2751
|
-
earnings,
|
|
2752
|
-
callCount: this.agentCallCounts.get(agent) ?? 0
|
|
2753
|
-
})).sort((a, b) => Number(b.earnings - a.earnings)).slice(0, 10);
|
|
2754
|
-
const confirmedEvents = periodEvents.filter((e) => e.status === "confirmed");
|
|
2755
|
-
const confirmationTimes = confirmedEvents.map((e) => Number(e.metadata?.confirmationTime ?? 0)).filter((t) => t > 0);
|
|
2756
|
-
const averageConfirmationTime = confirmationTimes.length > 0 ? confirmationTimes.reduce((a, b) => a + b, 0) / confirmationTimes.length : 0;
|
|
2757
|
-
const successRate = total > 0 ? successful / total * 100 : 0;
|
|
2758
|
-
const errorRate = total > 0 ? failed / total * 100 : 0;
|
|
2759
|
-
return {
|
|
2760
|
-
period: {
|
|
2761
|
-
start: startTime,
|
|
2762
|
-
end: now
|
|
2763
|
-
},
|
|
2764
|
-
payments: {
|
|
2765
|
-
total,
|
|
2766
|
-
successful,
|
|
2767
|
-
failed,
|
|
2768
|
-
pending
|
|
2769
|
-
},
|
|
2770
|
-
volume: {
|
|
2771
|
-
total: totalVolume,
|
|
2772
|
-
average: averageVolume,
|
|
2773
|
-
byToken: volumeByToken
|
|
2774
|
-
},
|
|
2775
|
-
agents: {
|
|
2776
|
-
totalActive: activeAgents.size,
|
|
2777
|
-
topEarners
|
|
2778
|
-
},
|
|
2779
|
-
performance: {
|
|
2780
|
-
averageConfirmationTime,
|
|
2781
|
-
successRate,
|
|
2782
|
-
errorRate
|
|
2783
|
-
}
|
|
2784
|
-
};
|
|
2785
|
-
}
|
|
2786
|
-
/**
|
|
2787
|
-
* Get agent earnings
|
|
2788
|
-
*/
|
|
2789
|
-
getAgentEarnings(agent) {
|
|
2790
|
-
const totalEarnings = this.agentEarnings.get(agent) ?? 0n;
|
|
2791
|
-
const totalCalls = this.agentCallCounts.get(agent) ?? 0;
|
|
2792
|
-
const averagePerCall = totalCalls > 0 ? totalEarnings / BigInt(totalCalls) : 0n;
|
|
2793
|
-
return {
|
|
2794
|
-
totalEarnings,
|
|
2795
|
-
totalCalls,
|
|
2796
|
-
averagePerCall
|
|
2797
|
-
};
|
|
2798
|
-
}
|
|
2799
|
-
/**
|
|
2800
|
-
* Get token volume
|
|
2801
|
-
*/
|
|
2802
|
-
getTokenVolume(token) {
|
|
2803
|
-
return this.tokenVolumes.get(token) ?? 0n;
|
|
2804
|
-
}
|
|
2805
|
-
/**
|
|
2806
|
-
* Get payment history for agent
|
|
2807
|
-
*/
|
|
2808
|
-
getAgentPaymentHistory(agent, limit) {
|
|
2809
|
-
const agentEvents = this.paymentEvents.filter((e) => e.agent === agent).sort((a, b) => Number(b.timestamp - a.timestamp));
|
|
2810
|
-
return limit ? agentEvents.slice(0, limit) : agentEvents;
|
|
2811
|
-
}
|
|
2812
|
-
/**
|
|
2813
|
-
* Get recent payments
|
|
2814
|
-
*/
|
|
2815
|
-
getRecentPayments(limit = 100) {
|
|
2816
|
-
return this.paymentEvents.slice(-limit).sort((a, b) => Number(b.timestamp - a.timestamp));
|
|
2817
|
-
}
|
|
2818
|
-
/**
|
|
2819
|
-
* Integrate with existing analytics streamer
|
|
2820
|
-
*/
|
|
2821
|
-
integrateWithStreamer(streamer) {
|
|
2822
|
-
this.analyticsStreamer = streamer;
|
|
2823
|
-
streamer.on("transactionAnalytics", (event) => {
|
|
2824
|
-
if (this.isX402Transaction(event)) {
|
|
2825
|
-
this.recordPaymentFromAnalyticsEvent(event);
|
|
2826
|
-
}
|
|
2827
|
-
});
|
|
2828
|
-
}
|
|
2829
|
-
/**
|
|
2830
|
-
* Integrate with existing analytics aggregator
|
|
2831
|
-
*/
|
|
2832
|
-
integrateWithAggregator(aggregator) {
|
|
2833
|
-
this.analyticsAggregator = aggregator;
|
|
2834
|
-
this.on("payment", (event) => {
|
|
2835
|
-
if (event.status === "confirmed" && this.analyticsAggregator) {
|
|
2836
|
-
const txEvent = {
|
|
2837
|
-
transactionType: "x402_payment",
|
|
2838
|
-
amount: event.amount,
|
|
2839
|
-
from: event.payer,
|
|
2840
|
-
to: event.recipient,
|
|
2841
|
-
status: "completed",
|
|
2842
|
-
timestamp: BigInt(event.timestamp),
|
|
2843
|
-
blockHeight: 0n
|
|
2844
|
-
// Would be populated from actual blockchain data
|
|
2845
|
-
};
|
|
2846
|
-
this.analyticsAggregator.processTransactionEvent(txEvent);
|
|
2847
|
-
}
|
|
2848
|
-
});
|
|
2849
|
-
}
|
|
2850
|
-
/**
|
|
2851
|
-
* Get aggregated metrics from integrated aggregator
|
|
2852
|
-
*/
|
|
2853
|
-
getAggregatedMetrics() {
|
|
2854
|
-
return this.analyticsAggregator?.aggregate() ?? null;
|
|
2855
|
-
}
|
|
2856
|
-
// Private helper methods
|
|
2857
|
-
aggregateAndEmitMetrics() {
|
|
2858
|
-
try {
|
|
2859
|
-
const metrics = this.getMetrics();
|
|
2860
|
-
this.emit("metrics", metrics);
|
|
2861
|
-
if (this.options.onMetrics) {
|
|
2862
|
-
this.options.onMetrics(metrics);
|
|
2863
|
-
}
|
|
2864
|
-
} catch (error) {
|
|
2865
|
-
this.handleError(error);
|
|
2866
|
-
}
|
|
2867
|
-
}
|
|
2868
|
-
pruneOldEvents() {
|
|
2869
|
-
if (!this.options.retentionPeriod) return;
|
|
2870
|
-
const cutoffTime = BigInt(Date.now()) - BigInt(this.options.retentionPeriod * 1e3);
|
|
2871
|
-
this.paymentEvents = this.paymentEvents.filter((e) => e.timestamp > cutoffTime);
|
|
2872
|
-
}
|
|
2873
|
-
getOldestEventTimestamp() {
|
|
2874
|
-
if (this.paymentEvents.length === 0) {
|
|
2875
|
-
return BigInt(Date.now());
|
|
2876
|
-
}
|
|
2877
|
-
return this.paymentEvents[0].timestamp;
|
|
2878
|
-
}
|
|
2879
|
-
isX402Transaction(event) {
|
|
2880
|
-
return event.transactionType === "x402_payment" || event.transactionType === "payment";
|
|
2881
|
-
}
|
|
2882
|
-
recordPaymentFromAnalyticsEvent(event) {
|
|
2883
|
-
const paymentEvent = {
|
|
2884
|
-
signature: "",
|
|
2885
|
-
// Not available in TransactionAnalyticsEvent
|
|
2886
|
-
timestamp: BigInt(event.timestamp),
|
|
2887
|
-
payer: event.from,
|
|
2888
|
-
recipient: event.to,
|
|
2889
|
-
amount: event.amount,
|
|
2890
|
-
token: "11111111111111111111111111111111",
|
|
2891
|
-
// Would parse from event
|
|
2892
|
-
status: event.status === "completed" ? "confirmed" : "failed"
|
|
2893
|
-
};
|
|
2894
|
-
this.recordPayment(paymentEvent);
|
|
2895
|
-
}
|
|
2896
|
-
handleError(error) {
|
|
2897
|
-
this.emit("error", error);
|
|
2898
|
-
if (this.options.onError) {
|
|
2899
|
-
this.options.onError(error);
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
/**
|
|
2903
|
-
* Clear all tracked data
|
|
2904
|
-
*/
|
|
2905
|
-
clearData() {
|
|
2906
|
-
this.paymentEvents = [];
|
|
2907
|
-
this.agentEarnings.clear();
|
|
2908
|
-
this.agentCallCounts.clear();
|
|
2909
|
-
this.tokenVolumes.clear();
|
|
2910
|
-
}
|
|
2911
|
-
/**
|
|
2912
|
-
* Get tracker statistics
|
|
2913
|
-
*/
|
|
2914
|
-
getStats() {
|
|
2915
|
-
return {
|
|
2916
|
-
eventsTracked: this.paymentEvents.length,
|
|
2917
|
-
agentsTracked: this.agentEarnings.size,
|
|
2918
|
-
tokensTracked: this.tokenVolumes.size,
|
|
2919
|
-
isActive: this.isActive
|
|
2920
|
-
};
|
|
2921
|
-
}
|
|
2922
|
-
};
|
|
2923
|
-
function createX402AnalyticsTracker(options) {
|
|
2924
|
-
return new X402AnalyticsTracker(options);
|
|
2925
|
-
}
|
|
2926
|
-
var PaymentStreamingManager = class extends EventEmitter {
|
|
2927
|
-
streams;
|
|
2928
|
-
intervals;
|
|
2929
|
-
x402Client;
|
|
2930
|
-
connection;
|
|
2931
|
-
constructor(x402Client, connection) {
|
|
2932
|
-
super();
|
|
2933
|
-
this.streams = /* @__PURE__ */ new Map();
|
|
2934
|
-
this.intervals = /* @__PURE__ */ new Map();
|
|
2935
|
-
this.x402Client = x402Client;
|
|
2936
|
-
this.connection = connection;
|
|
2937
|
-
}
|
|
2938
|
-
/**
|
|
2939
|
-
* Create a new payment stream
|
|
2940
|
-
*/
|
|
2941
|
-
async createStream(config) {
|
|
2942
|
-
const streamId = this.generateStreamId(config);
|
|
2943
|
-
this.validateStreamConfig(config);
|
|
2944
|
-
const stream = {
|
|
2945
|
-
id: streamId,
|
|
2946
|
-
config,
|
|
2947
|
-
status: "active",
|
|
2948
|
-
amountPaid: 0n,
|
|
2949
|
-
payments: [],
|
|
2950
|
-
startedAt: Date.now(),
|
|
2951
|
-
nextPaymentAt: Date.now() + config.intervalMs
|
|
2952
|
-
};
|
|
2953
|
-
this.streams.set(streamId, stream);
|
|
2954
|
-
this.startStreamInterval(stream);
|
|
2955
|
-
this.emit("stream_created", stream);
|
|
2956
|
-
return stream;
|
|
2957
|
-
}
|
|
2958
|
-
/**
|
|
2959
|
-
* Pause a payment stream
|
|
2960
|
-
*/
|
|
2961
|
-
pauseStream(streamId) {
|
|
2962
|
-
const stream = this.streams.get(streamId);
|
|
2963
|
-
if (!stream) {
|
|
2964
|
-
throw new Error(`Stream ${streamId} not found`);
|
|
2965
|
-
}
|
|
2966
|
-
if (stream.status !== "active") {
|
|
2967
|
-
throw new Error(`Stream ${streamId} is not active`);
|
|
2968
|
-
}
|
|
2969
|
-
this.stopStreamInterval(streamId);
|
|
2970
|
-
stream.status = "paused";
|
|
2971
|
-
this.emit("stream_paused", stream);
|
|
2972
|
-
}
|
|
2973
|
-
/**
|
|
2974
|
-
* Resume a paused payment stream
|
|
2975
|
-
*/
|
|
2976
|
-
resumeStream(streamId) {
|
|
2977
|
-
const stream = this.streams.get(streamId);
|
|
2978
|
-
if (!stream) {
|
|
2979
|
-
throw new Error(`Stream ${streamId} not found`);
|
|
2980
|
-
}
|
|
2981
|
-
if (stream.status !== "paused") {
|
|
2982
|
-
throw new Error(`Stream ${streamId} is not paused`);
|
|
2983
|
-
}
|
|
2984
|
-
stream.status = "active";
|
|
2985
|
-
stream.nextPaymentAt = Date.now() + stream.config.intervalMs;
|
|
2986
|
-
this.startStreamInterval(stream);
|
|
2987
|
-
this.emit("stream_resumed", stream);
|
|
2988
|
-
}
|
|
2989
|
-
/**
|
|
2990
|
-
* Cancel a payment stream
|
|
2991
|
-
*/
|
|
2992
|
-
cancelStream(streamId) {
|
|
2993
|
-
const stream = this.streams.get(streamId);
|
|
2994
|
-
if (!stream) {
|
|
2995
|
-
throw new Error(`Stream ${streamId} not found`);
|
|
202
|
+
signature,
|
|
203
|
+
merchant,
|
|
204
|
+
payer,
|
|
205
|
+
amount,
|
|
206
|
+
success,
|
|
207
|
+
timestamp,
|
|
208
|
+
network: this.network,
|
|
209
|
+
responseTimeMs,
|
|
210
|
+
metadata
|
|
211
|
+
};
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error("[X402 Indexer] Failed to extract payment data:", error);
|
|
214
|
+
return null;
|
|
2996
215
|
}
|
|
2997
|
-
this.stopStreamInterval(streamId);
|
|
2998
|
-
stream.status = "cancelled";
|
|
2999
|
-
stream.endedAt = Date.now();
|
|
3000
|
-
this.emit("stream_cancelled", stream);
|
|
3001
|
-
}
|
|
3002
|
-
/**
|
|
3003
|
-
* Get stream by ID
|
|
3004
|
-
*/
|
|
3005
|
-
getStream(streamId) {
|
|
3006
|
-
return this.streams.get(streamId);
|
|
3007
|
-
}
|
|
3008
|
-
/**
|
|
3009
|
-
* Get all streams
|
|
3010
|
-
*/
|
|
3011
|
-
getAllStreams() {
|
|
3012
|
-
return Array.from(this.streams.values());
|
|
3013
216
|
}
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// src/index.ts
|
|
220
|
+
init_reputation_tags();
|
|
221
|
+
init_MultiSourceAggregator();
|
|
222
|
+
var TIMESTAMP_TOLERANCE_MS = 5 * 60 * 1e3;
|
|
223
|
+
var SIGNATURE_HEADER = "x-payai-signature";
|
|
224
|
+
var TIMESTAMP_HEADER = "x-payai-timestamp";
|
|
225
|
+
var PayAIWebhookHandler = class extends EventEmitter {
|
|
226
|
+
options;
|
|
227
|
+
verifySignatures;
|
|
228
|
+
authorizationModule;
|
|
229
|
+
payAIFacilitatorAddress;
|
|
230
|
+
constructor(options = {}, authorizationModule, payAIFacilitatorAddress) {
|
|
231
|
+
super();
|
|
232
|
+
this.options = options;
|
|
233
|
+
this.verifySignatures = options.verifySignatures ?? process.env.NODE_ENV === "production";
|
|
234
|
+
this.authorizationModule = authorizationModule;
|
|
235
|
+
this.payAIFacilitatorAddress = payAIFacilitatorAddress;
|
|
3019
236
|
}
|
|
237
|
+
// =====================================================
|
|
238
|
+
// PUBLIC METHODS
|
|
239
|
+
// =====================================================
|
|
3020
240
|
/**
|
|
3021
|
-
*
|
|
241
|
+
* Handle an incoming webhook request
|
|
242
|
+
*
|
|
243
|
+
* @param request - The incoming HTTP request (must have headers and body)
|
|
244
|
+
* @returns Processing result
|
|
3022
245
|
*/
|
|
3023
|
-
async
|
|
3024
|
-
const stream = this.streams.get(streamId);
|
|
3025
|
-
if (!stream) {
|
|
3026
|
-
throw new Error(`Stream ${streamId} not found`);
|
|
3027
|
-
}
|
|
3028
|
-
const milestone = stream.config.milestones?.find((m) => m.id === milestoneId);
|
|
3029
|
-
if (!milestone) {
|
|
3030
|
-
throw new Error(`Milestone ${milestoneId} not found`);
|
|
3031
|
-
}
|
|
3032
|
-
if (milestone.completed) {
|
|
3033
|
-
throw new Error(`Milestone ${milestoneId} already completed`);
|
|
3034
|
-
}
|
|
3035
|
-
const conditionMet = await milestone.condition();
|
|
3036
|
-
if (!conditionMet) {
|
|
3037
|
-
throw new Error(`Milestone ${milestoneId} condition not met`);
|
|
3038
|
-
}
|
|
246
|
+
async handleWebhook(request) {
|
|
3039
247
|
try {
|
|
3040
|
-
const
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
248
|
+
const payload = typeof request.body === "string" ? JSON.parse(request.body) : request.body;
|
|
249
|
+
if (this.verifySignatures && this.options.webhookSecret) {
|
|
250
|
+
const bodyStr = typeof request.body === "string" ? request.body : JSON.stringify(request.body);
|
|
251
|
+
const verification = await this.verifySignature(
|
|
252
|
+
request.headers,
|
|
253
|
+
bodyStr
|
|
254
|
+
);
|
|
255
|
+
if (!verification.valid) {
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
error: verification.error ?? "Invalid signature"
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return await this.processPayload(payload);
|
|
3048
263
|
} catch (error) {
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
264
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
265
|
+
this.emit("error", new Error(`Webhook processing failed: ${errorMessage}`));
|
|
266
|
+
return {
|
|
267
|
+
success: false,
|
|
268
|
+
error: errorMessage
|
|
269
|
+
};
|
|
3055
270
|
}
|
|
3056
271
|
}
|
|
3057
272
|
/**
|
|
3058
|
-
*
|
|
273
|
+
* Verify webhook signature
|
|
3059
274
|
*/
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
275
|
+
async verifySignature(headers, body) {
|
|
276
|
+
const getHeader = (name) => {
|
|
277
|
+
if (headers instanceof Headers) {
|
|
278
|
+
return headers.get(name) ?? void 0;
|
|
279
|
+
}
|
|
280
|
+
return headers[name];
|
|
281
|
+
};
|
|
282
|
+
const signature = getHeader(SIGNATURE_HEADER);
|
|
283
|
+
const timestamp = getHeader(TIMESTAMP_HEADER);
|
|
284
|
+
if (!signature) {
|
|
285
|
+
return { valid: false, error: "Missing signature header" };
|
|
3063
286
|
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
}
|
|
3067
|
-
// Private methods
|
|
3068
|
-
generateStreamId(config) {
|
|
3069
|
-
const timestamp = Date.now();
|
|
3070
|
-
const hash = `${config.agentAddress}-${config.clientAddress}-${timestamp}`;
|
|
3071
|
-
return Buffer.from(hash).toString("base64").slice(0, 16);
|
|
3072
|
-
}
|
|
3073
|
-
validateStreamConfig(config) {
|
|
3074
|
-
if (config.totalAmount <= 0n) {
|
|
3075
|
-
throw new Error("Total amount must be positive");
|
|
287
|
+
if (!timestamp) {
|
|
288
|
+
return { valid: false, error: "Missing timestamp header" };
|
|
3076
289
|
}
|
|
3077
|
-
|
|
3078
|
-
|
|
290
|
+
const timestampMs = parseInt(timestamp, 10);
|
|
291
|
+
if (isNaN(timestampMs)) {
|
|
292
|
+
return { valid: false, error: "Invalid timestamp format" };
|
|
293
|
+
}
|
|
294
|
+
const now = Date.now();
|
|
295
|
+
if (Math.abs(now - timestampMs) > TIMESTAMP_TOLERANCE_MS) {
|
|
296
|
+
return { valid: false, error: "Timestamp too old or too far in future" };
|
|
3079
297
|
}
|
|
3080
|
-
if (
|
|
3081
|
-
|
|
298
|
+
if (!this.options.webhookSecret) {
|
|
299
|
+
return { valid: false, error: "Webhook secret not configured" };
|
|
3082
300
|
}
|
|
3083
|
-
|
|
3084
|
-
|
|
301
|
+
const expectedSignature = this.computeSignature(
|
|
302
|
+
timestamp,
|
|
303
|
+
body,
|
|
304
|
+
this.options.webhookSecret
|
|
305
|
+
);
|
|
306
|
+
const signatureBuffer = Buffer.from(signature, "hex");
|
|
307
|
+
const expectedBuffer = Buffer.from(expectedSignature, "hex");
|
|
308
|
+
if (signatureBuffer.length !== expectedBuffer.length) {
|
|
309
|
+
return { valid: false, error: "Invalid signature" };
|
|
3085
310
|
}
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
const milestoneTotal = config.milestones?.reduce((sum, m) => sum + m.amount, 0n) ?? 0n;
|
|
3089
|
-
if (totalViaInterval + milestoneTotal > config.totalAmount * 110n / 100n) {
|
|
3090
|
-
throw new Error("Payment schedule exceeds total amount (with 10% tolerance)");
|
|
311
|
+
if (!timingSafeEqual(signatureBuffer, expectedBuffer)) {
|
|
312
|
+
return { valid: false, error: "Invalid signature" };
|
|
3091
313
|
}
|
|
314
|
+
return { valid: true };
|
|
3092
315
|
}
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
316
|
+
/**
|
|
317
|
+
* Convert a PayAI payment to a reputation record
|
|
318
|
+
*/
|
|
319
|
+
paymentToReputationRecord(data) {
|
|
320
|
+
return {
|
|
321
|
+
agentAddress: data.merchant,
|
|
322
|
+
paymentSignature: data.transactionSignature,
|
|
323
|
+
amount: BigInt(data.amount),
|
|
324
|
+
success: data.success ?? data.status === "settled",
|
|
325
|
+
responseTimeMs: data.responseTimeMs ?? 0,
|
|
326
|
+
payerAddress: data.payer,
|
|
327
|
+
timestamp: new Date(data.settledAt ?? data.verifiedAt ?? Date.now()),
|
|
328
|
+
network: data.network
|
|
329
|
+
};
|
|
3098
330
|
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
331
|
+
// =====================================================
|
|
332
|
+
// PRIVATE METHODS
|
|
333
|
+
// =====================================================
|
|
334
|
+
/**
|
|
335
|
+
* Process a verified webhook payload
|
|
336
|
+
*/
|
|
337
|
+
async processPayload(payload) {
|
|
338
|
+
const { type, data } = payload;
|
|
339
|
+
switch (type) {
|
|
340
|
+
case "payment.verified":
|
|
341
|
+
this.emit("payment:verified", data);
|
|
342
|
+
await this.options.onPaymentVerified?.(data);
|
|
343
|
+
break;
|
|
344
|
+
case "payment.settled":
|
|
345
|
+
this.emit("payment:settled", data);
|
|
346
|
+
await this.options.onPaymentSettled?.(data);
|
|
347
|
+
if (this.options.onRecordReputation) {
|
|
348
|
+
if (this.authorizationModule && this.payAIFacilitatorAddress) {
|
|
349
|
+
const isAuthorized = await this.verifyOnChainAuthorization(data.merchant);
|
|
350
|
+
if (!isAuthorized) {
|
|
351
|
+
return {
|
|
352
|
+
success: false,
|
|
353
|
+
eventType: type,
|
|
354
|
+
paymentId: data.paymentId,
|
|
355
|
+
error: "Agent has not authorized PayAI facilitator to update reputation",
|
|
356
|
+
reputationRecorded: false
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const record = this.paymentToReputationRecord(data);
|
|
361
|
+
await this.options.onRecordReputation(record);
|
|
362
|
+
this.emit("reputation:recorded", record);
|
|
363
|
+
return {
|
|
364
|
+
success: true,
|
|
365
|
+
eventType: type,
|
|
366
|
+
paymentId: data.paymentId,
|
|
367
|
+
reputationRecorded: true
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
break;
|
|
371
|
+
case "payment.failed":
|
|
372
|
+
this.emit("payment:failed", data);
|
|
373
|
+
await this.options.onPaymentFailed?.(data);
|
|
374
|
+
if (this.options.onRecordReputation) {
|
|
375
|
+
const record = this.paymentToReputationRecord(data);
|
|
376
|
+
record.success = false;
|
|
377
|
+
await this.options.onRecordReputation(record);
|
|
378
|
+
this.emit("reputation:recorded", record);
|
|
379
|
+
return {
|
|
380
|
+
success: true,
|
|
381
|
+
eventType: type,
|
|
382
|
+
paymentId: data.paymentId,
|
|
383
|
+
reputationRecorded: true
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
case "payment.refunded":
|
|
388
|
+
break;
|
|
389
|
+
default:
|
|
390
|
+
return {
|
|
391
|
+
success: false,
|
|
392
|
+
error: `Unknown event type: ${type}`
|
|
393
|
+
};
|
|
3104
394
|
}
|
|
395
|
+
return {
|
|
396
|
+
success: true,
|
|
397
|
+
eventType: type,
|
|
398
|
+
paymentId: data.paymentId,
|
|
399
|
+
reputationRecorded: false
|
|
400
|
+
};
|
|
3105
401
|
}
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
this.completeStream(stream);
|
|
3116
|
-
return;
|
|
402
|
+
/**
|
|
403
|
+
* Verify on-chain authorization for an agent
|
|
404
|
+
*
|
|
405
|
+
* Checks if the agent has pre-authorized the PayAI facilitator
|
|
406
|
+
* to update their reputation on-chain.
|
|
407
|
+
*/
|
|
408
|
+
async verifyOnChainAuthorization(agentAddress) {
|
|
409
|
+
if (!this.authorizationModule || !this.payAIFacilitatorAddress) {
|
|
410
|
+
return true;
|
|
3117
411
|
}
|
|
3118
412
|
try {
|
|
3119
|
-
const
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
amount,
|
|
3126
|
-
signature
|
|
3127
|
-
});
|
|
3128
|
-
} catch (error) {
|
|
3129
|
-
this.emit("payment_failed", {
|
|
3130
|
-
streamId: stream.id,
|
|
3131
|
-
error
|
|
3132
|
-
});
|
|
3133
|
-
if (stream.config.autoResume !== true) {
|
|
3134
|
-
stream.status = "failed";
|
|
3135
|
-
stream.error = error;
|
|
3136
|
-
stream.endedAt = Date.now();
|
|
3137
|
-
this.stopStreamInterval(stream.id);
|
|
413
|
+
const authorization = await this.authorizationModule.fetchAuthorization(
|
|
414
|
+
agentAddress,
|
|
415
|
+
this.payAIFacilitatorAddress
|
|
416
|
+
);
|
|
417
|
+
if (!authorization) {
|
|
418
|
+
return false;
|
|
3138
419
|
}
|
|
420
|
+
const status = this.authorizationModule.getAuthorizationStatus(
|
|
421
|
+
authorization,
|
|
422
|
+
authorization.currentIndex
|
|
423
|
+
);
|
|
424
|
+
return status.isValid;
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.error("Failed to verify on-chain authorization:", error);
|
|
427
|
+
return false;
|
|
3139
428
|
}
|
|
3140
429
|
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
}
|
|
3150
|
-
});
|
|
3151
|
-
const receipt = await this.x402Client.pay(paymentRequest);
|
|
3152
|
-
const payment = {
|
|
3153
|
-
timestamp: Date.now(),
|
|
3154
|
-
amount,
|
|
3155
|
-
signature: receipt.signature,
|
|
3156
|
-
milestoneId,
|
|
3157
|
-
success: true
|
|
3158
|
-
};
|
|
3159
|
-
stream.payments.push(payment);
|
|
3160
|
-
stream.amountPaid += amount;
|
|
3161
|
-
return receipt.signature;
|
|
3162
|
-
}
|
|
3163
|
-
completeStream(stream) {
|
|
3164
|
-
this.stopStreamInterval(stream.id);
|
|
3165
|
-
stream.status = "completed";
|
|
3166
|
-
stream.endedAt = Date.now();
|
|
3167
|
-
this.emit("stream_completed", stream);
|
|
430
|
+
/**
|
|
431
|
+
* Compute HMAC-SHA256 signature for a webhook payload
|
|
432
|
+
*/
|
|
433
|
+
computeSignature(timestamp, body, secret) {
|
|
434
|
+
const signedPayload = `${timestamp}.${body}`;
|
|
435
|
+
const hmac = createHmac("sha256", secret);
|
|
436
|
+
hmac.update(signedPayload);
|
|
437
|
+
return hmac.digest("hex");
|
|
3168
438
|
}
|
|
3169
439
|
};
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
440
|
+
function createPayAIWebhookHandler(options = {}) {
|
|
441
|
+
return new PayAIWebhookHandler(options);
|
|
442
|
+
}
|
|
443
|
+
function generateTestWebhookSignature(body, secret, timestamp) {
|
|
444
|
+
const ts = (timestamp ?? Date.now()).toString();
|
|
445
|
+
const signedPayload = `${ts}.${body}`;
|
|
446
|
+
const hmac = createHmac("sha256", secret);
|
|
447
|
+
hmac.update(signedPayload);
|
|
448
|
+
return {
|
|
449
|
+
signature: hmac.digest("hex"),
|
|
450
|
+
timestamp: ts
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function createMockPayAIWebhook(overrides = {}) {
|
|
454
|
+
return {
|
|
455
|
+
id: `evt_${Date.now()}`,
|
|
456
|
+
type: "payment.settled",
|
|
457
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
458
|
+
data: {
|
|
459
|
+
paymentId: `pay_${Date.now()}`,
|
|
460
|
+
transactionSignature: `${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`,
|
|
461
|
+
network: "solana",
|
|
462
|
+
payer: "PayerWalletAddress111111111111111111111111",
|
|
463
|
+
merchant: "MerchantAgentAddress11111111111111111111",
|
|
464
|
+
amount: "1000000",
|
|
465
|
+
// 1 USDC
|
|
466
|
+
asset: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
467
|
+
assetSymbol: "USDC",
|
|
468
|
+
status: "settled",
|
|
469
|
+
resource: "https://api.example.com/ai/generate",
|
|
470
|
+
responseTimeMs: 250,
|
|
471
|
+
httpStatusCode: 200,
|
|
472
|
+
success: true,
|
|
473
|
+
settledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
474
|
+
...overrides
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
var DEFAULT_MARKETPLACE_URL = "https://marketplace.payai.network/api";
|
|
479
|
+
var PayAIAgentSync = class extends EventEmitter {
|
|
480
|
+
config;
|
|
481
|
+
constructor(config = {}) {
|
|
3175
482
|
super();
|
|
3176
|
-
this.
|
|
3177
|
-
|
|
3178
|
-
|
|
483
|
+
this.config = {
|
|
484
|
+
marketplaceUrl: config.marketplaceUrl ?? DEFAULT_MARKETPLACE_URL,
|
|
485
|
+
apiKey: config.apiKey,
|
|
486
|
+
network: config.network ?? "solana",
|
|
487
|
+
timeout: config.timeout ?? 3e4
|
|
488
|
+
};
|
|
3179
489
|
}
|
|
490
|
+
// =====================================================
|
|
491
|
+
// PUBLIC METHODS
|
|
492
|
+
// =====================================================
|
|
3180
493
|
/**
|
|
3181
|
-
*
|
|
3182
|
-
*
|
|
3183
|
-
* Establishes a secure communication channel between two agents.
|
|
494
|
+
* Register a GhostSpeak agent with PayAI marketplace
|
|
3184
495
|
*
|
|
3185
|
-
* @param
|
|
3186
|
-
* @returns
|
|
3187
|
-
*
|
|
3188
|
-
* @example
|
|
3189
|
-
* ```typescript
|
|
3190
|
-
* const session = await a2aClient.createSession({
|
|
3191
|
-
* responder: 'AgentBpubkey...',
|
|
3192
|
-
* sessionType: 'data_processing',
|
|
3193
|
-
* metadata: 'Processing financial data',
|
|
3194
|
-
* expiresIn: 7200 // 2 hours
|
|
3195
|
-
* })
|
|
3196
|
-
* console.log('Session created:', session.address)
|
|
3197
|
-
* ```
|
|
496
|
+
* @param agent - Agent data from GhostSpeak
|
|
497
|
+
* @returns Registration result
|
|
3198
498
|
*/
|
|
3199
|
-
async
|
|
499
|
+
async registerAgent(agent) {
|
|
3200
500
|
try {
|
|
3201
|
-
const
|
|
3202
|
-
const
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
}
|
|
3213
|
-
const
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
timestamp: Date.now(),
|
|
3218
|
-
data: { sessionAddress, signature }
|
|
501
|
+
const registration = this.toPayAIRegistration(agent);
|
|
502
|
+
const response = await this.makeRequest("/merchants/register", {
|
|
503
|
+
method: "POST",
|
|
504
|
+
body: JSON.stringify(registration)
|
|
505
|
+
});
|
|
506
|
+
if (!response.ok) {
|
|
507
|
+
const error = await response.text();
|
|
508
|
+
return {
|
|
509
|
+
success: false,
|
|
510
|
+
error: `Registration failed: ${error}`
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
const data = await response.json();
|
|
514
|
+
this.emit("agent:registered", {
|
|
515
|
+
agentAddress: agent.address,
|
|
516
|
+
agentId: data.agentId
|
|
3219
517
|
});
|
|
3220
|
-
const session = await fetchA2ASession(this.rpc, sessionAddress);
|
|
3221
518
|
return {
|
|
3222
|
-
|
|
3223
|
-
|
|
519
|
+
success: true,
|
|
520
|
+
agentId: data.agentId,
|
|
521
|
+
marketplaceUrl: data.url
|
|
3224
522
|
};
|
|
3225
523
|
} catch (error) {
|
|
3226
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
timestamp: Date.now(),
|
|
524
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
525
|
+
return {
|
|
526
|
+
success: false,
|
|
3230
527
|
error: errorMessage
|
|
3231
|
-
}
|
|
3232
|
-
throw new Error(`Failed to create A2A session: ${errorMessage}`);
|
|
528
|
+
};
|
|
3233
529
|
}
|
|
3234
530
|
}
|
|
3235
531
|
/**
|
|
3236
|
-
*
|
|
3237
|
-
*
|
|
3238
|
-
* Sends structured messages between agents with automatic context management.
|
|
532
|
+
* Update an agent's registration on PayAI marketplace
|
|
3239
533
|
*
|
|
3240
|
-
* @param
|
|
3241
|
-
* @
|
|
3242
|
-
*
|
|
3243
|
-
* @example
|
|
3244
|
-
* ```typescript
|
|
3245
|
-
* await a2aClient.sendMessage({
|
|
3246
|
-
* sessionAddress: session.address,
|
|
3247
|
-
* content: 'Processing complete. Results: {...}',
|
|
3248
|
-
* messageType: 'result'
|
|
3249
|
-
* })
|
|
3250
|
-
* ```
|
|
534
|
+
* @param agentId - PayAI agent ID
|
|
535
|
+
* @param agent - Updated agent data
|
|
536
|
+
* @returns Update result
|
|
3251
537
|
*/
|
|
3252
|
-
async
|
|
538
|
+
async updateAgent(agentId, agent) {
|
|
3253
539
|
try {
|
|
3254
|
-
const
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
const instruction = getSendA2aMessageInstruction({
|
|
3258
|
-
message: await this.deriveMessageAddress(params.sessionAddress),
|
|
3259
|
-
session: params.sessionAddress,
|
|
3260
|
-
sender: this.wallet,
|
|
3261
|
-
messageId,
|
|
3262
|
-
sessionId,
|
|
3263
|
-
senderArg: this.wallet.address,
|
|
3264
|
-
content: params.content,
|
|
3265
|
-
messageType: params.messageType ?? "text",
|
|
3266
|
-
timestamp
|
|
3267
|
-
}, { programAddress: this.programId });
|
|
3268
|
-
const signature = await this.sendTransaction([instruction]);
|
|
3269
|
-
this.emit("message_sent", {
|
|
3270
|
-
type: "message_sent",
|
|
3271
|
-
timestamp: Date.now(),
|
|
3272
|
-
data: { signature, content: params.content }
|
|
540
|
+
const response = await this.makeRequest(`/merchants/${agentId}`, {
|
|
541
|
+
method: "PATCH",
|
|
542
|
+
body: JSON.stringify(agent)
|
|
3273
543
|
});
|
|
3274
|
-
|
|
544
|
+
if (!response.ok) {
|
|
545
|
+
const error = await response.text();
|
|
546
|
+
return {
|
|
547
|
+
success: false,
|
|
548
|
+
error: `Update failed: ${error}`
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
this.emit("agent:updated", { agentId });
|
|
552
|
+
return {
|
|
553
|
+
success: true,
|
|
554
|
+
agentId
|
|
555
|
+
};
|
|
3275
556
|
} catch (error) {
|
|
3276
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
timestamp: Date.now(),
|
|
557
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
558
|
+
return {
|
|
559
|
+
success: false,
|
|
3280
560
|
error: errorMessage
|
|
3281
|
-
}
|
|
3282
|
-
throw new Error(`Failed to send A2A message: ${errorMessage}`);
|
|
561
|
+
};
|
|
3283
562
|
}
|
|
3284
563
|
}
|
|
3285
564
|
/**
|
|
3286
|
-
*
|
|
3287
|
-
*
|
|
3288
|
-
* Updates agent availability, capabilities, and status for discovery.
|
|
565
|
+
* Remove an agent from PayAI marketplace
|
|
3289
566
|
*
|
|
3290
|
-
* @param
|
|
3291
|
-
* @returns
|
|
3292
|
-
*
|
|
3293
|
-
* @example
|
|
3294
|
-
* ```typescript
|
|
3295
|
-
* await a2aClient.updateStatus({
|
|
3296
|
-
* status: 'busy',
|
|
3297
|
-
* capabilities: ['nlp', 'translation'],
|
|
3298
|
-
* availability: false
|
|
3299
|
-
* })
|
|
3300
|
-
* ```
|
|
567
|
+
* @param agentId - PayAI agent ID
|
|
568
|
+
* @returns Removal result
|
|
3301
569
|
*/
|
|
3302
|
-
async
|
|
570
|
+
async removeAgent(agentId) {
|
|
3303
571
|
try {
|
|
3304
|
-
const
|
|
3305
|
-
|
|
3306
|
-
status: statusAddress,
|
|
3307
|
-
session: address("11111111111111111111111111111111"),
|
|
3308
|
-
// Placeholder - need actual session
|
|
3309
|
-
updater: this.wallet,
|
|
3310
|
-
statusId: BigInt(Date.now()),
|
|
3311
|
-
agent: this.wallet.address,
|
|
3312
|
-
statusArg: update.status,
|
|
3313
|
-
capabilities: update.capabilities ?? [],
|
|
3314
|
-
availability: update.availability ?? true,
|
|
3315
|
-
lastUpdated: BigInt(Math.floor(Date.now() / 1e3))
|
|
3316
|
-
}, { programAddress: this.programId });
|
|
3317
|
-
const signature = await this.sendTransaction([instruction]);
|
|
3318
|
-
this.emit("status_updated", {
|
|
3319
|
-
type: "status_updated",
|
|
3320
|
-
timestamp: Date.now(),
|
|
3321
|
-
data: { signature, status: update.status }
|
|
572
|
+
const response = await this.makeRequest(`/merchants/${agentId}`, {
|
|
573
|
+
method: "DELETE"
|
|
3322
574
|
});
|
|
3323
|
-
|
|
575
|
+
if (!response.ok) {
|
|
576
|
+
const error = await response.text();
|
|
577
|
+
return {
|
|
578
|
+
success: false,
|
|
579
|
+
error: `Removal failed: ${error}`
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
this.emit("agent:removed", { agentId });
|
|
583
|
+
return {
|
|
584
|
+
success: true,
|
|
585
|
+
agentId
|
|
586
|
+
};
|
|
3324
587
|
} catch (error) {
|
|
3325
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
timestamp: Date.now(),
|
|
588
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
589
|
+
return {
|
|
590
|
+
success: false,
|
|
3329
591
|
error: errorMessage
|
|
3330
|
-
}
|
|
3331
|
-
throw new Error(`Failed to update A2A status: ${errorMessage}`);
|
|
592
|
+
};
|
|
3332
593
|
}
|
|
3333
594
|
}
|
|
3334
595
|
/**
|
|
3335
|
-
*
|
|
3336
|
-
*
|
|
3337
|
-
* Fetches current session state from on-chain.
|
|
596
|
+
* Check if an agent is registered on PayAI marketplace
|
|
3338
597
|
*
|
|
3339
|
-
* @param
|
|
3340
|
-
* @returns
|
|
598
|
+
* @param agentAddress - Agent's Solana address
|
|
599
|
+
* @returns Whether agent is registered
|
|
3341
600
|
*/
|
|
3342
|
-
async
|
|
601
|
+
async isAgentRegistered(agentAddress) {
|
|
3343
602
|
try {
|
|
3344
|
-
const
|
|
3345
|
-
|
|
3346
|
-
|
|
603
|
+
const response = await this.makeRequest(
|
|
604
|
+
`/merchants/check?address=${agentAddress}&network=${this.config.network}`
|
|
605
|
+
);
|
|
606
|
+
if (!response.ok) {
|
|
607
|
+
return false;
|
|
3347
608
|
}
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
} catch (error) {
|
|
3353
|
-
console.error("Failed to fetch A2A session:", error);
|
|
3354
|
-
return null;
|
|
609
|
+
const data = await response.json();
|
|
610
|
+
return data.registered;
|
|
611
|
+
} catch {
|
|
612
|
+
return false;
|
|
3355
613
|
}
|
|
3356
614
|
}
|
|
3357
615
|
/**
|
|
3358
|
-
* Get
|
|
616
|
+
* Get agent's PayAI marketplace listing
|
|
3359
617
|
*
|
|
3360
|
-
* @param
|
|
3361
|
-
* @returns
|
|
618
|
+
* @param agentAddress - Agent's Solana address
|
|
619
|
+
* @returns Agent listing or null
|
|
3362
620
|
*/
|
|
3363
|
-
async
|
|
621
|
+
async getAgentListing(agentAddress) {
|
|
3364
622
|
try {
|
|
3365
|
-
const
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
623
|
+
const response = await this.makeRequest(
|
|
624
|
+
`/merchants/by-address?address=${agentAddress}&network=${this.config.network}`
|
|
625
|
+
);
|
|
626
|
+
if (!response.ok) {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
return response.json();
|
|
630
|
+
} catch {
|
|
3369
631
|
return null;
|
|
3370
632
|
}
|
|
3371
633
|
}
|
|
3372
634
|
/**
|
|
3373
|
-
*
|
|
635
|
+
* Sync reputation score to PayAI marketplace
|
|
636
|
+
*
|
|
637
|
+
* Updates the agent's reputation score on PayAI based on
|
|
638
|
+
* GhostSpeak reputation calculations.
|
|
3374
639
|
*
|
|
3375
|
-
* @param
|
|
3376
|
-
* @
|
|
640
|
+
* @param agentId - PayAI agent ID
|
|
641
|
+
* @param reputationScore - New reputation score (0-10000)
|
|
642
|
+
* @returns Update result
|
|
3377
643
|
*/
|
|
3378
|
-
async
|
|
644
|
+
async syncReputationScore(agentId, reputationScore) {
|
|
3379
645
|
try {
|
|
3380
|
-
const
|
|
3381
|
-
|
|
646
|
+
const response = await this.makeRequest(`/merchants/${agentId}/reputation`, {
|
|
647
|
+
method: "POST",
|
|
648
|
+
body: JSON.stringify({
|
|
649
|
+
score: reputationScore,
|
|
650
|
+
source: "ghostspeak",
|
|
651
|
+
timestamp: Date.now()
|
|
652
|
+
})
|
|
653
|
+
});
|
|
654
|
+
if (!response.ok) {
|
|
655
|
+
const error = await response.text();
|
|
656
|
+
return {
|
|
657
|
+
success: false,
|
|
658
|
+
error: `Reputation sync failed: ${error}`
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
this.emit("reputation:synced", { agentId, reputationScore });
|
|
662
|
+
return {
|
|
663
|
+
success: true,
|
|
664
|
+
agentId
|
|
665
|
+
};
|
|
3382
666
|
} catch (error) {
|
|
3383
|
-
|
|
3384
|
-
return
|
|
667
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
668
|
+
return {
|
|
669
|
+
success: false,
|
|
670
|
+
error: errorMessage
|
|
671
|
+
};
|
|
3385
672
|
}
|
|
3386
673
|
}
|
|
3387
674
|
// =====================================================
|
|
3388
|
-
// PRIVATE
|
|
675
|
+
// PRIVATE METHODS
|
|
3389
676
|
// =====================================================
|
|
3390
|
-
async sendTransaction(instructions) {
|
|
3391
|
-
try {
|
|
3392
|
-
const { value: latestBlockhash } = await this.rpc.getLatestBlockhash().send();
|
|
3393
|
-
let message = createTransactionMessage({ version: 0 });
|
|
3394
|
-
message = setTransactionMessageFeePayer(this.wallet.address, message);
|
|
3395
|
-
message = setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, message);
|
|
3396
|
-
for (const ix of instructions) {
|
|
3397
|
-
message = appendTransactionMessageInstruction(ix, message);
|
|
3398
|
-
}
|
|
3399
|
-
const signedTransaction = await signTransactionMessageWithSigners(message);
|
|
3400
|
-
const signatures = Object.values(signedTransaction.signatures);
|
|
3401
|
-
if (signatures.length === 0) {
|
|
3402
|
-
throw new Error("Transaction has no signatures");
|
|
3403
|
-
}
|
|
3404
|
-
const signature = signatures[0];
|
|
3405
|
-
const wireTransaction = getBase64EncodedWireTransaction(signedTransaction);
|
|
3406
|
-
await this.rpc.sendTransaction(wireTransaction).send();
|
|
3407
|
-
await this.confirmTransaction(signature);
|
|
3408
|
-
return signature;
|
|
3409
|
-
} catch (error) {
|
|
3410
|
-
throw new Error(
|
|
3411
|
-
`Transaction failed: ${error instanceof Error ? error.message : String(error)}`
|
|
3412
|
-
);
|
|
3413
|
-
}
|
|
3414
|
-
}
|
|
3415
|
-
async confirmTransaction(signature) {
|
|
3416
|
-
for (let i = 0; i < 30; i++) {
|
|
3417
|
-
const status = await this.rpc.getSignatureStatuses([signature]).send();
|
|
3418
|
-
if (status.value[0]?.confirmationStatus === "confirmed" || status.value[0]?.confirmationStatus === "finalized") {
|
|
3419
|
-
return;
|
|
3420
|
-
}
|
|
3421
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3422
|
-
}
|
|
3423
|
-
throw new Error("Transaction confirmation timeout");
|
|
3424
|
-
}
|
|
3425
|
-
async deriveMessageAddress(sessionAddress) {
|
|
3426
|
-
return address("11111111111111111111111111111111");
|
|
3427
|
-
}
|
|
3428
|
-
async deriveStatusAddress() {
|
|
3429
|
-
return address("11111111111111111111111111111111");
|
|
3430
|
-
}
|
|
3431
|
-
};
|
|
3432
|
-
function createA2AClient(options, wallet) {
|
|
3433
|
-
return new A2AClient(options, wallet);
|
|
3434
|
-
}
|
|
3435
|
-
var H2AClient = class extends EventEmitter {
|
|
3436
|
-
rpc;
|
|
3437
|
-
programId;
|
|
3438
|
-
wallet;
|
|
3439
|
-
constructor(options, wallet) {
|
|
3440
|
-
super();
|
|
3441
|
-
this.rpc = createSolanaRpc(options.rpcEndpoint);
|
|
3442
|
-
this.programId = options.programId ?? GHOSTSPEAK_MARKETPLACE_PROGRAM_ADDRESS;
|
|
3443
|
-
this.wallet = wallet;
|
|
3444
|
-
}
|
|
3445
|
-
/**
|
|
3446
|
-
* Create a new communication session with an agent
|
|
3447
|
-
*
|
|
3448
|
-
* Establishes a communication channel between a human and an agent.
|
|
3449
|
-
*
|
|
3450
|
-
* @param config - Session configuration
|
|
3451
|
-
* @returns Session details including address
|
|
3452
|
-
*
|
|
3453
|
-
* @throws {Error} Currently not implemented - requires IDL regeneration
|
|
3454
|
-
*
|
|
3455
|
-
* @example
|
|
3456
|
-
* ```typescript
|
|
3457
|
-
* const session = await h2aClient.createSession({
|
|
3458
|
-
* agentAddress: 'Agent123...',
|
|
3459
|
-
* sessionType: 'content_writing',
|
|
3460
|
-
* metadata: 'Blog post about AI agents',
|
|
3461
|
-
* expiresIn: 7200 // 2 hours
|
|
3462
|
-
* })
|
|
3463
|
-
* ```
|
|
3464
|
-
*/
|
|
3465
|
-
async createSession(config) {
|
|
3466
|
-
const expiresAt = BigInt(Math.floor(Date.now() / 1e3) + (config.expiresIn ?? 3600));
|
|
3467
|
-
const sessionId = BigInt(Date.now());
|
|
3468
|
-
const initiator = this.wallet.address;
|
|
3469
|
-
const [sessionAddress] = await getProgramDerivedAddress({
|
|
3470
|
-
programAddress: this.programId,
|
|
3471
|
-
seeds: [
|
|
3472
|
-
getBytesEncoder().encode(new Uint8Array([99, 111, 109, 109, 95, 115, 101, 115, 115, 105, 111, 110])),
|
|
3473
|
-
// 'comm_session'
|
|
3474
|
-
getAddressEncoder().encode(initiator)
|
|
3475
|
-
]
|
|
3476
|
-
});
|
|
3477
|
-
const instruction = await getCreateCommunicationSessionInstructionAsync({
|
|
3478
|
-
session: sessionAddress,
|
|
3479
|
-
creator: this.wallet,
|
|
3480
|
-
sessionId,
|
|
3481
|
-
initiator,
|
|
3482
|
-
initiatorType: 0 /* Human */,
|
|
3483
|
-
responder: config.agentAddress,
|
|
3484
|
-
responderType: 1 /* Agent */,
|
|
3485
|
-
sessionType: config.sessionType,
|
|
3486
|
-
metadata: config.metadata ?? "",
|
|
3487
|
-
expiresAt
|
|
3488
|
-
}, { programAddress: this.programId });
|
|
3489
|
-
const signature = await this.sendTransaction([instruction]);
|
|
3490
|
-
this.emit("session_created", {
|
|
3491
|
-
type: "session_created",
|
|
3492
|
-
timestamp: Date.now(),
|
|
3493
|
-
data: {
|
|
3494
|
-
address: sessionAddress,
|
|
3495
|
-
signature,
|
|
3496
|
-
agent: config.agentAddress
|
|
3497
|
-
}
|
|
3498
|
-
});
|
|
3499
|
-
return { address: sessionAddress, signature };
|
|
3500
|
-
}
|
|
3501
|
-
/**
|
|
3502
|
-
* Send a service request to an agent
|
|
3503
|
-
*
|
|
3504
|
-
* Sends a work request to an agent with optional payment commitment.
|
|
3505
|
-
*
|
|
3506
|
-
* @param request - Service request parameters
|
|
3507
|
-
* @returns Transaction signature
|
|
3508
|
-
*
|
|
3509
|
-
* @example
|
|
3510
|
-
* ```typescript
|
|
3511
|
-
* await h2aClient.sendServiceRequest({
|
|
3512
|
-
* sessionAddress: session.address,
|
|
3513
|
-
* content: 'Generate 10 social media posts about web3',
|
|
3514
|
-
* messageType: 'service_request',
|
|
3515
|
-
* attachments: ['ipfs://QmBrandGuidelines'],
|
|
3516
|
-
* paymentAmount: 10_000_000n, // 10 USDC
|
|
3517
|
-
* deadline: Date.now() / 1000 + 172800 // 48 hours
|
|
3518
|
-
* })
|
|
3519
|
-
* ```
|
|
3520
|
-
*/
|
|
3521
|
-
async sendServiceRequest(request) {
|
|
3522
|
-
const messageId = BigInt(Date.now());
|
|
3523
|
-
const messageIdBytes = new Uint8Array(8);
|
|
3524
|
-
const view = new DataView(messageIdBytes.buffer);
|
|
3525
|
-
view.setBigUint64(0, messageId, true);
|
|
3526
|
-
const [messageAddress] = await getProgramDerivedAddress({
|
|
3527
|
-
programAddress: this.programId,
|
|
3528
|
-
seeds: [
|
|
3529
|
-
getBytesEncoder().encode(new Uint8Array([99, 111, 109, 109, 95, 109, 101, 115, 115, 97, 103, 101])),
|
|
3530
|
-
// 'comm_message'
|
|
3531
|
-
getAddressEncoder().encode(request.sessionAddress),
|
|
3532
|
-
getBytesEncoder().encode(messageIdBytes)
|
|
3533
|
-
]
|
|
3534
|
-
});
|
|
3535
|
-
const instruction = getSendCommunicationMessageInstruction({
|
|
3536
|
-
message: messageAddress,
|
|
3537
|
-
session: request.sessionAddress,
|
|
3538
|
-
sender: this.wallet,
|
|
3539
|
-
messageId,
|
|
3540
|
-
senderType: 0 /* Human */,
|
|
3541
|
-
content: request.content,
|
|
3542
|
-
messageType: request.messageType ?? "service_request",
|
|
3543
|
-
attachments: request.attachments ?? []
|
|
3544
|
-
}, { programAddress: this.programId });
|
|
3545
|
-
const signature = await this.sendTransaction([instruction]);
|
|
3546
|
-
this.emit("service_requested", {
|
|
3547
|
-
type: "service_requested",
|
|
3548
|
-
timestamp: Date.now(),
|
|
3549
|
-
data: {
|
|
3550
|
-
messageAddress,
|
|
3551
|
-
sessionAddress: request.sessionAddress,
|
|
3552
|
-
signature
|
|
3553
|
-
}
|
|
3554
|
-
});
|
|
3555
|
-
return signature;
|
|
3556
|
-
}
|
|
3557
677
|
/**
|
|
3558
|
-
*
|
|
3559
|
-
*
|
|
3560
|
-
* Sends a message to an agent with support for file attachments.
|
|
3561
|
-
*
|
|
3562
|
-
* @param params - Message parameters
|
|
3563
|
-
* @returns Transaction signature
|
|
678
|
+
* Convert GhostSpeak agent data to PayAI registration format
|
|
3564
679
|
*/
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
]
|
|
3578
|
-
});
|
|
3579
|
-
const instruction = getSendCommunicationMessageInstruction({
|
|
3580
|
-
message: messageAddress,
|
|
3581
|
-
session: params.sessionAddress,
|
|
3582
|
-
sender: this.wallet,
|
|
3583
|
-
messageId,
|
|
3584
|
-
senderType: 0 /* Human */,
|
|
3585
|
-
content: params.content,
|
|
3586
|
-
messageType: params.messageType ?? "text",
|
|
3587
|
-
attachments: params.attachments ?? []
|
|
3588
|
-
}, { programAddress: this.programId });
|
|
3589
|
-
const signature = await this.sendTransaction([instruction]);
|
|
3590
|
-
this.emit("message_sent", {
|
|
3591
|
-
type: "message_sent",
|
|
3592
|
-
timestamp: Date.now(),
|
|
3593
|
-
data: {
|
|
3594
|
-
messageAddress,
|
|
3595
|
-
sessionAddress: params.sessionAddress,
|
|
3596
|
-
signature
|
|
680
|
+
toPayAIRegistration(agent) {
|
|
681
|
+
return {
|
|
682
|
+
agentAddress: agent.address,
|
|
683
|
+
serviceEndpoint: agent.serviceEndpoint,
|
|
684
|
+
capabilities: agent.capabilities,
|
|
685
|
+
acceptedTokens: agent.acceptedTokens,
|
|
686
|
+
pricing: agent.pricing,
|
|
687
|
+
metadata: {
|
|
688
|
+
name: agent.name,
|
|
689
|
+
description: agent.description,
|
|
690
|
+
logo: agent.logo,
|
|
691
|
+
website: agent.website
|
|
3597
692
|
}
|
|
3598
|
-
}
|
|
3599
|
-
return signature;
|
|
693
|
+
};
|
|
3600
694
|
}
|
|
3601
695
|
/**
|
|
3602
|
-
*
|
|
3603
|
-
*
|
|
3604
|
-
* @param sessionAddress - Session PDA address
|
|
696
|
+
* Make an authenticated request to PayAI marketplace
|
|
3605
697
|
*/
|
|
3606
|
-
async
|
|
3607
|
-
const
|
|
3608
|
-
|
|
3609
|
-
|
|
698
|
+
async makeRequest(path, options = {}) {
|
|
699
|
+
const controller = new AbortController();
|
|
700
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
701
|
+
const headers = {
|
|
702
|
+
"Content-Type": "application/json",
|
|
703
|
+
"X-Ghostspeak-Integration": "1.0"
|
|
704
|
+
};
|
|
705
|
+
if (this.config.apiKey) {
|
|
706
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
3610
707
|
}
|
|
3611
|
-
const data = accountInfo.value.data;
|
|
3612
|
-
return getCommunicationSessionDataDecoder().decode(
|
|
3613
|
-
typeof data === "string" ? Uint8Array.from(Buffer.from(data, "base64")) : new Uint8Array(data)
|
|
3614
|
-
);
|
|
3615
|
-
}
|
|
3616
|
-
// =====================================================
|
|
3617
|
-
// PRIVATE HELPER METHODS
|
|
3618
|
-
// =====================================================
|
|
3619
|
-
async sendTransaction(instructions) {
|
|
3620
708
|
try {
|
|
3621
|
-
const
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
}
|
|
3633
|
-
const signature = signatures[0];
|
|
3634
|
-
const wireTransaction = getBase64EncodedWireTransaction(signedTransaction);
|
|
3635
|
-
await this.rpc.sendTransaction(wireTransaction).send();
|
|
3636
|
-
await this.confirmTransaction(signature);
|
|
3637
|
-
return signature;
|
|
3638
|
-
} catch (error) {
|
|
3639
|
-
throw new Error(
|
|
3640
|
-
`Transaction failed: ${error instanceof Error ? error.message : String(error)}`
|
|
3641
|
-
);
|
|
3642
|
-
}
|
|
3643
|
-
}
|
|
3644
|
-
async confirmTransaction(signature) {
|
|
3645
|
-
for (let i = 0; i < 30; i++) {
|
|
3646
|
-
const status = await this.rpc.getSignatureStatuses([signature]).send();
|
|
3647
|
-
if (status.value[0]?.confirmationStatus === "confirmed" || status.value[0]?.confirmationStatus === "finalized") {
|
|
3648
|
-
return;
|
|
3649
|
-
}
|
|
3650
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
709
|
+
const response = await fetch(`${this.config.marketplaceUrl}${path}`, {
|
|
710
|
+
...options,
|
|
711
|
+
headers: {
|
|
712
|
+
...headers,
|
|
713
|
+
...options.headers
|
|
714
|
+
},
|
|
715
|
+
signal: controller.signal
|
|
716
|
+
});
|
|
717
|
+
return response;
|
|
718
|
+
} finally {
|
|
719
|
+
clearTimeout(timeoutId);
|
|
3651
720
|
}
|
|
3652
|
-
throw new Error("Transaction confirmation timeout");
|
|
3653
721
|
}
|
|
3654
722
|
};
|
|
3655
|
-
function
|
|
3656
|
-
return new
|
|
723
|
+
function createPayAIAgentSync(config = {}) {
|
|
724
|
+
return new PayAIAgentSync(config);
|
|
3657
725
|
}
|
|
3658
726
|
|
|
3659
727
|
// src/utils/test-ipfs-config.ts
|
|
@@ -3698,7 +766,7 @@ var IPFSOperationError = class extends Error {
|
|
|
3698
766
|
this.name = "IPFSOperationError";
|
|
3699
767
|
}
|
|
3700
768
|
};
|
|
3701
|
-
var
|
|
769
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
3702
770
|
maxRetries: 3,
|
|
3703
771
|
baseDelay: 1e3,
|
|
3704
772
|
maxDelay: 3e4,
|
|
@@ -3759,7 +827,7 @@ var CircuitBreaker = class {
|
|
|
3759
827
|
}
|
|
3760
828
|
};
|
|
3761
829
|
var RetryHandler = class {
|
|
3762
|
-
constructor(config =
|
|
830
|
+
constructor(config = DEFAULT_RETRY_CONFIG) {
|
|
3763
831
|
this.config = config;
|
|
3764
832
|
this.circuitBreaker = new CircuitBreaker();
|
|
3765
833
|
}
|
|
@@ -3884,7 +952,7 @@ var IPFSErrorHandler = class {
|
|
|
3884
952
|
retryHandler;
|
|
3885
953
|
fallbackHandler;
|
|
3886
954
|
constructor(retryConfig) {
|
|
3887
|
-
this.retryHandler = new RetryHandler({ ...
|
|
955
|
+
this.retryHandler = new RetryHandler({ ...DEFAULT_RETRY_CONFIG, ...retryConfig });
|
|
3888
956
|
this.fallbackHandler = new FallbackHandler();
|
|
3889
957
|
}
|
|
3890
958
|
async executeWithErrorHandling(operation, context, fallbackValue) {
|
|
@@ -4096,12 +1164,12 @@ function assembleStyles() {
|
|
|
4096
1164
|
if (colorString.length === 3) {
|
|
4097
1165
|
colorString = [...colorString].map((character) => character + character).join("");
|
|
4098
1166
|
}
|
|
4099
|
-
const
|
|
1167
|
+
const integer = Number.parseInt(colorString, 16);
|
|
4100
1168
|
return [
|
|
4101
1169
|
/* eslint-disable no-bitwise */
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
1170
|
+
integer >> 16 & 255,
|
|
1171
|
+
integer >> 8 & 255,
|
|
1172
|
+
integer & 255
|
|
4105
1173
|
/* eslint-enable no-bitwise */
|
|
4106
1174
|
];
|
|
4107
1175
|
},
|
|
@@ -4288,30 +1356,30 @@ var supports_color_default = supportsColor;
|
|
|
4288
1356
|
|
|
4289
1357
|
// ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/utilities.js
|
|
4290
1358
|
function stringReplaceAll(string, substring, replacer) {
|
|
4291
|
-
let
|
|
4292
|
-
if (
|
|
1359
|
+
let index = string.indexOf(substring);
|
|
1360
|
+
if (index === -1) {
|
|
4293
1361
|
return string;
|
|
4294
1362
|
}
|
|
4295
1363
|
const substringLength = substring.length;
|
|
4296
1364
|
let endIndex = 0;
|
|
4297
1365
|
let returnValue = "";
|
|
4298
1366
|
do {
|
|
4299
|
-
returnValue += string.slice(endIndex,
|
|
4300
|
-
endIndex =
|
|
4301
|
-
|
|
4302
|
-
} while (
|
|
1367
|
+
returnValue += string.slice(endIndex, index) + substring + replacer;
|
|
1368
|
+
endIndex = index + substringLength;
|
|
1369
|
+
index = string.indexOf(substring, endIndex);
|
|
1370
|
+
} while (index !== -1);
|
|
4303
1371
|
returnValue += string.slice(endIndex);
|
|
4304
1372
|
return returnValue;
|
|
4305
1373
|
}
|
|
4306
|
-
function stringEncaseCRLFWithFirstIndex(string, prefix, postfix,
|
|
1374
|
+
function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
|
|
4307
1375
|
let endIndex = 0;
|
|
4308
1376
|
let returnValue = "";
|
|
4309
1377
|
do {
|
|
4310
|
-
const gotCR = string[
|
|
4311
|
-
returnValue += string.slice(endIndex, gotCR ?
|
|
4312
|
-
endIndex =
|
|
4313
|
-
|
|
4314
|
-
} while (
|
|
1378
|
+
const gotCR = string[index - 1] === "\r";
|
|
1379
|
+
returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
|
|
1380
|
+
endIndex = index + 1;
|
|
1381
|
+
index = string.indexOf("\n", endIndex);
|
|
1382
|
+
} while (index !== -1);
|
|
4315
1383
|
returnValue += string.slice(endIndex);
|
|
4316
1384
|
return returnValue;
|
|
4317
1385
|
}
|
|
@@ -4876,7 +1944,7 @@ var IPFSProvider = class {
|
|
|
4876
1944
|
client;
|
|
4877
1945
|
isPrivateNetwork;
|
|
4878
1946
|
constructor(options) {
|
|
4879
|
-
const
|
|
1947
|
+
const createClient = __require("kubo-rpc-client").create;
|
|
4880
1948
|
const ipfsNodeUrl = options?.ipfsNodeUrl || "http://localhost:5001";
|
|
4881
1949
|
const headers = options?.headers || {};
|
|
4882
1950
|
this.isPrivateNetwork = options?.usePrivateNetwork || false;
|
|
@@ -4891,7 +1959,7 @@ var IPFSProvider = class {
|
|
|
4891
1959
|
if (typeof headers !== "object" || headers === null) {
|
|
4892
1960
|
throw new Error("Headers must be a valid object");
|
|
4893
1961
|
}
|
|
4894
|
-
this.client =
|
|
1962
|
+
this.client = createClient({
|
|
4895
1963
|
url: ipfsNodeUrl,
|
|
4896
1964
|
headers
|
|
4897
1965
|
});
|
|
@@ -4923,9 +1991,9 @@ var IPFSProvider = class {
|
|
|
4923
1991
|
* This is not foolproof but can catch obvious cases
|
|
4924
1992
|
*/
|
|
4925
1993
|
isDataUnencrypted(data) {
|
|
4926
|
-
const
|
|
1994
|
+
const text = new TextDecoder("utf-8", { fatal: false }).decode(data.slice(0, 100));
|
|
4927
1995
|
const commonPatterns = ["{", "<", "BEGIN", "name", "address", "email", "password"];
|
|
4928
|
-
return commonPatterns.some((pattern) =>
|
|
1996
|
+
return commonPatterns.some((pattern) => text.toLowerCase().includes(pattern.toLowerCase()));
|
|
4929
1997
|
}
|
|
4930
1998
|
async retrieve(hash) {
|
|
4931
1999
|
try {
|
|
@@ -5278,6 +2346,6 @@ function clearFeatureGateCache() {
|
|
|
5278
2346
|
featureCache.clear();
|
|
5279
2347
|
}
|
|
5280
2348
|
|
|
5281
|
-
export {
|
|
2349
|
+
export { CircuitBreaker, DEFAULT_RETRY_CONFIG, FEATURE_GATES, FallbackHandler, IPFSErrorHandler, IPFSOperationError, IPFSProvider, LocalStorageProvider, PayAIAgentSync, PayAIWebhookHandler, PrivateDataQuery, PrivateMetadataStorage, RetryHandler, TEST_IPFS_CONFIG, WalletFundingService, X402TransactionIndexer, checkFeatureGate, clearFeatureGateCache, createIPFSErrorHandler, createMockPayAIWebhook, createPayAIAgentSync, createPayAIWebhookHandler, createPrivacyManifest, createTestIPFSConfig, defaultFundingService, ensureMinimumBalance, estimateStorageCost, fundWallet, generateTestWebhookSignature, isIPFSError, withIPFSErrorHandling };
|
|
5282
2350
|
//# sourceMappingURL=index.js.map
|
|
5283
2351
|
//# sourceMappingURL=index.js.map
|