@mixrpay/agent-sdk 0.8.9 → 0.9.5
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/LICENSE +21 -0
- package/README.md +49 -113
- package/dist/agent-wallet-PG5YUSUR.js +2 -0
- package/dist/chunk-IBXQMJ7G.js +1 -0
- package/dist/chunk-IUGFHZWI.js +32 -0
- package/dist/chunk-SWK4Q2A3.js +2 -0
- package/dist/cli.js +147 -0
- package/dist/credentials-QUSJGKLZ.js +2 -0
- package/dist/credentials-XJV2ESBQ.js +2 -0
- package/dist/credentials-Z4HQDXFU.js +1 -0
- package/dist/index.cjs +28 -4642
- package/dist/index.d.cts +1175 -308
- package/dist/index.d.ts +1175 -308
- package/dist/index.js +28 -4604
- package/dist/mcp-server.js +35 -0
- package/package.json +24 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 MixrPay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,165 +1,101 @@
|
|
|
1
1
|
# @mixrpay/agent-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AI agent payments and wallet management.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
Agents get spending authorization by claiming an **Access Code** from a human wallet owner.
|
|
8
|
-
|
|
9
|
-
### Step 1: Human Creates Access Code
|
|
5
|
+
## Installation
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @mixrpay/agent-sdk
|
|
9
|
+
```
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
## Quick Start
|
|
14
12
|
|
|
15
13
|
```typescript
|
|
16
14
|
import { AgentWallet } from '@mixrpay/agent-sdk';
|
|
17
15
|
|
|
18
|
-
//
|
|
19
|
-
const
|
|
20
|
-
inviteCode: 'mixr-abc123', // From the human
|
|
21
|
-
privateKey: process.env.AGENT_WALLET_KEY as `0x${string}`,
|
|
22
|
-
});
|
|
16
|
+
// Connect (auto-detects credentials from env)
|
|
17
|
+
const wallet = await AgentWallet.connect();
|
|
23
18
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
// Or with explicit credentials
|
|
20
|
+
const wallet = await AgentWallet.connect({
|
|
21
|
+
accessCode: 'mixr-abc123',
|
|
22
|
+
});
|
|
28
23
|
```
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
## Usage
|
|
31
26
|
|
|
32
27
|
```typescript
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
28
|
+
// Check balances
|
|
29
|
+
const balances = await wallet.getBalances();
|
|
36
30
|
|
|
37
|
-
//
|
|
38
|
-
const
|
|
39
|
-
```
|
|
31
|
+
// Transfer USDC
|
|
32
|
+
const tx = await wallet.transfer('0x...', '10.00');
|
|
40
33
|
|
|
41
|
-
|
|
34
|
+
// Swap tokens
|
|
35
|
+
const swap = await wallet.swap('ETH', 'USDC', '0.1');
|
|
42
36
|
|
|
43
|
-
|
|
37
|
+
// Bridge cross-chain
|
|
38
|
+
const bridge = await wallet.bridge('USDC', '100', 'ethereum');
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
| `info.address` | Session key's derived address | Used for signing requests |
|
|
48
|
-
| `info.walletAddress` | Human's funded wallet | Where funds are charged FROM |
|
|
40
|
+
// Call paid APIs (x402)
|
|
41
|
+
const response = await wallet.fetch('https://api.example.com/endpoint');
|
|
49
42
|
|
|
50
|
-
|
|
43
|
+
// Call gateway tools
|
|
44
|
+
const result = await wallet.callTool('firecrawl', {
|
|
45
|
+
action: 'scrape',
|
|
46
|
+
url: 'https://example.com',
|
|
47
|
+
});
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
// Execute arbitrary on-chain transactions
|
|
50
|
+
const result = await wallet.executeTransaction({
|
|
51
|
+
to: '0x...contractAddress',
|
|
52
|
+
data: '0x...calldata',
|
|
53
|
+
estimatedCostUsd: 5.0,
|
|
54
|
+
});
|
|
57
55
|
```
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
## Important: Session Key Security
|
|
57
|
+
## MCP Server
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- **Save it immediately** when you receive it (from `claimInvite()`)
|
|
66
|
-
- **Store it securely** (environment variable, secrets manager)
|
|
67
|
-
- **Never commit it** to version control
|
|
68
|
-
- **Never log it** to console or files
|
|
69
|
-
- **It cannot be recovered** if lost - you will need a new Access Code
|
|
70
|
-
|
|
71
|
-
The session key is shown **only once** when claimed. MixrPay does not store it.
|
|
72
|
-
|
|
73
|
-
## Installation
|
|
59
|
+
Use as an MCP server for Claude, Cline, or other MCP-compatible agents:
|
|
74
60
|
|
|
75
61
|
```bash
|
|
76
|
-
|
|
62
|
+
MIXRPAY_SESSION_KEY=sk_live_... npx mixrpay-mcp
|
|
77
63
|
```
|
|
78
64
|
|
|
79
|
-
|
|
65
|
+
Or programmatically:
|
|
80
66
|
|
|
81
67
|
```typescript
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const wallet = new AgentWallet({
|
|
85
|
-
sessionKey: process.env.MIXRPAY_SESSION_KEY!
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Call merchant APIs with automatic payment handling
|
|
89
|
-
const response = await wallet.callMerchantApi({
|
|
90
|
-
url: 'https://api.merchant.com/generate',
|
|
91
|
-
merchantPublicKey: 'pk_live_...',
|
|
92
|
-
body: { prompt: 'Hello world' }
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const data = await response.json();
|
|
68
|
+
const mcpConfig = wallet.mcp();
|
|
96
69
|
```
|
|
97
70
|
|
|
98
|
-
##
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
// Call merchant API with session-based payments
|
|
102
|
-
await wallet.callMerchantApi({ url, merchantPublicKey, body });
|
|
103
|
-
|
|
104
|
-
// Auto-pay fetch (drop-in replacement for fetch)
|
|
105
|
-
await wallet.fetch(url, options);
|
|
71
|
+
## CLI
|
|
106
72
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// Pre-flight balance check
|
|
111
|
-
const check = await wallet.canAfford(0.50);
|
|
112
|
-
if (check.canAfford) { /* proceed */ }
|
|
73
|
+
```bash
|
|
74
|
+
npm install -g @mixrpay/agent-sdk
|
|
113
75
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
## Configuration
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
const wallet = new AgentWallet({
|
|
123
|
-
sessionKey: 'sk_live_...', // Required
|
|
124
|
-
maxPaymentUsd: 5.0, // Optional: client-side limit
|
|
125
|
-
onPayment: (p) => console.log(p), // Optional: payment callback
|
|
126
|
-
});
|
|
76
|
+
mixrpay login
|
|
77
|
+
mixrpay status
|
|
78
|
+
mixrpay whoami
|
|
79
|
+
mixrpay logout
|
|
127
80
|
```
|
|
128
81
|
|
|
129
82
|
## Error Handling
|
|
130
83
|
|
|
131
84
|
```typescript
|
|
132
|
-
import {
|
|
133
|
-
AgentWallet,
|
|
134
|
-
InsufficientBalanceError,
|
|
135
|
-
SessionLimitExceededError,
|
|
136
|
-
MixrPayError,
|
|
137
|
-
} from '@mixrpay/agent-sdk';
|
|
85
|
+
import { AgentWallet, InsufficientBalanceError, MixrPayError } from '@mixrpay/agent-sdk';
|
|
138
86
|
|
|
139
87
|
try {
|
|
140
|
-
await wallet.
|
|
88
|
+
await wallet.transfer('0x...', '100.00');
|
|
141
89
|
} catch (error) {
|
|
142
90
|
if (error instanceof InsufficientBalanceError) {
|
|
143
91
|
console.log(`Need $${error.required}, have $${error.available}`);
|
|
144
92
|
}
|
|
145
|
-
|
|
146
|
-
// Check if error is retryable
|
|
147
93
|
if (error instanceof MixrPayError && error.isRetryable()) {
|
|
148
|
-
|
|
149
|
-
await sleep(delay);
|
|
150
|
-
return retry();
|
|
94
|
+
await sleep(error.retryAfterMs || 1000);
|
|
151
95
|
}
|
|
152
96
|
}
|
|
153
97
|
```
|
|
154
98
|
|
|
155
|
-
## What's New in v0.6.0
|
|
156
|
-
|
|
157
|
-
- `canAfford(amountUsd)` - Pre-flight balance check
|
|
158
|
-
- `isRetryable()` on all errors - Determine if operation should be retried
|
|
159
|
-
- `retryAfterMs` on errors - Suggested retry delay
|
|
160
|
-
- Enhanced `runDiagnostics()` - Session limits, latency, recommendations
|
|
161
|
-
- `requestId` and `correlationId` in payment events
|
|
162
|
-
|
|
163
99
|
## Documentation
|
|
164
100
|
|
|
165
101
|
[mixrpay.com/docs](https://www.mixrpay.com/docs)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as r from'fs';import*as s from'path';import*as o from'os';function d(){if(o.platform()==="win32"){let n=process.env.APPDATA||s.join(o.homedir(),"AppData","Roaming");return s.join(n,"mixrpay")}let e=process.env.XDG_CONFIG_HOME||s.join(o.homedir(),".config");return s.join(e,"mixrpay")}function a(){return s.join(d(),"credentials.json")}function g(){let t=d();r.existsSync(t)||r.mkdirSync(t,{recursive:true,mode:448});}function f(t){try{r.chmodSync(t,384);}catch{}}function y(t){try{g();let e=a(),n={};if(r.existsSync(e))try{let l=r.readFileSync(e,"utf-8");n=JSON.parse(l);}catch{n={};}let i={...n,...t,updatedAt:new Date().toISOString()},c=Object.fromEntries(Object.entries(i).filter(([l,p])=>p!==void 0));return r.writeFileSync(e,JSON.stringify(c,null,2),{mode:384}),f(e),!0}catch(e){return console.error("[MixrPay] Failed to save credentials:",e),false}}function u(){try{let t=a();if(!r.existsSync(t))return {success:!0,credentials:{}};let e=r.readFileSync(t,"utf-8");return {success:!0,credentials:JSON.parse(e)}}catch(t){return {success:false,error:t instanceof Error?t.message:"Failed to load credentials"}}}function S(t){try{let e=a();if(!r.existsSync(e))return !0;if(!t)return r.unlinkSync(e),!0;let n=u();if(!n.success)return !1;let i={...n.credentials};for(let c of t)delete i[c];return Object.keys(i).length===0?r.unlinkSync(e):(r.writeFileSync(e,JSON.stringify(i,null,2),{mode:384}),f(e)),!0}catch(e){return console.error("[MixrPay] Failed to delete credentials:",e),false}}function m(){let t=u();if(!t.success)return false;let{credentials:e}=t;return !!(e.sessionKey||e.apiToken||e.masterKey)}function h(){return a()}export{y as a,u as b,S as c,m as d,h as e};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {fileURLToPath}from'url';import {join,dirname}from'path';import {privateKeyToAccount,signMessage,generatePrivateKey,signTypedData}from'viem/accounts';import {createWalletClient,http,parseUnits,encodeFunctionData}from'viem';import {base}from'viem/chains';import {existsSync,readFileSync,mkdirSync,writeFileSync,chmodSync}from'fs';import {homedir}from'os';var l=class extends Error{code;retryAfterMs;constructor(e,t="MIXRPAY_ERROR",s){super(e),this.name="MixrPayError",this.code=t,this.retryAfterMs=s,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor);}isRetryable(){return false}},S=class extends l{required;available;topUpUrl;constructor(e,t){let s=e-t;super(`Insufficient balance: need $${e.toFixed(2)}, have $${t.toFixed(2)} (short $${s.toFixed(2)}). Top up your wallet to continue.`,"INSUFFICIENT_BALANCE"),this.name="InsufficientBalanceError",this.required=e,this.available=t,this.topUpUrl="https://mixrpay.com/manage/wallet";}},$=class extends l{expiredAt;constructor(e){super(`Session key expired at ${e}. Request a new session key from the wallet owner at https://mixrpay.com/manage/invites`,"SESSION_KEY_EXPIRED"),this.name="SessionKeyExpiredError",this.expiredAt=e;}},k=class extends l{limitType;limit;attempted;constructor(e,t,s){let a={per_tx:"Per-transaction",daily:"Daily",total:"Total",client_max:"Client-side"}[e]||e,r=e==="daily"?"Wait until tomorrow or request a higher limit.":e==="client_max"?"Increase maxPaymentUsd in your AgentWallet configuration.":"Request a new session key with a higher limit.";super(`${a} spending limit exceeded: limit is $${t.toFixed(2)}, attempted $${s.toFixed(2)}. ${r}`,"SPENDING_LIMIT_EXCEEDED"),this.name="SpendingLimitExceededError",this.limitType=e,this.limit=t,this.attempted=s;}isRetryable(){return this.limitType==="daily"}},A=class extends l{reason;txHash;constructor(e,t){let s=`Payment failed: ${e}`;t&&(s+=` (tx: ${t} - check on basescan.org)`),super(s,"PAYMENT_FAILED"),this.name="PaymentFailedError",this.reason=e,this.txHash=t;}isRetryable(){return true}},x=class extends l{reason;constructor(e="Invalid session key format"){super(`${e}. Session keys should be in format: sk_live_<64 hex chars> or sk_test_<64 hex chars>. Get one from https://mixrpay.com/manage/invites`,"INVALID_SESSION_KEY"),this.name="InvalidSessionKeyError",this.reason=e;}},T=class extends l{reason;constructor(e){super(`x402 protocol error: ${e}. This may indicate a server configuration issue. If the problem persists, contact the API provider.`,"X402_PROTOCOL_ERROR"),this.name="X402ProtocolError",this.reason=e;}isRetryable(){return true}},R=class extends l{sessionId;expiredAt;constructor(e,t){super(`Session ${e} has expired${t?` at ${t}`:""}. A new session will be created automatically on your next request.`,"SESSION_EXPIRED"),this.name="SessionExpiredError",this.sessionId=e,this.expiredAt=t;}},U=class extends l{sessionId;limit;requested;remaining;constructor(e,t,s,n){super(`Session spending limit exceeded: limit is $${e.toFixed(2)}, requested $${t.toFixed(2)}, remaining $${s.toFixed(2)}. Create a new session with a higher limit to continue.`,"SESSION_LIMIT_EXCEEDED"),this.name="SessionLimitExceededError",this.sessionId=n,this.limit=e,this.requested=t,this.remaining=s;}},P=class extends l{sessionId;constructor(e){super(`Session ${e} not found. It may have been deleted or never existed. Create a new session with getOrCreateSession().`,"SESSION_NOT_FOUND"),this.name="SessionNotFoundError",this.sessionId=e;}},I=class extends l{sessionId;reason;constructor(e,t){super(`Session ${e} has been revoked${t?`: ${t}`:""}. Create a new session with getOrCreateSession().`,"SESSION_REVOKED"),this.name="SessionRevokedError",this.sessionId=e,this.reason=t;}},C=class extends l{attempted;allowedPatterns;constructor(e,t){let s=t.slice(0,3).join(", "),n=t.length>3?"...":"";super(`Payment to "${e}" not allowed. Session allowlist: ${s}${n}. Update the session allowlist or create a new session.`,"MERCHANT_NOT_ALLOWED"),this.name="MerchantNotAllowedError",this.attempted=e,this.allowedPatterns=t;}};var ae={8453:"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",84532:"0x036CbD53842c5426634e7929541eC2318f3dCF7e"},re={name:"USD Coin",version:"2"},ie={TransferWithAuthorization:[{name:"from",type:"address"},{name:"to",type:"address"},{name:"value",type:"uint256"},{name:"validAfter",type:"uint256"},{name:"validBefore",type:"uint256"},{name:"nonce",type:"bytes32"}]},D=class u{privateKey;account;address;isTest;constructor(e,t){this.privateKey=e,this.account=privateKeyToAccount(e),this.address=this.account.address,this.isTest=t;}toString(){return `${this.isTest?"sk_test_":"sk_live_"}${this.privateKey.slice(2)}`}get privateKeyHex(){return this.privateKey.slice(2)}get rawPrivateKey(){return this.privateKey}static fromString(e){let t=/^sk_(live|test)_([a-fA-F0-9]{64})$/,s=e.match(t);if(!s)throw new x("Session key must be in format sk_live_{64_hex} or sk_test_{64_hex}");let n=s[1],a=s[2];try{let r=`0x${a}`;return new u(r,n==="test")}catch(r){throw new x(`Failed to decode session key: ${r}`)}}async signTransferAuthorization(e,t){return signTypedData({privateKey:this.privateKey,domain:e,types:ie,primaryType:"TransferWithAuthorization",message:t})}async signMessage(e){return signMessage({privateKey:this.privateKey,message:e})}getDefaultChainId(){return this.isTest?84532:8453}};function F(u){let e=ae[u.chainId];if(!e)throw new Error(`USDC not supported on chain ${u.chainId}`);let t={...re,chainId:u.chainId,verifyingContract:e},s={from:u.fromAddress,to:u.toAddress,value:u.value,validAfter:u.validAfter,validBefore:u.validBefore,nonce:u.nonce};return {domain:t,message:s}}function O(){let u=new Uint8Array(32);return crypto.getRandomValues(u),`0x${Array.from(u).map(e=>e.toString(16).padStart(2,"0")).join("")}`}function oe(u,e){return `MixrPay:${u}:${e.toLowerCase()}`}async function W(u){let e=Date.now(),t=oe(e,u.address),s=await u.signMessage(t);return {address:u.address,timestamp:e,signature:s}}var pe="0.9.2",q=".config/mixrpay",me="wallet.json";function J(){let u=process.env.MIXRPAY_WORKSPACE_DIR||process.env.OPENCLAW_WORKSPACE_DIR;return u?join(u,q):join(homedir(),q)}function M(){return join(J(),me)}async function B(u,e){let t=J(),s=M();existsSync(t)||mkdirSync(t,{recursive:true,mode:448});let n={address:e.toLowerCase(),privateKey:u,createdAt:new Date().toISOString(),sdkVersion:pe};writeFileSync(s,JSON.stringify(n,null,2),{encoding:"utf-8",mode:384});try{chmodSync(s,384);}catch{}}async function N(){let u=process.env.MIXRPAY_WALLET_KEY;if(u){if(u.startsWith("0x")&&u.length===66)return u;if(u.length===64)return `0x${u}`;console.warn("[MixrPay] MIXRPAY_WALLET_KEY has invalid format, ignoring");}let e=M();if(!existsSync(e))return null;try{let t=readFileSync(e,"utf-8"),s=JSON.parse(t);return !s.privateKey||!s.privateKey.startsWith("0x")?(console.warn("[MixrPay] Wallet file has invalid format"),null):s.privateKey}catch(t){return console.warn("[MixrPay] Failed to read wallet file:",t),null}}async function X(){let u=process.env.MIXRPAY_WALLET_KEY;return u&&(u.length===64||u.startsWith("0x")&&u.length===66)?true:existsSync(M())}async function G(u){let e=null,t=u.headers.get("X-Payment-Required");if(t)try{e=JSON.parse(t);}catch{}if(!e){let n=u.headers.get("WWW-Authenticate");if(n?.startsWith("X-402 "))try{let a=n.slice(6);e=JSON.parse(atob(a));}catch{}}if(!e)try{e=await u.json();}catch{}if(!e)throw new T("Could not parse payment requirements from 402 response");if(!e.recipient)throw new T("Missing recipient in payment requirements");if(!e.amount)throw new T("Missing amount in payment requirements");let s=Math.floor(Date.now()/1e3);return {recipient:e.recipient,amount:BigInt(e.amount),currency:e.currency||"USDC",chainId:e.chainId||e.chain_id||8453,facilitatorUrl:e.facilitatorUrl||e.facilitator_url||"https://x402.org/facilitator",nonce:e.nonce||O().slice(2),expiresAt:e.expiresAt||e.expires_at||s+300,description:e.description}}async function Y(u,e,t){let s=u.nonce.length===64?`0x${u.nonce}`:O(),a=BigInt(Math.floor(Date.now()/1e3))-60n,r=BigInt(u.expiresAt),{domain:i,message:o}=F({fromAddress:t,toAddress:u.recipient,value:u.amount,validAfter:a,validBefore:r,nonce:s,chainId:u.chainId}),d=await e.signTransferAuthorization(i,o),p={x402Version:1,scheme:"exact",network:u.chainId===8453?"base":"base-sepolia",payload:{signature:d,authorization:{from:t,to:u.recipient,value:u.amount.toString(),validAfter:a.toString(),validBefore:r.toString(),nonce:s}}};return btoa(JSON.stringify(p))}function z(u){return Date.now()/1e3>u.expiresAt}function L(u){return Number(u.amount)/1e6}var ve="0.9.4",b=process.env.MIXRPAY_BASE_URL||"https://www.mixrpay.com",Be="https://x402.org/facilitator",Se=3e4,Z={BASE_MAINNET:{chainId:8453,name:"Base",isTestnet:false},BASE_SEPOLIA:{chainId:84532,name:"Base Sepolia",isTestnet:true}},Q={debug:0,info:1,warn:2,error:3,none:4},H=class{level;prefix;constructor(e="none",t="[MixrPay]"){this.level=e,this.prefix=t;}setLevel(e){this.level=e;}shouldLog(e){return Q[e]>=Q[this.level]}debug(...e){this.shouldLog("debug")&&console.log(`${this.prefix} \u{1F50D}`,...e);}info(...e){this.shouldLog("info")&&console.log(`${this.prefix} \u2139\uFE0F`,...e);}warn(...e){this.shouldLog("warn")&&console.warn(`${this.prefix} \u26A0\uFE0F`,...e);}error(...e){this.shouldLog("error")&&console.error(`${this.prefix} \u274C`,...e);}payment(e,t,s){if(this.shouldLog("info")){let n=s?` for "${s}"`:"";console.log(`${this.prefix} \u{1F4B8} Paid $${e.toFixed(4)} to ${t.slice(0,10)}...${n}`);}}},ee=class u{sessionKey;walletAddress;maxPaymentUsd;onPayment;baseUrl;timeout;logger;payments=[];totalSpentUsd=0;sessionKeyInfo;sessionKeyInfoFetchedAt;allowlist;allowlistFetchedAt;selfCustodyAddress;selfCustodyKey;agentInstanceId;apiKey;constructor(e){this.validateConfig(e),this.sessionKey=D.fromString(e.sessionKey),this.walletAddress=e.walletAddress||this.sessionKey.address,this.maxPaymentUsd=e.maxPaymentUsd,this.onPayment=e.onPayment,this.baseUrl=(e.baseUrl||b).replace(/\/$/,""),this.timeout=e.timeout||Se,e.apiKey&&(this.apiKey=e.apiKey),this.logger=new H(e.logLevel||"none"),this.logger.debug("AgentWallet initialized",{walletAddress:this.walletAddress,isTestnet:this.isTestnet(),maxPaymentUsd:this.maxPaymentUsd,hasApiKey:!!this.apiKey});}validateConfig(e){if(!e.sessionKey)throw new x("Session key is required. Get one from the wallet owner at https://mixrpay.com/manage/invites");let t=e.sessionKey.trim();if(!t.startsWith("sk_live_")&&!t.startsWith("sk_test_")){let a=t.slice(0,8);throw new x(`Invalid session key prefix. Expected 'sk_live_' (mainnet) or 'sk_test_' (testnet), got '${a}'`)}let s=72;if(t.length!==s)throw new x(`Invalid session key length. Expected ${s} characters, got ${t.length}. Make sure you copied the complete key.`);let n=t.slice(8);if(!/^[0-9a-fA-F]+$/.test(n))throw new x("Invalid session key format. The key should contain only hexadecimal characters after the prefix.");if(e.maxPaymentUsd!==void 0){if(e.maxPaymentUsd<=0)throw new l("maxPaymentUsd must be a positive number");e.maxPaymentUsd>1e4&&this.logger?.warn("maxPaymentUsd is very high ($"+e.maxPaymentUsd+"). Consider using a lower limit for safety.");}}async fetchAllowlist(){if(this.allowlist!==void 0&&this.allowlistFetchedAt&&Date.now()-this.allowlistFetchedAt<3e5)return this.allowlist;try{let t=await this.getSessionKeyInfo();return this.allowlist=t.allowedMerchants||[],this.allowlistFetchedAt=Date.now(),this.logger.debug("Fetched allowlist",{patterns:this.allowlist.length,allowAll:this.allowlist.length===0}),this.allowlist}catch(t){return this.logger.warn("Failed to fetch allowlist, allowing all merchants",{error:t}),this.allowlist=[],this.allowlistFetchedAt=Date.now(),this.allowlist}}async validateMerchantAllowed(e,t){let s=await this.fetchAllowlist();if(s.length===0)return;if(!this.matchesAllowlist(e,s,t))throw new C(e,s)}matchesAllowlist(e,t,s){for(let n of t)if(this.matchPattern(e,n,s))return true;return false}matchPattern(e,t,s){return s==="url"?this.matchUrlPattern(e,t):this.matchToolPattern(e,t)}matchUrlPattern(e,t){let s;try{e.includes("://")?s=new URL(e).hostname.toLowerCase():s=e.toLowerCase();}catch{return false}let a=t.toLowerCase().trim();if(a.includes("://"))try{a=new URL(a).hostname;}catch{}if(s===a)return true;if(a.startsWith("*.")){let r=a.substring(2);if(s.endsWith(`.${r}`)&&s!==r)return true}return false}matchToolPattern(e,t){let s=e.toLowerCase().trim(),n=t.toLowerCase().trim();if(s===n)return true;if(n.endsWith("/*")){let a=n.substring(0,n.length-2);if(s.startsWith(`${a}/`))return true}return false}static async register(e){let{privateKey:t,name:s}=e,n=(e.baseUrl||b).replace(/\/$/,""),r=privateKeyToAccount(t).address,i=await fetch(`${n}/api/v1/agent/challenge?wallet=${r}&action=register`);if(!i.ok){let h=await i.json().catch(()=>({}));throw new l(h.error||`Failed to get challenge: ${i.status}`)}let{challenge:o,message:d}=await i.json(),p=await signMessage({message:d,privateKey:t}),c=await fetch(`${n}/api/v1/agent/register`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({challenge:o,external_wallet:r,signature:p,name:s})});if(!c.ok){let h=await c.json().catch(()=>({})),y=h.error||`Registration failed with status ${c.status}`,m=h.request_id,w=(h.code||"").toLowerCase(),f="";c.status===503?f=" The service may be temporarily unavailable. Please try again later.":c.status===500?f=" This is a server error. Please contact support with the request ID.":(w==="missing_challenge"||w==="missing_signature")&&(f=" This may indicate an SDK bug. Please update to the latest version.");let E=m?`${y} (request_id: ${m})${f}`:`${y}${f}`;throw new l(E)}let g=await c.json();return {userId:g.user_id,depositAddress:g.deposit_address}}static async checkServerHealth(e){let t=(e||b).replace(/\/$/,"");try{let s=await fetch(`${t}/api/health/ready?details=true`);if(!s.ok)return {healthy:!1,database:"unknown",agentRegistrationAvailable:!1,walletServiceConfigured:!1,error:`Health check failed with status ${s.status}`};let n=await s.json();return {healthy:n.status==="ready",database:n.database||"unknown",agentRegistrationAvailable:n.services?.agentRegistration?.available??!1,walletServiceConfigured:n.services?.wallet?.configured??!1}}catch(s){return {healthy:false,database:"unreachable",agentRegistrationAvailable:false,walletServiceConfigured:false,error:s instanceof Error?s.message:"Failed to reach server"}}}static async getSessionKey(e){let{privateKey:t,spendingLimitUsd:s,maxPerTxUsd:n,maxDailyUsd:a,durationDays:r}=e,i=(e.baseUrl||b).replace(/\/$/,""),d=privateKeyToAccount(t).address,p=await fetch(`${i}/api/v1/agent/challenge?wallet=${d}&action=session-key`);if(!p.ok){let w=await p.json().catch(()=>({}));throw new l(w.error||`Failed to get challenge: ${p.status}`)}let{challenge:c,message:g}=await p.json(),h=await signMessage({message:g,privateKey:t}),y=await fetch(`${i}/api/v1/agent/session-key`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({challenge:c,external_wallet:d,signature:h,spending_limit_usd:s,max_per_tx_usd:n,max_daily_usd:a,duration_days:r})});if(!y.ok){let w=await y.json().catch(()=>({}));throw new l(w.error||`Session key creation failed: ${y.status}`)}let m=await y.json();return {sessionKey:m.session_key,address:m.address,sessionKeyId:m.session_key_id,expiresAt:new Date(m.expires_at),limits:{maxTotalUsd:m.limits.max_total_usd,maxPerTxUsd:m.limits.max_per_tx_usd,maxDailyUsd:m.limits.max_daily_usd}}}static async getStatus(e){let{privateKey:t}=e,s=(e.baseUrl||b).replace(/\/$/,""),a=privateKeyToAccount(t).address,r=await fetch(`${s}/api/v1/agent/challenge?wallet=${a}&action=status`);if(!r.ok){let g=await r.json().catch(()=>({}));throw new l(g.error||`Failed to get challenge: ${r.status}`)}let{challenge:i,message:o}=await r.json(),d=await signMessage({message:o,privateKey:t}),p=await fetch(`${s}/api/v1/agent/status?challenge=${i}&external_wallet=${a}&signature=${encodeURIComponent(d)}`);if(!p.ok){let g=await p.json().catch(()=>({}));throw new l(g.error||`Failed to get status: ${p.status}`)}let c=await p.json();return {depositAddress:c.deposit_address,balanceUsd:c.balance_usd,activeSessions:c.active_sessions.map(g=>({id:g.id,expiresAt:new Date(g.expires_at),totalSpentUsd:g.total_spent_usd,remainingUsd:g.remaining_usd,maxTotalUsd:g.max_total_usd})),totalSpentUsd:c.total_spent_usd}}static async revokeSessionKey(e){let{privateKey:t,sessionKeyId:s}=e,n=(e.baseUrl||b).replace(/\/$/,""),r=privateKeyToAccount(t).address,i=await fetch(`${n}/api/v1/agent/challenge?wallet=${r}&action=revoke`);if(!i.ok){let g=await i.json().catch(()=>({}));throw new l(g.error||`Failed to get challenge: ${i.status}`)}let{challenge:o,message:d}=await i.json(),p=await signMessage({message:d,privateKey:t}),c=await fetch(`${n}/api/v1/agent/session-key/revoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({challenge:o,external_wallet:r,signature:p,session_key_id:s})});if(!c.ok){let g=await c.json().catch(()=>({}));throw new l(g.error||`Revocation failed: ${c.status}`)}return true}static async withdraw(e){let{privateKey:t,amountUsd:s}=e,n=(e.baseUrl||b).replace(/\/$/,""),r=privateKeyToAccount(t).address,i=await fetch(`${n}/api/v1/agent/challenge?wallet=${r}&action=withdraw`);if(!i.ok){let h=await i.json().catch(()=>({}));throw new l(h.error||`Failed to get challenge: ${i.status}`)}let{challenge:o,message:d}=await i.json(),p=await signMessage({message:d,privateKey:t}),c=await fetch(`${n}/api/v1/agent/withdraw`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({challenge:o,external_wallet:r,signature:p,to_address:r,amount_usd:s})});if(!c.ok){let h=await c.json().catch(()=>({}));throw new l(h.error||`Withdrawal failed: ${c.status}`)}let g=await c.json();return {txHash:g.tx_hash,amountUsd:g.amount_usd,remainingBalanceUsd:g.remaining_balance_usd}}static async claimInvite(e){let{inviteCode:t,privateKey:s}=e,n=(e.baseUrl||b).replace(/\/$/,""),r=privateKeyToAccount(s).address,i=await fetch(`${n}/api/v1/agent/challenge?wallet=${r}&action=claim-invite`);if(!i.ok){let h=await i.json().catch(()=>({}));throw new l(h.error||`Failed to get challenge: ${i.status}`)}let{challenge:o,message:d}=await i.json(),p=await signMessage({message:d,privateKey:s}),c=await fetch(`${n}/api/v1/agent/claim-invite`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({invite_code:t,challenge:o,agent_wallet_address:r,signature:p})});if(!c.ok){let y=(await c.json().catch(()=>({}))).error||`Failed to claim invite: ${c.status}`,m="";throw c.status===404?m=" The invite code may be invalid or misspelled.":c.status===400&&(y.includes("already claimed")?m=" This invite has already been used by another agent.":y.includes("expired")?m=" Ask the wallet owner to create a new invite.":y.includes("revoked")&&(m=" The wallet owner has revoked this invite.")),new l(`${y}${m}`)}let g=await c.json();return {sessionKey:g.session_key,address:g.address,sessionKeyId:g.session_key_id,expiresAt:new Date(g.expires_at),limits:{budgetUsd:g.limits.budget_usd,budgetPeriod:g.limits.budget_period,maxPerTxUsd:g.limits.max_per_tx_usd},allowedMerchants:g.allowed_merchants||[],inviterName:g.inviter_name||"Anonymous"}}static async activate(e,t){let s=(t?.baseUrl||b).replace(/\/$/,""),n=new u({sessionKey:e,baseUrl:s}),a=await n.getSessionAuthHeaders(),r=await fetch(`${s}/api/v2/agent/activate`,{method:"POST",headers:{"Content-Type":"application/json",...a}});if(!r.ok){let o=await r.json().catch(()=>({}));throw new l(o.error||`Activation failed: ${r.status}`)}let i=await r.json();return {wallet:n,budget:{maxTotalUsd:i.budget.max_total_usd,maxDailyUsd:i.budget.max_daily_usd,maxPerTxUsd:i.budget.max_per_tx_usd,spentUsd:i.budget.spent_usd,remainingUsd:i.budget.remaining_usd},capabilities:{executeTransactions:i.capabilities.execute_transactions,gasSponsored:i.capabilities.gas_sponsored,batchedTx:i.capabilities.batched_tx},skills:i.skills||[],tools:i.tools||[],gatewayProviders:i.gateway_providers||[]}}static async connect(e){let t=(e?.baseUrl||process.env.MIXRPAY_BASE_URL||b).replace(/\/$/,""),s=e?.logLevel||"none",n=process.env.MIXRPAY_INSTANCE_ID;if(e?.sessionKey){let p=new u({sessionKey:e.sessionKey,baseUrl:t,logLevel:s,maxPaymentUsd:e?.maxPaymentUsd,onPayment:e?.onPayment});return n&&p.setAgentInstanceId(n),p}let a=process.env.MIXRPAY_SESSION_KEY;if(a){let p=new u({sessionKey:a,baseUrl:t,logLevel:s,maxPaymentUsd:e?.maxPaymentUsd,onPayment:e?.onPayment});return n&&p.setAgentInstanceId(n),p}let r=process.env.MIXRPAY_API_KEY||process.env.MIXRPAY_AGENT_TOKEN;if(r&&(r.startsWith("agt_live_")||r.startsWith("agt_test_")))return u.fromApiKey(r,{baseUrl:t,logLevel:s});let i=e?.accessCode||process.env.MIXRPAY_ACCESS_CODE;if(i)return u.fromAccessCode(i,{baseUrl:t,logLevel:s});let o=e?.masterKey||process.env.MIXRPAY_MASTER_KEY,d=e?.agentName||process.env.MIXRPAY_AGENT_NAME;if(o&&d)return u.fromMasterKey(o,d,{baseUrl:t,logLevel:s});try{let{loadCredentials:p}=await import('./credentials-XJV2ESBQ.js'),c=p();if(c.success&&c.credentials.sessionKey){let g=new u({sessionKey:c.credentials.sessionKey,baseUrl:c.credentials.baseUrl||t,logLevel:s,maxPaymentUsd:e?.maxPaymentUsd,onPayment:e?.onPayment,apiKey:c.credentials.apiToken});return n&&g.setAgentInstanceId(n),g}if(c.success&&c.credentials.apiToken)return u.fromApiKey(c.credentials.apiToken,{baseUrl:t,logLevel:s});if(c.success&&c.credentials.masterKey&&c.credentials.defaultAgentName)return u.fromMasterKey(c.credentials.masterKey,c.credentials.defaultAgentName,{baseUrl:t,logLevel:s})}catch{}if(e?.interactive)return u.deviceFlowLogin({baseUrl:t,logLevel:s});throw new l(`No MixrPay credentials found. Options:
|
|
3
|
+
1. Set MIXRPAY_SESSION_KEY environment variable
|
|
4
|
+
2. Set MIXRPAY_API_KEY or MIXRPAY_AGENT_TOKEN environment variable (agt_live_xxx)
|
|
5
|
+
3. Set MIXRPAY_ACCESS_CODE environment variable (mixr-xxx)
|
|
6
|
+
4. Pass sessionKey or accessCode to connect()
|
|
7
|
+
5. Run \`npx mixrpay login\` to cache credentials
|
|
8
|
+
6. Use connect({ interactive: true }) for browser login`)}static async fromApiKey(e,t){let s=(t?.baseUrl||b).replace(/\/$/,"");if(!e.startsWith("agt_live_")&&!e.startsWith("agt_test_"))throw new l(`Invalid API key format. Expected 'agt_live_' or 'agt_test_' prefix, got '${e.slice(0,9)}...'`);let n=await fetch(`${s}/api/v2/agent/session-from-token`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`}});if(!n.ok){let o=await n.json().catch(()=>({}));throw new l(o.error||`Failed to exchange API key for session: ${n.status}`)}let a=await n.json(),r=a.session_key;if(!r)throw new l("No session_key in API response");let i=new u({sessionKey:r,baseUrl:s,logLevel:t?.logLevel});return i.setApiKey(e),a.agent_instance_id&&i.setAgentInstanceId(a.agent_instance_id),i}static async fromAccessCode(e,t){let s=(t?.baseUrl||b).replace(/\/$/,"");if(!e.startsWith("mixr-"))throw new l(`Invalid access code format. Expected 'mixr-' prefix, got '${e.slice(0,5)}...'`);let n=await fetch(`${s}/api/v2/agent/connect`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:e})});if(!n.ok){let p=await n.json().catch(()=>({})),c=(p.code||"").toLowerCase(),g="";throw n.status===404||c==="invalid_code"?g=" Check the code for typos or get a new one from the MixrPay dashboard.":n.status===400&&(c==="code_claimed"?g=" This code has already been used. Get a new code from the dashboard.":c==="code_expired"?g=" This code has expired. Get a new code from the dashboard.":c==="code_revoked"&&(g=" This code has been revoked by the owner.")),new l((p.error||`Failed to activate access code: ${n.status}`)+g)}let a=await n.json(),r=a.session_key,i=a.token,o=a.agent_id;if(!r)throw new l("No session_key in access code response");try{let{saveCredentials:p}=await import('./credentials-XJV2ESBQ.js');p({sessionKey:r,apiToken:i,baseUrl:s});}catch{}let d=new u({sessionKey:r,baseUrl:s,logLevel:t?.logLevel});return i&&(d.apiKey=i),o&&(d.agentInstanceId=o),d}static async fromMasterKey(e,t,s){let n=(s?.baseUrl||b).replace(/\/$/,"");if(!e.startsWith("mk_live_")&&!e.startsWith("mk_test_"))throw new l("Invalid master key format. Expected 'mk_live_' or 'mk_test_' prefix.");let a=await fetch(`${n}/api/v2/master-keys/session`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify({agent_name:t})});if(!a.ok){let d=await a.json().catch(()=>({}));throw a.status===404?new l(`Agent '${t}' not found. Use the MixrPay dashboard to create it.`):new l(d.error||`Failed to get session from master key: ${a.status}`)}let r=await a.json(),i=r.session_key;if(!i)throw new l("No session_key in master key response");let o=new u({sessionKey:i,baseUrl:n,logLevel:s?.logLevel});return r.agent?.id&&(o.agentInstanceId=r.agent.id),o}static async deviceFlowLogin(e){let t=(e?.baseUrl||b).replace(/\/$/,""),s=await fetch(`${t}/api/v2/auth/device/authorize`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!s.ok){let h=await s.json().catch(()=>({}));throw new l(h.error||`Device flow initiation failed: ${s.status}`)}let n=await s.json(),{device_code:a,user_code:r,verification_uri:i,verification_uri_complete:o,expires_in:d,interval:p}=n;console.log(`
|
|
9
|
+
\u{1F510} MixrPay Login
|
|
10
|
+
`),console.log(`Visit: ${o||i}`),console.log(`Enter code: ${r}
|
|
11
|
+
`),console.log(`Waiting for authentication...
|
|
12
|
+
`);let c=(p||5)*1e3,g=Date.now()+(d||300)*1e3;for(;Date.now()<g;){await new Promise(w=>setTimeout(w,c));let h=await fetch(`${t}/api/v2/auth/device/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:a})});if(h.ok){let f=(await h.json()).session_key;if(f){console.log(`\u2705 Authenticated successfully!
|
|
13
|
+
`);try{let{saveCredentials:E}=await import('./credentials-XJV2ESBQ.js');E({sessionKey:f,baseUrl:t});}catch{}return new u({sessionKey:f,baseUrl:t,logLevel:e?.logLevel})}}let m=(await h.json().catch(()=>({}))).error;if(m!=="authorization_pending")if(m==="slow_down"){await new Promise(w=>setTimeout(w,c));continue}else {if(m==="expired_token")throw new l("Device code expired. Please try again.");if(m==="access_denied")throw new l("Authentication was denied by the user.");if(m)throw new l(`Device flow error: ${m}`)}}throw new l("Device flow timed out. Please try again.")}async spawnChildInvite(e){let{budgetUsd:t,name:s,allowedMerchants:n,expiresInDays:a}=e,r=await this.getSessionAuthHeaders(),i=await fetch(`${this.baseUrl}/api/v1/agent/spawn`,{method:"POST",headers:{"Content-Type":"application/json",...r},body:JSON.stringify({budget_usd:t,name:s,allowed_merchants:n,expires_in_days:a})});if(!i.ok){let d=await i.json().catch(()=>({}));throw i.status===409?new l("Concurrent modification - please retry"):i.status===429?new l("Rate limited - too many spawn attempts"):new l(d.error||`Failed to spawn child: ${i.status}`)}let o=await i.json();return {inviteCode:o.invite_code,inviteId:o.invite_id,budgetUsd:o.budget_usd,expiresAt:new Date(o.expires_at),depth:o.depth,maxSpawnBudget:o.max_spawn_budget,allowedMerchants:o.allowed_merchants||[]}}async getAvailableBudget(){let e=await this.getSessionAuthHeaders(),t=await fetch(`${this.baseUrl}/api/v1/agent/descendants`,{method:"GET",headers:e});if(!t.ok){let n=await t.json().catch(()=>({}));throw new l(n.error||`Failed to get budget: ${t.status}`)}let s=await t.json();return {totalBudget:s.budget.total_usd,spent:s.budget.spent_usd,allocatedToChildren:s.budget.allocated_to_children_usd,available:s.budget.available_usd,maxSpawnBudget:s.max_spawn_budget,canSpawn:s.can_spawn}}async getChildSessions(){let e=await this.getSessionAuthHeaders(),t=await fetch(`${this.baseUrl}/api/v1/agent/descendants`,{method:"GET",headers:e});if(!t.ok){let n=await t.json().catch(()=>({}));throw new l(n.error||`Failed to get children: ${t.status}`)}return (await t.json()).children||[]}async listChildSessions(){return this.getChildSessions()}async fetch(e,t){this.logger.debug(`Fetching ${t?.method||"GET"} ${e}`),await this.validateMerchantAllowed(e,"url");let s=crypto.randomUUID(),n=this.extractCorrelationId(t?.headers),a=new AbortController,r=setTimeout(()=>a.abort(),this.timeout);try{let i=await fetch(e,{...t,signal:a.signal});return this.logger.debug(`Initial response: ${i.status}`),i.status===402&&(this.logger.info(`Payment required for ${e}`),i=await this.handlePaymentRequired(e,t,i,s,n)),i}catch(i){throw i instanceof Error&&i.name==="AbortError"?new l(`Request timeout after ${this.timeout}ms`):i}finally{clearTimeout(r);}}extractCorrelationId(e){if(!e)return;if(e instanceof Headers)return e.get("X-Correlation-Id")||e.get("x-correlation-id")||void 0;if(Array.isArray(e)){let s=e.find(([n])=>n.toLowerCase()==="x-correlation-id");return s?s[1]:void 0}let t=e;return t["X-Correlation-Id"]||t["x-correlation-id"]||void 0}async handlePaymentRequired(e,t,s,n,a){let r;try{r=await G(s),this.logger.debug("Payment requirements:",{amount:`$${L(r).toFixed(4)}`,recipient:r.recipient,description:r.description});}catch(c){throw this.logger.error("Failed to parse payment requirements:",c),new A(`Failed to parse payment requirements: ${c}. The server may not be properly configured for x402 payments.`)}if(z(r))throw new A("Payment requirements have expired. This usually means the request took too long. Try again.");let i=L(r);if(this.maxPaymentUsd!==void 0&&i>this.maxPaymentUsd)throw new k("client_max",this.maxPaymentUsd,i);let o;try{this.logger.debug("Signing payment authorization..."),o=await Y(r,this.sessionKey,this.walletAddress);}catch(c){throw this.logger.error("Failed to sign payment:",c),new A(`Failed to sign payment: ${c}. This may indicate an issue with the session key.`)}this.logger.debug("Retrying request with payment...");let d=new Headers(t?.headers);d.set("X-PAYMENT",o);let p=await fetch(e,{...t,headers:d});if(p.status!==402){let c={amountUsd:i,recipient:r.recipient,txHash:p.headers.get("X-Payment-TxHash"),timestamp:new Date,description:r.description,url:e,requestId:n,correlationId:a};this.payments.push(c),this.totalSpentUsd+=i,this.logger.payment(i,r.recipient,r.description),this.onPayment&&this.onPayment(c);}else await this.handlePaymentError(p);return p}async handlePaymentError(e){let t={};try{t=await e.json();}catch{}let s=(t.error_code||"").toLowerCase(),n=t.error||t.message||"Payment required";if(this.logger.error("Payment failed:",{errorCode:s,errorMessage:n,errorData:t}),s==="insufficient_balance"){let a=t.required||0,r=t.available||0;throw new S(a,r)}else throw s==="session_key_expired"?new $(t.expired_at||"unknown"):s==="spending_limit_exceeded"?new k(t.limit_type||"unknown",t.limit||0,t.attempted||0):new A(n)}getWalletAddress(){return this.walletAddress}isTestnet(){return this.sessionKey.isTest}getNetwork(){return this.isTestnet()?Z.BASE_SEPOLIA:Z.BASE_MAINNET}async getBalance(){this.logger.debug("Fetching wallet balance...");try{let e=await fetch(`${this.baseUrl}/api/v1/session-key/info`,{headers:{"X-Session-Key":this.sessionKey.address.toLowerCase()}});if(!e.ok)throw new Error("Failed to get session key info");let t=await e.json(),s=t.wallet_address||t.walletAddress;if(!s)throw new Error("No wallet address in session key info");let n=await fetch(`${this.baseUrl}/api/wallet/balance?address=${s}&chain=base`);if(n.ok){let a=await n.json(),r=a.balance_usdc||a.balance_usd||0;return this.logger.debug(`Balance: $${r}`),r}}catch(e){this.logger.warn("Failed to fetch balance:",e);}return this.logger.debug("Using estimated balance based on tracking"),Math.max(0,100-this.totalSpentUsd)}async canAfford(e){let t=await this.getBalance(),s=t>=e;return {canAfford:s,balance:t,shortfall:s?0:e-t,remainingAfter:s?t-e:0}}async getSessionKeyInfo(e=false){let t=this.sessionKeyInfoFetchedAt?Date.now()-this.sessionKeyInfoFetchedAt:1/0;if(!e&&this.sessionKeyInfo&&t<6e4)return this.sessionKeyInfo;this.logger.debug("Fetching session key info...");try{let s=await fetch(`${this.baseUrl}/api/v1/session-key/info`,{headers:{"X-Session-Key":this.sessionKey.address.toLowerCase()}});if(s.ok){let n=await s.json();return this.sessionKeyInfo={address:this.sessionKey.address,walletAddress:n.wallet_address??n.walletAddress??null,isValid:n.is_valid??n.isValid??!0,limits:{perTxUsd:n.per_tx_limit_usd??n.perTxLimitUsd??null,dailyUsd:n.daily_limit_usd??n.dailyLimitUsd??null,totalUsd:n.total_limit_usd??n.totalLimitUsd??null},usage:{todayUsd:n.today_spent_usd??n.todaySpentUsd??0,totalUsd:n.total_spent_usd??n.totalSpentUsd??0,txCount:n.tx_count??n.txCount??0},expiresAt:n.expires_at?new Date(n.expires_at):null,createdAt:n.created_at?new Date(n.created_at):null,name:n.name,allowedMerchants:n.allowed_merchants??n.allowedMerchants??[]},this.sessionKeyInfoFetchedAt=Date.now(),this.sessionKeyInfo}}catch(s){this.logger.warn("Failed to fetch session key info:",s);}return {address:this.sessionKey.address,walletAddress:null,isValid:true,limits:{perTxUsd:null,dailyUsd:null,totalUsd:null},usage:{todayUsd:this.totalSpentUsd,totalUsd:this.totalSpentUsd,txCount:this.payments.length},expiresAt:null,createdAt:null}}async getSpendingStats(){this.logger.debug("Fetching spending stats...");try{let e=await fetch(`${this.baseUrl}/api/v1/session-key/stats`,{headers:{"X-Session-Key":this.sessionKey.address.toLowerCase()}});if(e.ok){let t=await e.json();return {totalSpentUsd:t.total_spent_usd||t.totalSpentUsd||this.totalSpentUsd,txCount:t.tx_count||t.txCount||this.payments.length,remainingDailyUsd:t.remaining_daily_usd||t.remainingDailyUsd||null,remainingTotalUsd:t.remaining_total_usd||t.remainingTotalUsd||null,expiresAt:t.expires_at?new Date(t.expires_at):null}}}catch(e){this.logger.warn("Failed to fetch spending stats:",e);}return {totalSpentUsd:this.totalSpentUsd,txCount:this.payments.length,remainingDailyUsd:null,remainingTotalUsd:null,expiresAt:null}}getPaymentHistory(){return [...this.payments]}getTotalSpent(){return this.totalSpentUsd}async createSelfCustodyWallet(){if(!this.agentInstanceId)throw new l("Agent instance ID is required for self-custody wallet. Use AgentWallet.fromApiKey() or set MIXRPAY_INSTANCE_ID environment variable.","NO_AGENT_INSTANCE_ID");let e=await N();if(e){let i=privateKeyToAccount(e);return this.selfCustodyAddress=i.address,this.selfCustodyKey=e,this.logger.info("Loaded existing self-custody wallet",{address:i.address}),{address:i.address,privateKey:e}}let t=generatePrivateKey(),s=privateKeyToAccount(t),n=Date.now(),a=`MixrPay Wallet Registration
|
|
14
|
+
Address: ${s.address}
|
|
15
|
+
Timestamp: ${n}`,r=await s.signMessage({message:a});return await this.registerWalletAddress(s.address,r,n),await B(t,s.address),this.selfCustodyAddress=s.address,this.selfCustodyKey=t,this.logger.info("Created self-custody wallet",{address:s.address}),{address:s.address,privateKey:t}}async registerWalletAddress(e,t,s){if(!this.apiKey)throw new l("API key is required for wallet registration. Use AgentWallet.fromApiKey() to initialize.","NO_API_KEY");let n=await fetch(`${this.baseUrl}/api/v2/agent/wallet/register`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify({address:e,signature:t,timestamp:s})});if(!n.ok){let a=await n.json().catch(()=>({}));throw new l(a.error||`Failed to register wallet: ${n.status}`,"WALLET_REGISTRATION_FAILED")}}async loadSelfCustodyWallet(){let e=await N();if(!e)return false;let t=privateKeyToAccount(e);return this.selfCustodyAddress=t.address,this.selfCustodyKey=e,this.logger.debug("Loaded self-custody wallet",{address:t.address}),true}async hasSelfCustodyWallet(){return await X()}getSelfCustodyAddress(){return this.selfCustodyAddress||null}async executeFromOwnWallet(e){if(!this.selfCustodyKey||!this.selfCustodyAddress)throw new l("Self-custody wallet not loaded. Call loadSelfCustodyWallet() or createSelfCustodyWallet() first.","WALLET_NOT_LOADED");let{to:t,value:s,data:n,gasLimit:a}=e,r=privateKeyToAccount(this.selfCustodyKey),i=createWalletClient({account:r,chain:base,transport:http()});this.logger.debug("Executing transaction from self-custody wallet",{from:this.selfCustodyAddress,to:t,value:s?.toString()});try{let o=await i.sendTransaction({to:t,value:s??BigInt(0),data:n,gas:a});this.logger.info("Transaction submitted",{txHash:o});let d=!1;try{await this.reportTransaction(o,t,s??BigInt(0)),d=!0;}catch(p){this.logger.warn("Failed to report transaction to MixrPay",{txHash:o,error:p instanceof Error?p.message:p});}return {txHash:o,reportedToMixrPay:d}}catch(o){throw this.logger.error("Transaction failed",{error:o instanceof Error?o.message:o}),new l(`Transaction failed: ${o instanceof Error?o.message:"Unknown error"}`,"TRANSACTION_FAILED")}}async transferUSDC(e){if(!this.selfCustodyKey||!this.selfCustodyAddress)throw new l("Self-custody wallet not loaded. Call loadSelfCustodyWallet() or createSelfCustodyWallet() first.","WALLET_NOT_LOADED");let{to:t,amountUsdc:s}=e,n="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",a=6,r=parseUnits(s.toFixed(a),a),i=encodeFunctionData({abi:[{name:"transfer",type:"function",inputs:[{name:"to",type:"address"},{name:"amount",type:"uint256"}],outputs:[{type:"bool"}]}],functionName:"transfer",args:[t,r]});return this.logger.debug("Transferring USDC",{from:this.selfCustodyAddress,to:t,amountUsdc:s,amountMinorUnits:r.toString()}),(await this.executeFromOwnWallet({to:n,data:i})).txHash}async reportTransaction(e,t,s){if(!this.apiKey){this.logger.warn("Cannot report transaction - no API key set");return}let n=await fetch(`${this.baseUrl}/api/v2/agent/tx/report`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify({tx_hash:e,to_address:t,value:s.toString(),chain_id:base.id})});if(!n.ok){let a=await n.json().catch(()=>({}));throw new l(a.error||`Failed to report transaction: ${n.status}`,"TX_REPORT_FAILED")}}getAgentInstanceId(){return this.agentInstanceId}setAgentInstanceId(e){this.agentInstanceId=e;}setApiKey(e){this.apiKey=e;}mcp(){let e=join(dirname(fileURLToPath(import.meta.url)),"mcp-server.js"),t={MIXRPAY_SESSION_KEY:this.sessionKey.toString(),MIXRPAY_BASE_URL:this.baseUrl};return this.apiKey&&(t.MIXRPAY_API_KEY=this.apiKey),this.agentInstanceId&&(t.MIXRPAY_INSTANCE_ID=this.agentInstanceId),this.selfCustodyKey&&(t.AGENT_WALLET_KEY=this.selfCustodyKey),this.maxPaymentUsd&&(t.MIXRPAY_MAX_PAYMENT_USD=String(this.maxPaymentUsd)),{command:"node",args:[e],env:t}}async runDiagnostics(){this.logger.info("Running diagnostics...");let e=[],t=[],s={},n,a;s.sessionKeyFormat=true;try{let o=Date.now(),d=await fetch(`${this.baseUrl}/health`,{method:"GET",signal:AbortSignal.timeout(5e3)});n=Date.now()-o,s.apiConnectivity=d.ok,d.ok||(e.push(`API server returned ${d.status}. Check baseUrl configuration.`),t.push("Verify the baseUrl configuration points to a valid MixrPay server.")),n>2e3&&(e.push(`High API latency: ${n}ms. This may cause timeouts.`),t.push("Consider using a server closer to your region or check network connectivity."));}catch{s.apiConnectivity=false,e.push("Cannot connect to MixrPay API. Check your network connection and baseUrl."),t.push("Verify network connectivity and that the MixrPay server is running.");}try{let o=await this.getSessionKeyInfo(!0);s.sessionKeyValid=o.isValid,o.isValid||(e.push("Session key is invalid or has been revoked."),t.push("Request a new session key from the wallet owner at https://mixrpay.com/manage/invites"));let d=new Date,p=null;o.expiresAt&&(p=(o.expiresAt.getTime()-d.getTime())/(1e3*60*60),o.expiresAt<d?(s.sessionKeyValid=!1,e.push(`Session key expired on ${o.expiresAt.toISOString()}`),t.push("Create a new session key to continue making payments.")):p<24&&(e.push(`Session key expires in ${p.toFixed(1)} hours.`),t.push("Consider creating a new session key before the current one expires.")));let c=await this.getSpendingStats();a={remainingDailyUsd:c.remainingDailyUsd,remainingTotalUsd:c.remainingTotalUsd,expiresAt:o.expiresAt,expiresInHours:p},c.remainingDailyUsd!==null&&c.remainingDailyUsd<1&&(e.push(`Daily limit nearly exhausted: $${c.remainingDailyUsd.toFixed(2)} remaining.`),t.push("Wait until tomorrow for daily limit to reset, or request a higher daily limit.")),c.remainingTotalUsd!==null&&c.remainingTotalUsd<1&&(e.push(`Total limit nearly exhausted: $${c.remainingTotalUsd.toFixed(2)} remaining.`),t.push("Request a new session key with a higher total limit."));}catch{s.sessionKeyValid=false,e.push("Could not verify session key validity."),t.push("Check network connectivity and try again.");}let r=0;try{r=await this.getBalance(),s.hasBalance=r>0,r<=0?(e.push("Wallet has no USDC balance. Top up at https://mixrpay.com/manage/wallet"),t.push("Deposit USDC to your wallet address to enable payments.")):r<1&&(e.push(`Low balance: $${r.toFixed(2)}. Consider topping up.`),t.push("Top up your wallet balance to avoid payment failures."));}catch{s.hasBalance=false,e.push("Could not fetch wallet balance."),t.push("Check network connectivity and try again.");}let i=e.length===0;return this.logger.info("Diagnostics complete:",{healthy:i,issues:e,latencyMs:n}),{healthy:i,issues:e,checks:s,sdkVersion:ve,network:this.getNetwork().name,walletAddress:this.walletAddress,sessionLimits:a,latencyMs:n,recommendations:t}}async generateSiwe(e){let{domain:t,uri:s,statement:n,nonce:a=crypto.randomUUID(),issuedAt:r=new Date,expirationTime:i,notBefore:o,requestId:d,resources:p}=e,c=this.sessionKey.address,g=this.getNetwork().chainId,h="1",y=`${t} wants you to sign in with your Ethereum account:
|
|
16
|
+
`;if(y+=`${c}
|
|
17
|
+
|
|
18
|
+
`,n&&(y+=`${n}
|
|
19
|
+
|
|
20
|
+
`),y+=`URI: ${s}
|
|
21
|
+
`,y+=`Version: ${h}
|
|
22
|
+
`,y+=`Chain ID: ${g}
|
|
23
|
+
`,y+=`Nonce: ${a}
|
|
24
|
+
`,y+=`Issued At: ${r.toISOString()}`,i&&(y+=`
|
|
25
|
+
Expiration Time: ${i.toISOString()}`),o&&(y+=`
|
|
26
|
+
Not Before: ${o.toISOString()}`),d&&(y+=`
|
|
27
|
+
Request ID: ${d}`),p&&p.length>0){y+=`
|
|
28
|
+
Resources:`;for(let w of p)y+=`
|
|
29
|
+
- ${w}`;}let m=await this.sessionKey.signMessage(y);return {message:y,signature:m,address:c,chainId:g,nonce:a,issuedAt:r,expirationTime:i}}setDebug(e){this.logger.setLevel(e?"debug":"none");}setLogLevel(e){this.logger.setLevel(e);}async getSessionAuthHeaders(){let e=await W(this.sessionKey);return {"X-Session-Auth":JSON.stringify(e)}}async getOrCreateSession(e){this.logger.debug("getOrCreateSession called",e);let{merchantPublicKey:t,spendingLimitUsd:s,durationDays:n}=e;try{let h=await this.getSessionByMerchant(t);if(h&&h.status==="active")return this.logger.debug("Found existing active session",h.id),h}catch{}this.logger.info(`Creating new session with merchant ${t.slice(0,20)}...`);let a=await this.getSessionAuthHeaders(),r=await fetch(`${this.baseUrl}/api/v2/session/authorize`,{method:"POST",headers:{"Content-Type":"application/json",...a},body:JSON.stringify({merchant_public_key:t,spending_limit_usd:s,duration_days:n})});if(!r.ok){let h=await r.json().catch(()=>({}));throw new l(h.message||h.error||`Failed to create session: ${r.status}`)}let i=await r.json(),o=i.session_id,d=i.message_to_sign;if(!o||!d)throw new l("Invalid authorize response: missing session_id or message_to_sign");this.logger.debug("Signing session authorization message...");let p=await this.sessionKey.signMessage(d),c=await fetch(`${this.baseUrl}/api/v2/session/confirm`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({session_id:o,signature:p,wallet_address:this.walletAddress})});if(!c.ok){let h=await c.json().catch(()=>({}));throw new l(h.message||`Failed to confirm session: ${c.status}`)}let g=await c.json();return this.logger.info(`Session created: ${g.session?.id||o}`),this.parseSessionResponse(g.session||g)}async getSessionByMerchant(e){this.logger.debug("getSessionByMerchant",e);let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/session/status?merchant_public_key=${encodeURIComponent(e)}`,{headers:t});if(s.status===404)return null;if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.message||`Failed to get session: ${s.status}`)}let n=await s.json();return n.session?this.parseSessionResponse(n.session):null}async listSessions(){this.logger.debug("listSessions");let e=await this.getSessionAuthHeaders(),t=await fetch(`${this.baseUrl}/api/v2/session/list`,{headers:e});if(!t.ok){let n=await t.json().catch(()=>({}));throw new l(n.message||`Failed to list sessions: ${t.status}`)}return ((await t.json()).sessions||[]).map(n=>this.parseSessionResponse(n))}async revokeSession(e){this.logger.debug("revokeSession",e);let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/session/revoke`,{method:"POST",headers:{"Content-Type":"application/json",...t},body:JSON.stringify({session_id:e})});if(!s.ok){let n=await s.json().catch(()=>({}));return this.logger.error("Failed to revoke session:",n),false}return this.logger.info(`Session ${e} revoked`),true}async getSessionStats(){this.logger.debug("getSessionStats");let e=await this.listSessions(),t=new Date,s=[],n=[],a=[];for(let d of e)d.status==="revoked"?a.push(d):d.status==="expired"||d.expiresAt&&d.expiresAt<t?n.push(d):d.status==="active"&&s.push(d);let r=s.reduce((d,p)=>d+p.spendingLimitUsd,0),i=e.reduce((d,p)=>d+p.amountUsedUsd,0),o=s.reduce((d,p)=>d+p.remainingLimitUsd,0);return {activeCount:s.length,expiredCount:n.length,revokedCount:a.length,totalAuthorizedUsd:r,totalSpentUsd:i,totalRemainingUsd:o,activeSessions:s.map(d=>({id:d.id,merchantName:d.merchantName,merchantPublicKey:d.merchantId,spendingLimitUsd:d.spendingLimitUsd,remainingUsd:d.remainingLimitUsd,expiresAt:d.expiresAt}))}}async chargeSession(e,t,s={}){this.logger.debug("chargeSession",{sessionId:e,amountUsd:t,options:s});let n=await this.getSessionAuthHeaders(),a=await fetch(`${this.baseUrl}/api/v2/charge`,{method:"POST",headers:{"Content-Type":"application/json",...n},body:JSON.stringify({session_id:e,price_usd:t,feature:s.feature,idempotency_key:s.idempotencyKey,metadata:s.metadata})});if(!a.ok){let i=await a.json().catch(()=>({})),o=(i.error||i.error_code||"").toLowerCase();if(a.status===402){if(o==="session_limit_exceeded"){let d=i.sessionLimitUsd||i.session_limit_usd||0,p=i.remainingUsd||i.remaining_usd||0;throw new U(d,t,p,e)}if(o==="insufficient_balance")throw new S(t,i.availableUsd||i.available_usd||0)}throw a.status===404||o==="session_not_found"?new P(e):o==="session_expired"?new R(e,i.expiredAt||i.expires_at):o==="session_revoked"?new I(e,i.reason):new l(i.message||`Charge failed: ${a.status}`)}let r=await a.json();return this.logger.payment(t,e,s.feature),{success:true,chargeId:r.chargeId||r.charge_id,amountUsd:r.amountUsd||r.amount_usd||t,txHash:r.txHash||r.tx_hash,remainingSessionBalanceUsd:r.remainingSessionBalanceUsd||r.remaining_session_balance_usd||0}}async callMerchantApi(e){let{url:t,method:s="POST",body:n,headers:a={},merchantPublicKey:r,priceUsd:i,feature:o}=e;if(this.logger.debug("callMerchantApi",{url:t,method:s,merchantPublicKey:r,priceUsd:i}),await this.validateMerchantAllowed(t,"url"),i!==void 0&&this.maxPaymentUsd!==void 0&&i>this.maxPaymentUsd)throw new k("client_max",this.maxPaymentUsd,i);let d=await this.getOrCreateSession({merchantPublicKey:r}),p={"Content-Type":"application/json","X-Mixr-Session":d.id,...a};o&&(p["X-Mixr-Feature"]=o);let c=n!==void 0?typeof n=="string"?n:JSON.stringify(n):void 0,g=await fetch(t,{method:s,headers:p,body:c,signal:AbortSignal.timeout(this.timeout)}),h=g.headers.get("X-Mixr-Charged");if(h){let y=parseFloat(h);if(!isNaN(y)){let m={amountUsd:y,recipient:r,txHash:g.headers.get("X-Payment-TxHash"),timestamp:new Date,description:o||"API call",url:t,requestId:crypto.randomUUID(),correlationId:this.extractCorrelationId(a)};this.payments.push(m),this.totalSpentUsd+=y,this.logger.payment(y,r,o),this.onPayment&&this.onPayment(m);}}if(g.status===402){let y=await g.json().catch(()=>({})),m=(y.error||y.error_code||"").toLowerCase();if(m==="session_expired"){this.logger.info("Session expired, creating new one...");let w=await this.getOrCreateSession({merchantPublicKey:r});return p["X-Mixr-Session"]=w.id,fetch(t,{method:s,headers:p,body:c,signal:AbortSignal.timeout(this.timeout)})}if(m==="session_limit_exceeded"){let w=y.sessionLimitUsd||y.session_limit_usd||0,f=y.remainingUsd||y.remaining_usd||0;throw new U(w,i||0,f,d.id)}if(m==="session_revoked")throw new I(d.id,y.reason);if(m==="session_not_found")throw new P(d.id);if(m==="insufficient_balance")throw new S(i||0,y.availableUsd||y.available_usd||0)}return g}parseSessionResponse(e){return {id:e.id||e.session_id||e.sessionId,merchantId:e.merchantId||e.merchant_id,merchantName:e.merchantName||e.merchant_name||"Unknown",status:e.status||"active",spendingLimitUsd:Number(e.spendingLimitUsd||e.spending_limit_usd||e.spendingLimit||0),amountUsedUsd:Number(e.amountUsedUsd||e.amount_used_usd||e.amountUsed||0),remainingLimitUsd:Number(e.remainingLimitUsd||e.remaining_limit_usd||e.remainingLimit||Number(e.spendingLimitUsd||e.spending_limit_usd||0)-Number(e.amountUsedUsd||e.amount_used_usd||0)),expiresAt:new Date(e.expiresAt||e.expires_at),createdAt:new Date(e.createdAt||e.created_at)}}async getMCPAuthHeaders(){let e=Date.now().toString(),t=`MixrPay MCP Auth
|
|
30
|
+
Wallet: ${this.walletAddress}
|
|
31
|
+
Timestamp: ${e}`,s=await this.sessionKey.signMessage(t);return {"X-Mixr-Wallet":this.walletAddress,"X-Mixr-Signature":s,"X-Mixr-Timestamp":e}}async listTools(){this.logger.debug("listTools");let e=await fetch(`${this.baseUrl}/api/mcp`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({jsonrpc:"2.0",method:"tools/list",id:Date.now()})});if(!e.ok)throw new l(`Failed to list MCP tools: ${e.status}`);let t=await e.json();if(t.error)throw new l(t.error.message||"Failed to list MCP tools");return (t.result?.tools||[]).map(s=>({name:s.name,description:s.description,inputSchema:s.inputSchema,priceUsd:s["x-mixrpay"]?.priceUsd||0,merchantName:s["x-mixrpay"]?.merchantName,merchantSlug:s["x-mixrpay"]?.merchantSlug,verified:s["x-mixrpay"]?.verified||false}))}async listMCPTools(){return this.listTools()}async callTool(e,t={}){this.logger.debug("callTool",{toolName:e,args:t}),await this.validateMerchantAllowed(e,"tool");let s=await this.getMCPAuthHeaders(),a=await(await fetch(`${this.baseUrl}/api/mcp`,{method:"POST",headers:{"Content-Type":"application/json",...s},body:JSON.stringify({jsonrpc:"2.0",method:"tools/call",params:{name:e,arguments:t},id:Date.now()})})).json();if(a.error)throw new l(a.error.message||"MCP tool call failed");let r=a.result?.content?.[0],i=r?.text?JSON.parse(r.text):null,o=a.result?._mixrpay||{};if(o.chargedUsd){let d={amountUsd:o.chargedUsd,recipient:e.split("/")[0]||e,txHash:o.txHash,timestamp:new Date,description:`MCP: ${e}`,url:`${this.baseUrl}/api/mcp`,requestId:crypto.randomUUID()};this.payments.push(d),this.totalSpentUsd+=o.chargedUsd,this.logger.payment(o.chargedUsd,e,"MCP call"),this.onPayment&&this.onPayment(d);}return {data:i,chargedUsd:o.chargedUsd||0,txHash:o.txHash,latencyMs:o.latencyMs}}async callMCPTool(e,t={}){return this.callTool(e,t)}async deployJitMcp(e){this.logger.debug("deployJitMcp",{glamaId:e.glamaId,toolName:e.toolName});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/jit/deploy`,{method:"POST",headers:{"Content-Type":"application/json",...t},body:JSON.stringify({glama_id:e.glamaId,glama_namespace:e.glamaNamespace,glama_slug:e.glamaSlug,tool_name:e.toolName,tool_description:e.toolDescription,env_vars:e.envVars,ttl_hours:e.ttlHours||24})});if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.error||`JIT deploy failed: ${s.status}`)}let n=await s.json();if(n.payment?.amount_usd>0){let a={amountUsd:n.payment.amount_usd,recipient:"mixrpay-jit-deploy",txHash:n.payment.tx_hash,timestamp:new Date,description:`JIT Deploy: ${e.toolName}`,url:`${this.baseUrl}/api/v2/jit/deploy`,requestId:n.request_id||crypto.randomUUID()};this.payments.push(a),this.totalSpentUsd+=n.payment.amount_usd,this.logger.payment(n.payment.amount_usd,"jit-deploy",e.toolName),this.onPayment&&this.onPayment(a);}return {instance:this.parseJitInstance(n.instance),payment:{method:n.payment.method,amountUsd:n.payment.amount_usd,txHash:n.payment.tx_hash}}}async listJitInstances(e){this.logger.debug("listJitInstances",e);let t=await this.getSessionAuthHeaders(),s=new URLSearchParams;e?.status&&s.set("status",e.status);let n=await fetch(`${this.baseUrl}/api/v2/jit/instances?${s}`,{headers:t});if(!n.ok){let r=await n.json().catch(()=>({}));throw new l(r.error||`Failed to list JIT instances: ${n.status}`)}return ((await n.json()).instances||[]).map(r=>this.parseJitInstance(r))}async stopJitInstance(e){this.logger.debug("stopJitInstance",{instanceId:e});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/jit/instances/${e}`,{method:"DELETE",headers:t});if(!s.ok){let n=await s.json().catch(()=>({}));throw new l(n.error||`Failed to stop JIT instance: ${s.status}`)}}parseJitInstance(e){return {id:e.id,endpointUrl:e.endpoint_url,toolName:e.tool_name,glamaId:e.glama_id,glamaNamespace:e.glama_namespace,glamaSlug:e.glama_slug,status:e.status,mode:e.mode,ttlHours:e.ttl_hours,expiresAt:new Date(e.expires_at),createdAt:new Date(e.created_at)}}async getJitInstance(e){this.logger.debug("getJitInstance",{instanceId:e});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/jit/instances/${e}`,{headers:t});if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.error||`Failed to get JIT instance: ${s.status}`)}let n=await s.json();return this.parseJitInstance(n.instance)}async executeTransaction(e){this.logger.debug("executeTransaction",{to:e.to,estimatedCostUsd:e.estimatedCostUsd});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/tx/execute`,{method:"POST",headers:{"Content-Type":"application/json",...t},body:JSON.stringify({to:e.to,data:e.data,value:e.value?.toString()??"0",chain_id:e.chainId,estimated_cost_usd:e.estimatedCostUsd,description:e.description,idempotency_key:e.idempotencyKey})}),n=await s.json().catch(()=>({}));if(!s.ok){let a=(n.error_code||"tx_failed").toLowerCase(),r=n.error||`Transaction execution failed: ${s.status}`;throw a==="budget_exceeded"?new l(r,"SPENDING_LIMIT_EXCEEDED"):new l(r,a)}return this.logger.info("Transaction executed",{txHash:n.tx_hash,estimatedCostUsd:e.estimatedCostUsd,remainingBudgetUsd:n.remaining_budget_usd}),{success:true,txHash:n.tx_hash,chargeId:n.charge_id,estimatedCostUsd:n.estimated_cost_usd,remainingBudgetUsd:n.remaining_budget_usd}}async useSkill(e,t){this.logger.debug("useSkill",{skillId:e,options:t});let s=await this.getSessionAuthHeaders(),n=await fetch(`${this.baseUrl}/api/v2/skills/${e}/use`,{method:"POST",headers:{"Content-Type":"application/json",...s},body:JSON.stringify({ttl_hours:t?.ttlHours||24})});if(!n.ok){let r=await n.json().catch(()=>({}));throw r.configure_url?new l(`${r.error||"Skill not configured"}. Configure at: ${this.baseUrl}${r.configure_url}`,"SKILL_NOT_CONFIGURED"):new l(r.error||`Failed to use skill: ${n.status}`)}let a=await n.json();return {skillId:a.skill_id,endpoint:a.endpoint,tools:a.tools||[],expiresAt:new Date(a.expires_at),instanceId:a.instance_id}}async listSkills(){this.logger.debug("listSkills");let e=await this.getSessionAuthHeaders(),t=await fetch(`${this.baseUrl}/api/v2/skills`,{headers:e});if(!t.ok){let n=await t.json().catch(()=>({}));throw new l(n.error||`Failed to list skills: ${t.status}`)}return (await t.json()).skills||[]}async getConnectedApps(){this.logger.debug("getConnectedApps");let e=await this.getSessionAuthHeaders(),t=await fetch(`${this.baseUrl}/api/v2/skills/composio/connections`,{headers:e});if(!t.ok){let a=await t.json().catch(()=>({}));throw new l(a.error||`Failed to get connected apps: ${t.status}`,"CONNECTED_APPS_FAILED")}let s=await t.json();return {connected:(s.connections||[]).filter(a=>a.status==="connected").map(a=>a.app.toLowerCase()),connections:s.connections||[],oauthConnections:s.oauth_connections||[],apiKeyConnections:s.api_key_connections||[]}}async getSkillStatus(e){this.logger.debug("getSkillStatus",{skillId:e});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/skills/${e}/configure`,{headers:t});if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.error||`Failed to get skill status: ${s.status}`)}let n=await s.json();return {skillId:n.skill_id,status:n.status,configuredVars:n.configured_vars||[],lastUsedAt:n.last_used_at?new Date(n.last_used_at):void 0,errorMessage:n.error_message}}async configureSkill(e,t){this.logger.debug("configureSkill",{skillId:e,varNames:Object.keys(t)});let s=await this.getSessionAuthHeaders(),n=await fetch(`${this.baseUrl}/api/v2/skills/${e}/configure`,{method:"POST",headers:{"Content-Type":"application/json",...s},body:JSON.stringify({env_vars:t})});if(!n.ok){let r=await n.json().catch(()=>({}));throw new l(r.error||`Failed to configure skill: ${n.status}`,r.error_code)}let a=await n.json();return {success:true,skillId:a.skill_id,configuredVars:a.configured_vars||[]}}async searchGlamaDirectory(e){this.logger.debug("searchGlamaDirectory",{query:e});let t=new URLSearchParams({q:e}),s=await fetch(`${this.baseUrl}/api/mcp/glama?${t}`);if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.error||`Glama search failed: ${s.status}`)}let n=await s.json();return {servers:(n.servers||[]).map(a=>this.parseGlamaServer(a)),pageInfo:n.pageInfo,query:n.query}}async getFeaturedGlamaServers(){this.logger.debug("getFeaturedGlamaServers");let e=await fetch(`${this.baseUrl}/api/mcp/glama`);if(!e.ok){let s=await e.json().catch(()=>({}));throw new l(s.error||`Failed to get featured servers: ${e.status}`)}let t=await e.json();return {servers:(t.servers||[]).map(s=>this.parseGlamaServer(s)),featured:t.featured}}parseGlamaServer(e){let t=e.importData;return {id:e.id,name:e.name,namespace:e.namespace,slug:e.slug,description:e.description,url:e.url,attributes:e.attributes,canHost:e.canHost,tools:e.tools||[],repository:e.repository,license:e.spdxLicense?.name,importData:t?{glamaId:t.glamaId,glamaNamespace:t.glamaNamespace,glamaSlug:t.glamaSlug,suggestedName:t.suggestedName,suggestedDescription:t.suggestedDescription,hostingType:t.hostingType,requiredEnvVars:t.requiredEnvVars,optionalEnvVars:t.optionalEnvVars}:void 0}}async deployTaskAgent(e){this.logger.debug("deployTaskAgent",{name:e.name,budgetUsd:e.budgetUsd});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/jit-task/deploy`,{method:"POST",headers:{"Content-Type":"application/json",...t},body:JSON.stringify({name:e.name,prompt:e.prompt,system_prompt:e.systemPrompt,model:e.model||"claude-sonnet-4-5",tools:e.tools||[],budget_usd:e.budgetUsd,ttl_hours:e.ttlHours||24,max_iterations:e.maxIterations||10,plan_id:e.planId,task_id:e.taskId,idempotency_key:e.idempotencyKey,auto_run:e.autoRun||false})});if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.error||`Task agent deploy failed: ${s.status}`)}let n=await s.json();return {instance:this.parseTaskAgentInstance(n.instance),idempotent:n.idempotent||false,requestId:n.request_id}}async getTaskAgentStatus(e){this.logger.debug("getTaskAgentStatus",{instanceId:e});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/jit-task/${e}`,{method:"GET",headers:t});if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.error||`Failed to get task status: ${s.status}`)}let n=await s.json();return this.parseTaskAgentStatus(n)}async triggerTaskAgent(e){this.logger.debug("triggerTaskAgent",{instanceId:e});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/jit-task/${e}`,{method:"POST",headers:{"Content-Type":"application/json",...t}});if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.error||`Failed to trigger task: ${s.status}`)}let n=await s.json();return {success:n.success,status:n.status}}async cancelTaskAgent(e){this.logger.debug("cancelTaskAgent",{instanceId:e});let t=await this.getSessionAuthHeaders(),s=await fetch(`${this.baseUrl}/api/v2/jit-task/${e}`,{method:"DELETE",headers:t});if(!s.ok){let a=await s.json().catch(()=>({}));throw new l(a.error||`Failed to cancel task: ${s.status}`)}let n=await s.json();return {success:n.success,status:n.status}}async listTaskAgents(e){this.logger.debug("listTaskAgents",{options:e});let t=await this.getSessionAuthHeaders(),s=new URLSearchParams;e?.status&&s.set("status",e.status),e?.planId&&s.set("plan_id",e.planId),e?.taskId&&s.set("task_id",e.taskId),e?.limit!==void 0&&s.set("limit",String(e.limit)),e?.offset!==void 0&&s.set("offset",String(e.offset));let n=s.toString(),a=`${this.baseUrl}/api/v2/jit-task${n?`?${n}`:""}`,r=await fetch(a,{method:"GET",headers:t});if(!r.ok){let o=await r.json().catch(()=>({}));throw new l(o.error||`Failed to list task agents: ${r.status}`)}let i=await r.json();return {taskAgents:(i.task_agents||[]).map(o=>{let d=o.budget,p=o.iterations,c=o.usage,g=o.plan,h=o.task;return {id:o.id,name:o.name,prompt:o.prompt,model:o.model,tools:o.tools,status:o.status,budget:{totalUsd:d.total_usd,spentUsd:d.spent_usd,remainingUsd:d.remaining_usd},iterations:{current:p.current,max:p.max},usage:{toolCalls:c.tool_calls,inputTokens:c.input_tokens,outputTokens:c.output_tokens},result:o.result,error:o.error,depth:o.depth,plan:g?{id:g.id,title:g.title}:null,task:h?{id:h.id,title:h.title}:null,ttlHours:o.ttl_hours,expiresAt:new Date(o.expires_at),createdAt:new Date(o.created_at),startedAt:o.started_at?new Date(o.started_at):void 0,completedAt:o.completed_at?new Date(o.completed_at):void 0}}),pagination:{total:i.pagination.total,limit:i.pagination.limit,offset:i.pagination.offset,hasMore:i.pagination.has_more},stats:{active:i.stats.active??0,completed:i.stats.completed??0,failed:i.stats.failed??0,totalSpentUsd:i.stats.total_spent_usd??0}}}async waitForTaskAgent(e,t){let s=t?.pollIntervalMs||2e3,n=t?.timeoutMs||3e5,a=Date.now();for(this.logger.debug("waitForTaskAgent",{instanceId:e,pollIntervalMs:s,timeoutMs:n});;){let r=await this.getTaskAgentStatus(e);if(["completed","failed","cancelled","budget_exceeded","expired"].includes(r.status))return {id:r.id,status:r.status,result:r.result,error:r.error,spentUsd:r.budget.spentUsd,iterations:r.iterations.current,toolCalls:r.usage.toolCalls,completedAt:r.completedAt};if(Date.now()-a>n)throw new l(`Task agent wait timeout after ${n}ms`);await new Promise(i=>setTimeout(i,s));}}parseTaskAgentInstance(e){return {id:e.id,name:e.name,endpointUrl:e.endpoint_url,statusUrl:e.status_url,triggerUrl:e.trigger_url,cancelUrl:e.cancel_url,resultUrl:e.result_url,status:e.status,budgetUsd:e.budget_usd,expiresAt:new Date(e.expires_at),depth:e.depth,maxIterations:e.max_iterations}}parseTaskAgentStatus(e){let t=e.budget,s=e.iterations,n=e.usage,a=e.endpoints;return {id:e.id,name:e.name,prompt:e.prompt,model:e.model,tools:e.tools,status:e.status,budget:{totalUsd:t?.total_usd??0,spentUsd:t?.spent_usd??0,remainingUsd:t?.remaining_usd??0},iterations:{current:s?.current??0,max:s?.max??0},usage:{toolCalls:n?.tool_calls??0,inputTokens:n?.input_tokens??0,outputTokens:n?.output_tokens??0},result:e.result,error:e.error,depth:e.depth,planId:e.plan_id,taskId:e.task_id,endpoints:{statusUrl:a?.status_url??"",triggerUrl:a?.trigger_url??"",cancelUrl:a?.cancel_url??"",resultUrl:a?.result_url??""},ttlHours:e.ttl_hours,expiresAt:new Date(e.expires_at),createdAt:new Date(e.created_at),startedAt:e.started_at?new Date(e.started_at):void 0,completedAt:e.completed_at?new Date(e.completed_at):void 0}}async complete(e,t){this.logger.debug("complete",{promptLength:e.length,model:t?.model});let s=await this.runAgent({messages:[{role:"user",content:e}],config:{model:t?.model||"gpt-4o-mini",maxIterations:1,tools:[],systemPrompt:t?.systemPrompt}});return {text:s.response,costUsd:s.cost.totalUsd,tokens:s.tokens,model:t?.model||"gpt-4o-mini"}}async runAgent(e){let{sessionId:t,messages:s,config:n={},stream:a=false,idempotencyKey:r,onEvent:i}=e;this.logger.debug("runAgent",{sessionId:t||"(from signature)",messageCount:s.length,config:n,stream:a});let o={messages:s.map(p=>({role:p.role,content:p.content})),config:{model:n.model,max_iterations:n.maxIterations,tools:n.tools,system_prompt:n.systemPrompt},stream:a,idempotency_key:r};t&&(o.session_id=t);let d=18e4;if(!a){let p=await this.getSessionAuthHeaders(),c=await fetch(`${this.baseUrl}/api/v2/agent/run`,{method:"POST",headers:{"Content-Type":"application/json",...p},body:JSON.stringify(o),signal:AbortSignal.timeout(d)});if(!c.ok){let h=await c.json().catch(()=>({}));throw new l(h.error||`Agent run failed: ${c.status}`)}let g=await c.json();if(g.cost?.total_usd>0){let h={amountUsd:g.cost.total_usd,recipient:"mixrpay-agent-run",txHash:g.tx_hash,timestamp:new Date,description:`Agent run: ${g.run_id}`,url:`${this.baseUrl}/api/v2/agent/run`,requestId:r||crypto.randomUUID()};this.payments.push(h),this.totalSpentUsd+=g.cost.total_usd,this.logger.payment(g.cost.total_usd,"agent-run",g.run_id),this.onPayment&&this.onPayment(h);}return {runId:g.run_id,status:g.status,response:g.response,iterations:g.iterations,toolsUsed:g.tools_used,cost:{llmUsd:g.cost.llm_usd,toolsUsd:g.cost.tools_usd,totalUsd:g.cost.total_usd},tokens:g.tokens,sessionRemainingUsd:g.session_remaining_usd,txHash:g.tx_hash}}return this.runAgentStreaming(o,i)}async runAgentStreaming(e,t){let n=await this.getSessionAuthHeaders(),a=await fetch(`${this.baseUrl}/api/v2/agent/run`,{method:"POST",headers:{"Content-Type":"application/json",...n},body:JSON.stringify(e),signal:AbortSignal.timeout(3e5)});if(!a.ok){let p=await a.json().catch(()=>({}));throw new l(p.error||`Agent run failed: ${a.status}`)}let r=a.body?.getReader();if(!r)throw new l("No response body for streaming");let i=new TextDecoder,o="",d=null;for(;;){let{done:p,value:c}=await r.read();if(p)break;o+=i.decode(c,{stream:true});let g=o.split(`
|
|
32
|
+
`);o=g.pop()||"";let h="";for(let y of g)if(y.startsWith("event: "))h=y.slice(7).trim();else if(y.startsWith("data: ")&&h){try{let m=JSON.parse(y.slice(6)),w=this.parseSSEEvent(h,m);if(w){if(t?.(w),h==="complete"&&(d={runId:m.run_id,status:"completed",response:m.response,iterations:m.iterations,toolsUsed:m.tools_used,cost:{llmUsd:0,toolsUsd:0,totalUsd:m.total_cost_usd},tokens:{prompt:0,completion:0},sessionRemainingUsd:0,txHash:m.tx_hash},m.total_cost_usd>0)){let f={amountUsd:m.total_cost_usd,recipient:"mixrpay-agent-run",txHash:m.tx_hash,timestamp:new Date,description:`Agent run: ${m.run_id}`,url:`${this.baseUrl}/api/v2/agent/run`,requestId:e.idempotency_key||crypto.randomUUID()};this.payments.push(f),this.totalSpentUsd+=m.total_cost_usd,this.onPayment&&this.onPayment(f);}if(h==="error")throw new l(m.error||"Agent run failed")}}catch(m){if(m instanceof l)throw m;this.logger.warn("Failed to parse SSE event:",m);}h="";}}if(!d)throw new l("Agent run completed without final result");return d}parseSSEEvent(e,t){switch(e){case "run_start":return {type:"run_start",runId:t.run_id};case "iteration_start":return {type:"iteration_start",iteration:t.iteration};case "llm_chunk":return {type:"llm_chunk",delta:t.delta};case "tool_call":return {type:"tool_call",tool:t.tool,arguments:t.arguments};case "tool_result":return {type:"tool_result",tool:t.tool,success:t.success,costUsd:t.cost_usd,error:t.error};case "iteration_complete":return {type:"iteration_complete",iteration:t.iteration,tokens:t.tokens,costUsd:t.cost_usd};case "complete":return {type:"complete",runId:t.run_id,response:t.response,totalCostUsd:t.total_cost_usd,txHash:t.tx_hash,iterations:t.iterations,toolsUsed:t.tools_used};case "error":return {type:"error",error:t.error,partialCostUsd:t.partial_cost_usd};default:return null}}async getAgentRunStatus(e,t){this.logger.debug("getAgentRunStatus",{runId:e});let s=await this.getSessionAuthHeaders(),n=await fetch(`${this.baseUrl}/api/v2/agent/run/${e}`,{headers:s});if(!n.ok){let r=await n.json().catch(()=>({}));throw new l(r.error||`Failed to get run status: ${n.status}`)}let a=await n.json();return {runId:a.run_id,status:a.status,response:a.response,iterations:a.iterations,toolsUsed:a.tools_used,cost:{llmUsd:a.cost.llm_usd,toolsUsd:a.cost.tools_usd,totalUsd:a.cost.total_usd},tokens:a.tokens,txHash:a.tx_hash,error:a.error,startedAt:new Date(a.started_at),completedAt:a.completed_at?new Date(a.completed_at):void 0}}async callToolWithSession(e,t,s={}){this.logger.debug("callToolWithSession",{sessionId:e,toolName:t,args:s}),await this.validateMerchantAllowed(t,"tool");let a=await(await fetch(`${this.baseUrl}/api/mcp`,{method:"POST",headers:{"Content-Type":"application/json","X-Mixr-Session":e},body:JSON.stringify({jsonrpc:"2.0",method:"tools/call",params:{name:t,arguments:s},id:Date.now()})})).json();if(a.error)throw new l(a.error.message||"MCP tool call failed");let r=a.result?.content?.[0],i=r?.text?JSON.parse(r.text):null,o=a.result?._mixrpay||{};if(o.chargedUsd){let d={amountUsd:o.chargedUsd,recipient:t.split("/")[0]||t,txHash:o.txHash,timestamp:new Date,description:`MCP: ${t}`,url:`${this.baseUrl}/api/mcp`,requestId:crypto.randomUUID()};this.payments.push(d),this.totalSpentUsd+=o.chargedUsd,this.logger.payment(o.chargedUsd,t,"MCP call (session)"),this.onPayment&&this.onPayment(d);}return {data:i,chargedUsd:o.chargedUsd||0,txHash:o.txHash,latencyMs:o.latencyMs}}async callMCPToolWithSession(e,t,s={}){return this.callToolWithSession(e,t,s)}async createTask(e){this.logger.info(`Creating task: ${e.title}`);let t=await this.callApi("/api/v2/tasks",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:e.title,description:e.description,budget_usd:e.budgetUsd,deliverables:e.deliverables,category:e.category,expires_in_days:e.expiresInDays})}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to create task");return this.parseTask(s.task)}async listTasks(e){let t=new URLSearchParams;e?.status&&t.set("status",e.status),e?.category&&t.set("category",e.category),e?.minBudget!==void 0&&t.set("min_budget",String(e.minBudget)),e?.maxBudget!==void 0&&t.set("max_budget",String(e.maxBudget)),e?.limit!==void 0&&t.set("limit",String(e.limit)),e?.offset!==void 0&&t.set("offset",String(e.offset));let s=`/api/v2/tasks${t.toString()?`?${t}`:""}`,n=await this.callApi(s,{method:"GET"}),a=await n.json();if(!n.ok)throw new l(a.error||"Failed to list tasks");return {tasks:a.tasks.map(r=>this.parseTask(r)),pagination:a.pagination}}async getTask(e){let t=await this.callApi(`/api/v2/tasks/${e}`,{method:"GET"}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to get task");return this.parseTask(s.task)}async getMyTasks(e={}){let t=new URLSearchParams;e.role&&t.set("role",e.role),e.status&&t.set("status",e.status),e.page&&t.set("page",e.page.toString()),e.limit&&t.set("limit",e.limit.toString());let s=t.toString(),n=`/api/v2/tasks/my${s?`?${s}`:""}`,a=await this.callApi(n,{method:"GET"}),r=await a.json();if(!a.ok)throw new l(r.error||"Failed to get tasks");return {tasks:r.tasks.map(i=>({...this.parseTask(i),isCreator:i.is_creator,isAgent:i.is_agent})),pagination:{page:r.pagination.page,limit:r.pagination.limit,total:r.pagination.total,totalPages:r.pagination.total_pages}}}async requestTask(e,t){this.logger.info(`Requesting task: ${e}`);let s=await this.callApi(`/api/v2/tasks/${e}/request`,{method:"POST",headers:{"Content-Type":"application/json"},body:t?JSON.stringify({message:t}):"{}"}),n=await s.json();if(!s.ok)throw new l(n.error||"Failed to request task");return {id:n.request.id,taskId:n.request.task_id,status:n.request.status,message:n.request.message,createdAt:new Date(n.request.created_at)}}async getTaskRequests(e){let t=await this.callApi(`/api/v2/tasks/${e}/requests`,{method:"GET"}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to get task requests");return s.requests.map(n=>({id:n.id,status:n.status,message:n.message,createdAt:new Date(n.created_at),reviewedAt:n.reviewed_at?new Date(n.reviewed_at):void 0,agent:n.agent}))}async acceptTaskRequest(e,t){this.logger.info(`Accepting request ${t} for task ${e}`);let s=await this.callApi(`/api/v2/tasks/${e}/requests/${t}/accept`,{method:"POST"}),n=await s.json();if(!s.ok)throw new l(n.error||"Failed to accept request");return {success:n.success,task:{id:n.task.id,status:n.task.status,agentUserId:n.task.agent_user_id},session:{id:n.session.id,sessionKey:n.session.session_key,address:n.session.address,expiresAt:new Date(n.session.expires_at),budgetUsd:n.session.budget_usd,allowedMerchants:n.session.allowed_merchants||[]},invite:{id:n.invite.id,code:n.invite.code},listingFeeTxHash:n.listing_fee_tx_hash}}async rejectTaskRequest(e,t){let s=await this.callApi(`/api/v2/tasks/${e}/requests/${t}/reject`,{method:"POST"}),n=await s.json();if(!s.ok)throw new l(n.error||"Failed to reject request")}async submitTask(e,t){this.logger.info(`Submitting task: ${e}`);let s=await this.callApi(`/api/v2/tasks/${e}/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({output_text:t.text,output_url:t.url})}),n=await s.json();if(!s.ok)throw new l(n.error||"Failed to submit task")}async approveTask(e){this.logger.info(`Approving task: ${e}`);let t=await this.callApi(`/api/v2/tasks/${e}/approve`,{method:"POST"}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to approve task");return {success:s.success,task:{id:s.task.id,status:s.task.status,completedAt:new Date(s.task.completed_at)},payout:{status:s.payout.status,amountUsd:s.payout.amount_usd,txHash:s.payout.tx_hash}}}async rejectTaskSubmission(e,t){let s=await this.callApi(`/api/v2/tasks/${e}/reject`,{method:"POST",headers:{"Content-Type":"application/json"},body:t?JSON.stringify({feedback:t}):"{}"}),n=await s.json();if(!s.ok)throw new l(n.error||"Failed to reject task")}async cancelTask(e){let t=await this.callApi(`/api/v2/tasks/${e}`,{method:"DELETE"}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to cancel task")}async createSubtask(e){this.logger.info(`Creating subtask: ${e.title}`);let t=await this.callApi("/api/v2/tasks/create-subtask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parent_task_id:e.parentTaskId,title:e.title,description:e.description,deliverables:e.deliverables,category:e.category,budget_usd:e.budgetUsd,allow_sub_agents:e.allowSubAgents,expires_in_days:e.expiresInDays,idempotency_key:e.idempotencyKey})}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to create subtask");return this.parseTask(s.task)}async updateTaskStatus(e,t,s,n){this.logger.info(`Updating task ${e} status to ${t}`);let a=await this.callApi(`/api/v2/tasks/${e}/status`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({status:t,note:s,idempotency_key:n})}),r=await a.json();if(!a.ok)throw new l(r.error||"Failed to update task status");return this.parseTask(r.task)}async getSubtasks(e){let t=await this.callApi(`/api/v2/tasks/${e}/subtasks`,{method:"GET"}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to get subtasks");return (s.subtasks||[]).map(n=>this.parseTask(n))}async getTaskStatusHistory(e){let t=await this.callApi(`/api/v2/tasks/${e}/status`,{method:"GET"}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to get task status history");return {taskId:s.task_id,currentStatus:s.current_status,history:(s.history||[]).map(n=>({id:n.id,oldStatus:n.old_status,newStatus:n.new_status,note:n.note,createdAt:new Date(n.created_at)}))}}async requestApproval(e){this.logger.info(`Requesting approval for: ${e.actionType}`);let t=await this.callApi("/api/v2/approvals/request",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action_type:e.actionType,action_payload:e.actionPayload,context:e.context,expires_in_hours:e.expiresInHours,idempotency_key:e.idempotencyKey})}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to request approval");return s.auto_approved?{autoApproved:true,reason:s.reason}:this.parseApprovalRequest(s.approval_request)}async checkApprovalStatus(e){let t=await this.callApi(`/api/v2/approvals/check?request_id=${e}`,{method:"GET"}),s=await t.json();if(!t.ok)throw new l(s.error||"Failed to check approval status");return {requestId:s.request_id,status:s.status,responseNote:s.response_note,approvalScope:s.approval_scope,respondedAt:s.responded_at?new Date(s.responded_at):void 0}}async checkActionPermission(e,t){let s=`/api/v2/approvals/check?action_type=${encodeURIComponent(e)}`;t!==void 0&&(s+=`&amount_usd=${t}`);let n=await this.callApi(s,{method:"GET"}),a=await n.json();if(!n.ok)throw new l(a.error||"Failed to check action permission");return {actionType:a.action_type,needsApproval:a.needs_approval,reason:a.reason}}async waitForApproval(e,t={}){let s=t.pollIntervalMs||5e3,n=t.timeoutMs||3e5,a=Date.now();for(;Date.now()-a<n;){let r=await this.checkApprovalStatus(e);if(r.status!=="pending")return r;await new Promise(i=>setTimeout(i,s));}return {requestId:e,status:"expired"}}parseApprovalRequest(e){return {id:e.id,actionType:e.action_type,actionPayload:e.action_payload,context:e.context,status:e.status,responseNote:e.response_note,approvalScope:e.approval_scope,createdAt:new Date(e.created_at),expiresAt:new Date(e.expires_at),respondedAt:e.responded_at?new Date(e.responded_at):void 0}}parseTask(e){return {id:e.id,title:e.title,description:e.description,deliverables:e.deliverables||[],category:e.category,budgetUsd:e.budget_usd,listingFeeUsd:e.listing_fee_usd,status:e.status,createdAt:new Date(e.created_at),updatedAt:e.updated_at?new Date(e.updated_at):void 0,expiresAt:e.expires_at?new Date(e.expires_at):void 0,claimedAt:e.claimed_at?new Date(e.claimed_at):void 0,submittedAt:e.submitted_at?new Date(e.submitted_at):void 0,completedAt:e.completed_at?new Date(e.completed_at):void 0,creator:e.creator,assignedAgent:e.assigned_agent,requestCount:e.request_count,output:e.output,payment:e.payment}}async getBalances(){let e=await this.callApiWithAuth("/api/v2/agent/balances",{method:"GET"});if(!e.ok){let t=await e.json().catch(()=>({}));throw new l(t.error||`Failed to get balances: ${e.status}`)}return e.json()}async transfer(e,t,s="USDC"){let n=await this.callApiWithAuth("/api/v2/agent/transfer",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({to:e,amount:t,currency:s})});if(!n.ok){let a=await n.json().catch(()=>({}));throw new l(a.error||`Transfer failed: ${n.status}`)}return n.json()}async swap(e,t,s,n=100){let a=await this.callApiWithAuth("/api/v2/agent/swap",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sell_token:e,buy_token:t,sell_amount:s,slippage_bps:n})});if(!a.ok){let r=await a.json().catch(()=>({}));throw new l(r.error||`Swap failed: ${a.status}`)}return a.json()}async bridge(e,t,s,n){let a=await this.callApiWithAuth("/api/v2/agent/bridge",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e,amount:t,dest_chain:s,dest_address:n})});if(!a.ok){let r=await a.json().catch(()=>({}));throw new l(r.error||`Bridge failed: ${a.status}`)}return a.json()}async getBridgeStatus(e){let t=await this.callApiWithAuth(`/api/v2/agent/bridge/status?order_id=${encodeURIComponent(e)}`,{method:"GET"});if(!t.ok){let s=await t.json().catch(()=>({}));throw new l(s.error||`Bridge status check failed: ${t.status}`)}return t.json()}async fetchPaid(e,t){let s=await this.callApiWithAuth("/api/v2/agent/fetch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:e,method:t?.method||"GET",headers:t?.headers,body:t?.body,max_payment_usd:t?.maxPaymentUsd})});if(!s.ok){let n=await s.json().catch(()=>({}));throw new l(n.error||`Fetch failed: ${s.status}`)}return s.json()}async callApiWithAuth(e,t={}){let s=`${this.baseUrl}${e}`,n={...t.headers};if(this.apiKey)n.Authorization=`Bearer ${this.apiKey}`;else {let a=await this.getSessionAuthHeaders();Object.assign(n,a);}return fetch(s,{...t,headers:n})}async callApi(e,t={}){let s=`${this.baseUrl}${e}`,n={...t.headers};if(this.sessionKey){let r=privateKeyToAccount(this.sessionKey.rawPrivateKey).address.toLowerCase(),i=Date.now(),o=`MixrPay:${i}:${r}`,d=await signMessage({message:o,privateKey:this.sessionKey.rawPrivateKey});n["X-Session-Auth"]=JSON.stringify({address:r,timestamp:i,signature:d});}return fetch(s,{...t,headers:n})}};export{l as a,ve as b,b as c,Be as d,Se as e,Z as f,ee as g};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import*as r from'fs';import*as s from'path';import*as o from'os';function d(){if(o.platform()==="win32"){let n=process.env.APPDATA||s.join(o.homedir(),"AppData","Roaming");return s.join(n,"mixrpay")}let e=process.env.XDG_CONFIG_HOME||s.join(o.homedir(),".config");return s.join(e,"mixrpay")}function a(){return s.join(d(),"credentials.json")}function g(){let t=d();r.existsSync(t)||r.mkdirSync(t,{recursive:true,mode:448});}function f(t){try{r.chmodSync(t,384);}catch{}}function y(t){try{g();let e=a(),n={};if(r.existsSync(e))try{let l=r.readFileSync(e,"utf-8");n=JSON.parse(l);}catch{n={};}let i={...n,...t,updatedAt:new Date().toISOString()},c=Object.fromEntries(Object.entries(i).filter(([l,p])=>p!==void 0));return r.writeFileSync(e,JSON.stringify(c,null,2),{mode:384}),f(e),!0}catch(e){return console.error("[MixrPay] Failed to save credentials:",e),false}}function u(){try{let t=a();if(!r.existsSync(t))return {success:!0,credentials:{}};let e=r.readFileSync(t,"utf-8");return {success:!0,credentials:JSON.parse(e)}}catch(t){return {success:false,error:t instanceof Error?t.message:"Failed to load credentials"}}}function S(t){try{let e=a();if(!r.existsSync(e))return !0;if(!t)return r.unlinkSync(e),!0;let n=u();if(!n.success)return !1;let i={...n.credentials};for(let c of t)delete i[c];return Object.keys(i).length===0?r.unlinkSync(e):(r.writeFileSync(e,JSON.stringify(i,null,2),{mode:384}),f(e)),!0}catch(e){return console.error("[MixrPay] Failed to delete credentials:",e),false}}function m(){let t=u();if(!t.success)return false;let{credentials:e}=t;return !!(e.sessionKey||e.apiToken||e.masterKey)}function h(){return a()}export{y as a,u as b,S as c,m as d,h as e};
|