@mixrpay/agent-sdk 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -4
- package/dist/index.cjs +301 -17
- package/dist/index.d.cts +210 -3
- package/dist/index.d.ts +210 -3
- package/dist/index.js +299 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,9 +41,13 @@ await wallet.fetch(url, options);
|
|
|
41
41
|
// Get wallet balance
|
|
42
42
|
const balance = await wallet.getBalance();
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
const
|
|
46
|
-
|
|
44
|
+
// Pre-flight balance check
|
|
45
|
+
const check = await wallet.canAfford(0.50);
|
|
46
|
+
if (check.canAfford) { /* proceed */ }
|
|
47
|
+
|
|
48
|
+
// Run diagnostics
|
|
49
|
+
const diag = await wallet.runDiagnostics();
|
|
50
|
+
console.log(diag.recommendations);
|
|
47
51
|
```
|
|
48
52
|
|
|
49
53
|
## Configuration
|
|
@@ -63,6 +67,7 @@ import {
|
|
|
63
67
|
AgentWallet,
|
|
64
68
|
InsufficientBalanceError,
|
|
65
69
|
SessionLimitExceededError,
|
|
70
|
+
MixrPayError,
|
|
66
71
|
} from '@mixrpay/agent-sdk';
|
|
67
72
|
|
|
68
73
|
try {
|
|
@@ -71,12 +76,27 @@ try {
|
|
|
71
76
|
if (error instanceof InsufficientBalanceError) {
|
|
72
77
|
console.log(`Need $${error.required}, have $${error.available}`);
|
|
73
78
|
}
|
|
79
|
+
|
|
80
|
+
// Check if error is retryable
|
|
81
|
+
if (error instanceof MixrPayError && error.isRetryable()) {
|
|
82
|
+
const delay = error.retryAfterMs || 1000;
|
|
83
|
+
await sleep(delay);
|
|
84
|
+
return retry();
|
|
85
|
+
}
|
|
74
86
|
}
|
|
75
87
|
```
|
|
76
88
|
|
|
89
|
+
## What's New in v0.6.0
|
|
90
|
+
|
|
91
|
+
- `canAfford(amountUsd)` - Pre-flight balance check
|
|
92
|
+
- `isRetryable()` on all errors - Determine if operation should be retried
|
|
93
|
+
- `retryAfterMs` on errors - Suggested retry delay
|
|
94
|
+
- Enhanced `runDiagnostics()` - Session limits, latency, recommendations
|
|
95
|
+
- `requestId` and `correlationId` in payment events
|
|
96
|
+
|
|
77
97
|
## Documentation
|
|
78
98
|
|
|
79
|
-
[mixrpay.com/
|
|
99
|
+
[mixrpay.com/docs](https://www.mixrpay.com/docs)
|
|
80
100
|
|
|
81
101
|
## License
|
|
82
102
|
|
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,8 @@ __export(index_exports, {
|
|
|
32
32
|
SessionNotFoundError: () => SessionNotFoundError,
|
|
33
33
|
SessionRevokedError: () => SessionRevokedError,
|
|
34
34
|
SpendingLimitExceededError: () => SpendingLimitExceededError,
|
|
35
|
+
X402ProtocolError: () => X402ProtocolError,
|
|
36
|
+
getErrorMessage: () => getErrorMessage,
|
|
35
37
|
isMixrPayError: () => isMixrPayError
|
|
36
38
|
});
|
|
37
39
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -43,14 +45,43 @@ var import_accounts = require("viem/accounts");
|
|
|
43
45
|
var MixrPayError = class extends Error {
|
|
44
46
|
/** Error code for programmatic handling */
|
|
45
47
|
code;
|
|
46
|
-
|
|
48
|
+
/** Optional hint for how long to wait before retrying (in milliseconds) */
|
|
49
|
+
retryAfterMs;
|
|
50
|
+
constructor(message, code = "MIXRPAY_ERROR", retryAfterMs) {
|
|
47
51
|
super(message);
|
|
48
52
|
this.name = "MixrPayError";
|
|
49
53
|
this.code = code;
|
|
54
|
+
this.retryAfterMs = retryAfterMs;
|
|
50
55
|
if (Error.captureStackTrace) {
|
|
51
56
|
Error.captureStackTrace(this, this.constructor);
|
|
52
57
|
}
|
|
53
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Check if this error is retryable.
|
|
61
|
+
*
|
|
62
|
+
* Returns true if the operation might succeed on retry (e.g., transient network issues).
|
|
63
|
+
* Returns false if the error requires user action to resolve (e.g., insufficient balance).
|
|
64
|
+
*
|
|
65
|
+
* @returns true if the operation should be retried, false otherwise
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* try {
|
|
70
|
+
* await wallet.fetch(...);
|
|
71
|
+
* } catch (error) {
|
|
72
|
+
* if (error instanceof MixrPayError && error.isRetryable()) {
|
|
73
|
+
* // Retry the operation
|
|
74
|
+
* await retry();
|
|
75
|
+
* } else {
|
|
76
|
+
* // Handle permanent failure
|
|
77
|
+
* throw error;
|
|
78
|
+
* }
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
isRetryable() {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
54
85
|
};
|
|
55
86
|
var InsufficientBalanceError = class extends MixrPayError {
|
|
56
87
|
/** Amount required for the payment in USD */
|
|
@@ -108,6 +139,14 @@ var SpendingLimitExceededError = class extends MixrPayError {
|
|
|
108
139
|
this.limit = limit;
|
|
109
140
|
this.attempted = attempted;
|
|
110
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Daily limits reset at midnight, so those are retryable (after waiting).
|
|
144
|
+
* Other limit types require user action (new session key, config change).
|
|
145
|
+
* @returns true only for daily limits
|
|
146
|
+
*/
|
|
147
|
+
isRetryable() {
|
|
148
|
+
return this.limitType === "daily";
|
|
149
|
+
}
|
|
111
150
|
};
|
|
112
151
|
var PaymentFailedError = class extends MixrPayError {
|
|
113
152
|
/** Detailed reason for the failure */
|
|
@@ -124,6 +163,13 @@ var PaymentFailedError = class extends MixrPayError {
|
|
|
124
163
|
this.reason = reason;
|
|
125
164
|
this.txHash = txHash;
|
|
126
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Payment failures are often transient (network issues, temporary server errors).
|
|
168
|
+
* @returns true - payment failures should generally be retried
|
|
169
|
+
*/
|
|
170
|
+
isRetryable() {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
127
173
|
};
|
|
128
174
|
var InvalidSessionKeyError = class extends MixrPayError {
|
|
129
175
|
/** Detailed reason why the key is invalid */
|
|
@@ -148,6 +194,13 @@ var X402ProtocolError = class extends MixrPayError {
|
|
|
148
194
|
this.name = "X402ProtocolError";
|
|
149
195
|
this.reason = reason;
|
|
150
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Protocol errors may be caused by temporary server issues.
|
|
199
|
+
* @returns true - worth retrying in case server recovers
|
|
200
|
+
*/
|
|
201
|
+
isRetryable() {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
151
204
|
};
|
|
152
205
|
var SessionExpiredError = class extends MixrPayError {
|
|
153
206
|
/** ID of the expired session */
|
|
@@ -215,6 +268,15 @@ var SessionRevokedError = class extends MixrPayError {
|
|
|
215
268
|
function isMixrPayError(error) {
|
|
216
269
|
return error instanceof MixrPayError;
|
|
217
270
|
}
|
|
271
|
+
function getErrorMessage(error) {
|
|
272
|
+
if (error instanceof MixrPayError) {
|
|
273
|
+
return error.message;
|
|
274
|
+
}
|
|
275
|
+
if (error instanceof Error) {
|
|
276
|
+
return error.message;
|
|
277
|
+
}
|
|
278
|
+
return String(error);
|
|
279
|
+
}
|
|
218
280
|
|
|
219
281
|
// src/session-key.ts
|
|
220
282
|
var USDC_ADDRESSES = {
|
|
@@ -445,7 +507,7 @@ function getAmountUsd(requirements) {
|
|
|
445
507
|
}
|
|
446
508
|
|
|
447
509
|
// src/agent-wallet.ts
|
|
448
|
-
var SDK_VERSION = "0.
|
|
510
|
+
var SDK_VERSION = "0.6.1";
|
|
449
511
|
var DEFAULT_BASE_URL = process.env.MIXRPAY_BASE_URL || "https://www.mixrpay.com";
|
|
450
512
|
var DEFAULT_TIMEOUT = 3e4;
|
|
451
513
|
var NETWORKS = {
|
|
@@ -637,7 +699,19 @@ var AgentWallet = class {
|
|
|
637
699
|
});
|
|
638
700
|
if (!registerResponse.ok) {
|
|
639
701
|
const error = await registerResponse.json().catch(() => ({}));
|
|
640
|
-
|
|
702
|
+
const errorMessage = error.error || `Registration failed with status ${registerResponse.status}`;
|
|
703
|
+
const requestId = error.request_id;
|
|
704
|
+
const errorCode = error.code;
|
|
705
|
+
let helpText = "";
|
|
706
|
+
if (registerResponse.status === 503) {
|
|
707
|
+
helpText = " The service may be temporarily unavailable. Please try again later.";
|
|
708
|
+
} else if (registerResponse.status === 500) {
|
|
709
|
+
helpText = " This is a server error. Please contact support with the request ID.";
|
|
710
|
+
} else if (errorCode === "MISSING_CHALLENGE" || errorCode === "MISSING_SIGNATURE") {
|
|
711
|
+
helpText = " This may indicate an SDK bug. Please update to the latest version.";
|
|
712
|
+
}
|
|
713
|
+
const fullMessage = requestId ? `${errorMessage} (request_id: ${requestId})${helpText}` : `${errorMessage}${helpText}`;
|
|
714
|
+
throw new MixrPayError(fullMessage);
|
|
641
715
|
}
|
|
642
716
|
const data = await registerResponse.json();
|
|
643
717
|
return {
|
|
@@ -645,6 +719,52 @@ var AgentWallet = class {
|
|
|
645
719
|
depositAddress: data.deposit_address
|
|
646
720
|
};
|
|
647
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* Check if the MixrPay server is properly configured for agent registration.
|
|
724
|
+
*
|
|
725
|
+
* Use this to diagnose registration issues before attempting to register.
|
|
726
|
+
*
|
|
727
|
+
* @param baseUrl - MixrPay API base URL (default: https://www.mixrpay.com)
|
|
728
|
+
* @returns Server health status including agent registration availability
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```typescript
|
|
732
|
+
* const status = await AgentWallet.checkServerHealth();
|
|
733
|
+
* if (!status.agentRegistrationAvailable) {
|
|
734
|
+
* console.error('Agent registration is not available:', status);
|
|
735
|
+
* }
|
|
736
|
+
* ```
|
|
737
|
+
*/
|
|
738
|
+
static async checkServerHealth(baseUrl) {
|
|
739
|
+
const url = (baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
740
|
+
try {
|
|
741
|
+
const response = await fetch(`${url}/api/health/ready?details=true`);
|
|
742
|
+
if (!response.ok) {
|
|
743
|
+
return {
|
|
744
|
+
healthy: false,
|
|
745
|
+
database: "unknown",
|
|
746
|
+
agentRegistrationAvailable: false,
|
|
747
|
+
privyConfigured: false,
|
|
748
|
+
error: `Health check failed with status ${response.status}`
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
const data = await response.json();
|
|
752
|
+
return {
|
|
753
|
+
healthy: data.status === "ready",
|
|
754
|
+
database: data.database || "unknown",
|
|
755
|
+
agentRegistrationAvailable: data.services?.agentRegistration?.available ?? false,
|
|
756
|
+
privyConfigured: data.services?.privy?.configured ?? false
|
|
757
|
+
};
|
|
758
|
+
} catch (error) {
|
|
759
|
+
return {
|
|
760
|
+
healthy: false,
|
|
761
|
+
database: "unreachable",
|
|
762
|
+
agentRegistrationAvailable: false,
|
|
763
|
+
privyConfigured: false,
|
|
764
|
+
error: error instanceof Error ? error.message : "Failed to reach server"
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
}
|
|
648
768
|
/**
|
|
649
769
|
* Get a session key for an already-registered agent.
|
|
650
770
|
*
|
|
@@ -819,6 +939,67 @@ var AgentWallet = class {
|
|
|
819
939
|
}
|
|
820
940
|
return true;
|
|
821
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* Withdraw USDC from agent's MixrPay wallet to their external wallet.
|
|
944
|
+
*
|
|
945
|
+
* SECURITY: Withdrawals can ONLY go to the agent's own registration wallet
|
|
946
|
+
* (the wallet used during `register()`). This prevents prompt injection
|
|
947
|
+
* attacks where a compromised agent might be tricked into withdrawing
|
|
948
|
+
* to an attacker's address.
|
|
949
|
+
*
|
|
950
|
+
* @param options - Withdrawal options
|
|
951
|
+
* @returns Withdrawal result with transaction hash
|
|
952
|
+
* @throws {MixrPayError} If withdrawal fails
|
|
953
|
+
*
|
|
954
|
+
* @example
|
|
955
|
+
* ```typescript
|
|
956
|
+
* const result = await AgentWallet.withdraw({
|
|
957
|
+
* privateKey: process.env.AGENT_WALLET_KEY as `0x${string}`,
|
|
958
|
+
* amountUsd: 50.00,
|
|
959
|
+
* });
|
|
960
|
+
*
|
|
961
|
+
* console.log(`Withdrew $${result.amountUsd}`);
|
|
962
|
+
* console.log(`Transaction: ${result.txHash}`);
|
|
963
|
+
* console.log(`Remaining balance: $${result.remainingBalanceUsd}`);
|
|
964
|
+
* ```
|
|
965
|
+
*/
|
|
966
|
+
static async withdraw(options) {
|
|
967
|
+
const { privateKey, amountUsd } = options;
|
|
968
|
+
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
969
|
+
const account = (0, import_accounts2.privateKeyToAccount)(privateKey);
|
|
970
|
+
const walletAddress = account.address;
|
|
971
|
+
const challengeResponse = await fetch(
|
|
972
|
+
`${baseUrl}/api/v1/agent/challenge?wallet=${walletAddress}&action=withdraw`
|
|
973
|
+
);
|
|
974
|
+
if (!challengeResponse.ok) {
|
|
975
|
+
const error = await challengeResponse.json().catch(() => ({}));
|
|
976
|
+
throw new MixrPayError(error.error || `Failed to get challenge: ${challengeResponse.status}`);
|
|
977
|
+
}
|
|
978
|
+
const { challenge, message } = await challengeResponse.json();
|
|
979
|
+
const signature = await (0, import_accounts2.signMessage)({ message, privateKey });
|
|
980
|
+
const withdrawResponse = await fetch(`${baseUrl}/api/v1/agent/withdraw`, {
|
|
981
|
+
method: "POST",
|
|
982
|
+
headers: { "Content-Type": "application/json" },
|
|
983
|
+
body: JSON.stringify({
|
|
984
|
+
challenge,
|
|
985
|
+
external_wallet: walletAddress,
|
|
986
|
+
signature,
|
|
987
|
+
to_address: walletAddress,
|
|
988
|
+
// Always withdraw to self
|
|
989
|
+
amount_usd: amountUsd
|
|
990
|
+
})
|
|
991
|
+
});
|
|
992
|
+
if (!withdrawResponse.ok) {
|
|
993
|
+
const error = await withdrawResponse.json().catch(() => ({}));
|
|
994
|
+
throw new MixrPayError(error.error || `Withdrawal failed: ${withdrawResponse.status}`);
|
|
995
|
+
}
|
|
996
|
+
const data = await withdrawResponse.json();
|
|
997
|
+
return {
|
|
998
|
+
txHash: data.tx_hash,
|
|
999
|
+
amountUsd: data.amount_usd,
|
|
1000
|
+
remainingBalanceUsd: data.remaining_balance_usd
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
822
1003
|
// ===========================================================================
|
|
823
1004
|
// Core Methods
|
|
824
1005
|
// ===========================================================================
|
|
@@ -857,6 +1038,8 @@ var AgentWallet = class {
|
|
|
857
1038
|
*/
|
|
858
1039
|
async fetch(url, init) {
|
|
859
1040
|
this.logger.debug(`Fetching ${init?.method || "GET"} ${url}`);
|
|
1041
|
+
const requestId = crypto.randomUUID();
|
|
1042
|
+
const correlationId = this.extractCorrelationId(init?.headers);
|
|
860
1043
|
const controller = new AbortController();
|
|
861
1044
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
862
1045
|
try {
|
|
@@ -867,7 +1050,7 @@ var AgentWallet = class {
|
|
|
867
1050
|
this.logger.debug(`Initial response: ${response.status}`);
|
|
868
1051
|
if (response.status === 402) {
|
|
869
1052
|
this.logger.info(`Payment required for ${url}`);
|
|
870
|
-
response = await this.handlePaymentRequired(url, init, response);
|
|
1053
|
+
response = await this.handlePaymentRequired(url, init, response, requestId, correlationId);
|
|
871
1054
|
}
|
|
872
1055
|
return response;
|
|
873
1056
|
} catch (error) {
|
|
@@ -879,10 +1062,27 @@ var AgentWallet = class {
|
|
|
879
1062
|
clearTimeout(timeoutId);
|
|
880
1063
|
}
|
|
881
1064
|
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Extract correlation ID from request headers.
|
|
1067
|
+
*/
|
|
1068
|
+
extractCorrelationId(headers) {
|
|
1069
|
+
if (!headers) return void 0;
|
|
1070
|
+
if (headers instanceof Headers) {
|
|
1071
|
+
return headers.get("X-Correlation-Id") || headers.get("x-correlation-id") || void 0;
|
|
1072
|
+
}
|
|
1073
|
+
if (Array.isArray(headers)) {
|
|
1074
|
+
const entry = headers.find(
|
|
1075
|
+
([key]) => key.toLowerCase() === "x-correlation-id"
|
|
1076
|
+
);
|
|
1077
|
+
return entry ? entry[1] : void 0;
|
|
1078
|
+
}
|
|
1079
|
+
const record = headers;
|
|
1080
|
+
return record["X-Correlation-Id"] || record["x-correlation-id"] || void 0;
|
|
1081
|
+
}
|
|
882
1082
|
/**
|
|
883
1083
|
* Handle a 402 Payment Required response.
|
|
884
1084
|
*/
|
|
885
|
-
async handlePaymentRequired(url, init, response) {
|
|
1085
|
+
async handlePaymentRequired(url, init, response, requestId, correlationId) {
|
|
886
1086
|
let requirements;
|
|
887
1087
|
try {
|
|
888
1088
|
requirements = await parse402Response(response);
|
|
@@ -938,7 +1138,9 @@ var AgentWallet = class {
|
|
|
938
1138
|
txHash: retryResponse.headers.get("X-Payment-TxHash"),
|
|
939
1139
|
timestamp: /* @__PURE__ */ new Date(),
|
|
940
1140
|
description: requirements.description,
|
|
941
|
-
url
|
|
1141
|
+
url,
|
|
1142
|
+
requestId,
|
|
1143
|
+
correlationId
|
|
942
1144
|
};
|
|
943
1145
|
this.payments.push(payment);
|
|
944
1146
|
this.totalSpentUsd += amountUsd;
|
|
@@ -1037,6 +1239,36 @@ var AgentWallet = class {
|
|
|
1037
1239
|
this.logger.debug("Using estimated balance based on tracking");
|
|
1038
1240
|
return Math.max(0, 100 - this.totalSpentUsd);
|
|
1039
1241
|
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Check if the wallet can afford a specific amount.
|
|
1244
|
+
*
|
|
1245
|
+
* This is a convenience method to check balance before making a request
|
|
1246
|
+
* when you know the expected cost.
|
|
1247
|
+
*
|
|
1248
|
+
* @param amountUsd - Amount to check in USD
|
|
1249
|
+
* @returns Object with affordability information
|
|
1250
|
+
*
|
|
1251
|
+
* @example
|
|
1252
|
+
* ```typescript
|
|
1253
|
+
* const check = await wallet.canAfford(5.00);
|
|
1254
|
+
* if (check.canAfford) {
|
|
1255
|
+
* console.log(`Can afford! Will have $${check.remainingAfter.toFixed(2)} left`);
|
|
1256
|
+
* await wallet.fetch(url);
|
|
1257
|
+
* } else {
|
|
1258
|
+
* console.log(`Need $${check.shortfall.toFixed(2)} more`);
|
|
1259
|
+
* }
|
|
1260
|
+
* ```
|
|
1261
|
+
*/
|
|
1262
|
+
async canAfford(amountUsd) {
|
|
1263
|
+
const balance = await this.getBalance();
|
|
1264
|
+
const canAfford = balance >= amountUsd;
|
|
1265
|
+
return {
|
|
1266
|
+
canAfford,
|
|
1267
|
+
balance,
|
|
1268
|
+
shortfall: canAfford ? 0 : amountUsd - balance,
|
|
1269
|
+
remainingAfter: canAfford ? balance - amountUsd : 0
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1040
1272
|
/**
|
|
1041
1273
|
* Get information about the session key.
|
|
1042
1274
|
*
|
|
@@ -1177,56 +1409,100 @@ var AgentWallet = class {
|
|
|
1177
1409
|
async runDiagnostics() {
|
|
1178
1410
|
this.logger.info("Running diagnostics...");
|
|
1179
1411
|
const issues = [];
|
|
1412
|
+
const recommendations = [];
|
|
1180
1413
|
const checks = {};
|
|
1414
|
+
let latencyMs;
|
|
1415
|
+
let sessionLimits;
|
|
1181
1416
|
checks.sessionKeyFormat = true;
|
|
1182
1417
|
try {
|
|
1418
|
+
const startTime = Date.now();
|
|
1183
1419
|
const response = await fetch(`${this.baseUrl}/health`, {
|
|
1184
1420
|
method: "GET",
|
|
1185
1421
|
signal: AbortSignal.timeout(5e3)
|
|
1186
1422
|
});
|
|
1423
|
+
latencyMs = Date.now() - startTime;
|
|
1187
1424
|
checks.apiConnectivity = response.ok;
|
|
1188
1425
|
if (!response.ok) {
|
|
1189
1426
|
issues.push(`API server returned ${response.status}. Check baseUrl configuration.`);
|
|
1427
|
+
recommendations.push("Verify the baseUrl configuration points to a valid MixrPay server.");
|
|
1428
|
+
}
|
|
1429
|
+
if (latencyMs > 2e3) {
|
|
1430
|
+
issues.push(`High API latency: ${latencyMs}ms. This may cause timeouts.`);
|
|
1431
|
+
recommendations.push("Consider using a server closer to your region or check network connectivity.");
|
|
1190
1432
|
}
|
|
1191
1433
|
} catch {
|
|
1192
1434
|
checks.apiConnectivity = false;
|
|
1193
1435
|
issues.push("Cannot connect to MixrPay API. Check your network connection and baseUrl.");
|
|
1436
|
+
recommendations.push("Verify network connectivity and that the MixrPay server is running.");
|
|
1194
1437
|
}
|
|
1195
1438
|
try {
|
|
1196
1439
|
const info = await this.getSessionKeyInfo(true);
|
|
1197
1440
|
checks.sessionKeyValid = info.isValid;
|
|
1198
1441
|
if (!info.isValid) {
|
|
1199
1442
|
issues.push("Session key is invalid or has been revoked.");
|
|
1443
|
+
recommendations.push("Request a new session key from the wallet owner or create one at /wallet/sessions.");
|
|
1444
|
+
}
|
|
1445
|
+
const now = /* @__PURE__ */ new Date();
|
|
1446
|
+
let expiresInHours = null;
|
|
1447
|
+
if (info.expiresAt) {
|
|
1448
|
+
expiresInHours = (info.expiresAt.getTime() - now.getTime()) / (1e3 * 60 * 60);
|
|
1449
|
+
if (info.expiresAt < now) {
|
|
1450
|
+
checks.sessionKeyValid = false;
|
|
1451
|
+
issues.push(`Session key expired on ${info.expiresAt.toISOString()}`);
|
|
1452
|
+
recommendations.push("Create a new session key to continue making payments.");
|
|
1453
|
+
} else if (expiresInHours < 24) {
|
|
1454
|
+
issues.push(`Session key expires in ${expiresInHours.toFixed(1)} hours.`);
|
|
1455
|
+
recommendations.push("Consider creating a new session key before the current one expires.");
|
|
1456
|
+
}
|
|
1200
1457
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1458
|
+
const stats = await this.getSpendingStats();
|
|
1459
|
+
sessionLimits = {
|
|
1460
|
+
remainingDailyUsd: stats.remainingDailyUsd,
|
|
1461
|
+
remainingTotalUsd: stats.remainingTotalUsd,
|
|
1462
|
+
expiresAt: info.expiresAt,
|
|
1463
|
+
expiresInHours
|
|
1464
|
+
};
|
|
1465
|
+
if (stats.remainingDailyUsd !== null && stats.remainingDailyUsd < 1) {
|
|
1466
|
+
issues.push(`Daily limit nearly exhausted: $${stats.remainingDailyUsd.toFixed(2)} remaining.`);
|
|
1467
|
+
recommendations.push("Wait until tomorrow for daily limit to reset, or request a higher daily limit.");
|
|
1468
|
+
}
|
|
1469
|
+
if (stats.remainingTotalUsd !== null && stats.remainingTotalUsd < 1) {
|
|
1470
|
+
issues.push(`Total limit nearly exhausted: $${stats.remainingTotalUsd.toFixed(2)} remaining.`);
|
|
1471
|
+
recommendations.push("Request a new session key with a higher total limit.");
|
|
1204
1472
|
}
|
|
1205
1473
|
} catch {
|
|
1206
1474
|
checks.sessionKeyValid = false;
|
|
1207
1475
|
issues.push("Could not verify session key validity.");
|
|
1476
|
+
recommendations.push("Check network connectivity and try again.");
|
|
1208
1477
|
}
|
|
1478
|
+
let balance = 0;
|
|
1209
1479
|
try {
|
|
1210
|
-
|
|
1480
|
+
balance = await this.getBalance();
|
|
1211
1481
|
checks.hasBalance = balance > 0;
|
|
1212
1482
|
if (balance <= 0) {
|
|
1213
1483
|
issues.push("Wallet has no USDC balance. Top up at your MixrPay server /wallet");
|
|
1484
|
+
recommendations.push("Deposit USDC to your wallet address to enable payments.");
|
|
1214
1485
|
} else if (balance < 1) {
|
|
1215
1486
|
issues.push(`Low balance: $${balance.toFixed(2)}. Consider topping up.`);
|
|
1487
|
+
recommendations.push("Top up your wallet balance to avoid payment failures.");
|
|
1216
1488
|
}
|
|
1217
1489
|
} catch {
|
|
1218
1490
|
checks.hasBalance = false;
|
|
1219
1491
|
issues.push("Could not fetch wallet balance.");
|
|
1492
|
+
recommendations.push("Check network connectivity and try again.");
|
|
1220
1493
|
}
|
|
1221
1494
|
const healthy = issues.length === 0;
|
|
1222
|
-
this.logger.info("Diagnostics complete:", { healthy, issues });
|
|
1495
|
+
this.logger.info("Diagnostics complete:", { healthy, issues, latencyMs });
|
|
1223
1496
|
return {
|
|
1224
1497
|
healthy,
|
|
1225
1498
|
issues,
|
|
1226
1499
|
checks,
|
|
1227
1500
|
sdkVersion: SDK_VERSION,
|
|
1228
1501
|
network: this.getNetwork().name,
|
|
1229
|
-
walletAddress: this.walletAddress
|
|
1502
|
+
walletAddress: this.walletAddress,
|
|
1503
|
+
sessionLimits,
|
|
1504
|
+
latencyMs,
|
|
1505
|
+
recommendations
|
|
1230
1506
|
};
|
|
1231
1507
|
}
|
|
1232
1508
|
/**
|
|
@@ -1635,7 +1911,9 @@ var AgentWallet = class {
|
|
|
1635
1911
|
txHash: response.headers.get("X-Payment-TxHash"),
|
|
1636
1912
|
timestamp: /* @__PURE__ */ new Date(),
|
|
1637
1913
|
description: feature || "API call",
|
|
1638
|
-
url
|
|
1914
|
+
url,
|
|
1915
|
+
requestId: crypto.randomUUID(),
|
|
1916
|
+
correlationId: this.extractCorrelationId(customHeaders)
|
|
1639
1917
|
};
|
|
1640
1918
|
this.payments.push(payment);
|
|
1641
1919
|
this.totalSpentUsd += amountUsd;
|
|
@@ -1834,7 +2112,8 @@ Timestamp: ${timestamp}`;
|
|
|
1834
2112
|
txHash: mixrpay.txHash,
|
|
1835
2113
|
timestamp: /* @__PURE__ */ new Date(),
|
|
1836
2114
|
description: `MCP: ${toolName}`,
|
|
1837
|
-
url: `${this.baseUrl}/api/mcp
|
|
2115
|
+
url: `${this.baseUrl}/api/mcp`,
|
|
2116
|
+
requestId: crypto.randomUUID()
|
|
1838
2117
|
};
|
|
1839
2118
|
this.payments.push(payment);
|
|
1840
2119
|
this.totalSpentUsd += mixrpay.chargedUsd;
|
|
@@ -1951,7 +2230,8 @@ Timestamp: ${timestamp}`;
|
|
|
1951
2230
|
txHash: data.tx_hash,
|
|
1952
2231
|
timestamp: /* @__PURE__ */ new Date(),
|
|
1953
2232
|
description: `Agent run: ${data.run_id}`,
|
|
1954
|
-
url: `${this.baseUrl}/api/v2/agent/run
|
|
2233
|
+
url: `${this.baseUrl}/api/v2/agent/run`,
|
|
2234
|
+
requestId: idempotencyKey || crypto.randomUUID()
|
|
1955
2235
|
};
|
|
1956
2236
|
this.payments.push(payment);
|
|
1957
2237
|
this.totalSpentUsd += data.cost.total_usd;
|
|
@@ -2043,7 +2323,8 @@ Timestamp: ${timestamp}`;
|
|
|
2043
2323
|
txHash: data.tx_hash,
|
|
2044
2324
|
timestamp: /* @__PURE__ */ new Date(),
|
|
2045
2325
|
description: `Agent run: ${data.run_id}`,
|
|
2046
|
-
url: `${this.baseUrl}/api/v2/agent/run
|
|
2326
|
+
url: `${this.baseUrl}/api/v2/agent/run`,
|
|
2327
|
+
requestId: body.idempotency_key || crypto.randomUUID()
|
|
2047
2328
|
};
|
|
2048
2329
|
this.payments.push(payment);
|
|
2049
2330
|
this.totalSpentUsd += data.total_cost_usd;
|
|
@@ -2219,7 +2500,8 @@ Timestamp: ${timestamp}`;
|
|
|
2219
2500
|
txHash: mixrpay.txHash,
|
|
2220
2501
|
timestamp: /* @__PURE__ */ new Date(),
|
|
2221
2502
|
description: `MCP: ${toolName}`,
|
|
2222
|
-
url: `${this.baseUrl}/api/mcp
|
|
2503
|
+
url: `${this.baseUrl}/api/mcp`,
|
|
2504
|
+
requestId: crypto.randomUUID()
|
|
2223
2505
|
};
|
|
2224
2506
|
this.payments.push(payment);
|
|
2225
2507
|
this.totalSpentUsd += mixrpay.chargedUsd;
|
|
@@ -2250,5 +2532,7 @@ Timestamp: ${timestamp}`;
|
|
|
2250
2532
|
SessionNotFoundError,
|
|
2251
2533
|
SessionRevokedError,
|
|
2252
2534
|
SpendingLimitExceededError,
|
|
2535
|
+
X402ProtocolError,
|
|
2536
|
+
getErrorMessage,
|
|
2253
2537
|
isMixrPayError
|
|
2254
2538
|
});
|