xache 5.0.0__py3-none-any.whl
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.
- xache/__init__.py +142 -0
- xache/client.py +331 -0
- xache/crypto/__init__.py +17 -0
- xache/crypto/signing.py +244 -0
- xache/crypto/wallet.py +240 -0
- xache/errors.py +184 -0
- xache/payment/__init__.py +5 -0
- xache/payment/handler.py +244 -0
- xache/services/__init__.py +29 -0
- xache/services/budget.py +285 -0
- xache/services/collective.py +174 -0
- xache/services/extraction.py +173 -0
- xache/services/facilitator.py +296 -0
- xache/services/identity.py +415 -0
- xache/services/memory.py +401 -0
- xache/services/owner.py +293 -0
- xache/services/receipts.py +202 -0
- xache/services/reputation.py +274 -0
- xache/services/royalty.py +290 -0
- xache/services/sessions.py +268 -0
- xache/services/workspaces.py +447 -0
- xache/types.py +399 -0
- xache/utils/__init__.py +5 -0
- xache/utils/cache.py +214 -0
- xache/utils/http.py +209 -0
- xache/utils/retry.py +101 -0
- xache-5.0.0.dist-info/METADATA +337 -0
- xache-5.0.0.dist-info/RECORD +30 -0
- xache-5.0.0.dist-info/WHEEL +5 -0
- xache-5.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Royalty Service - Revenue tracking and earnings management
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Dict, Optional
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from ..types import DID
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class HeuristicRevenue:
|
|
12
|
+
"""Revenue for a single heuristic"""
|
|
13
|
+
heuristic_id: str
|
|
14
|
+
pattern: str
|
|
15
|
+
domain: str
|
|
16
|
+
earned_usd: str
|
|
17
|
+
usage_count: int
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class MonthlyEarning:
|
|
22
|
+
"""Monthly earnings"""
|
|
23
|
+
month: str
|
|
24
|
+
earned_usd: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class RevenueStats:
|
|
29
|
+
"""Revenue statistics for an agent"""
|
|
30
|
+
agent_did: DID
|
|
31
|
+
total_earned_usd: str
|
|
32
|
+
heuristic_count: int
|
|
33
|
+
top_heuristics: List[HeuristicRevenue]
|
|
34
|
+
earnings_by_domain: Dict[str, str]
|
|
35
|
+
monthly_earnings: List[MonthlyEarning]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class PendingPayout:
|
|
40
|
+
"""Pending payout information"""
|
|
41
|
+
heuristic_id: str
|
|
42
|
+
heuristic_key: str
|
|
43
|
+
pending_amount: str
|
|
44
|
+
last_query_at: str
|
|
45
|
+
query_count: int
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class PlatformRevenue:
|
|
50
|
+
"""Platform-wide revenue statistics"""
|
|
51
|
+
total_revenue: str
|
|
52
|
+
total_agents: int
|
|
53
|
+
total_heuristics: int
|
|
54
|
+
total_queries: int
|
|
55
|
+
average_revenue_per_agent: str
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class TopEarner:
|
|
60
|
+
"""Top earner information"""
|
|
61
|
+
rank: int
|
|
62
|
+
agent_did: DID
|
|
63
|
+
total_earned_usd: str
|
|
64
|
+
heuristic_count: int
|
|
65
|
+
reputation_score: float
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class RoyaltyService:
|
|
69
|
+
"""
|
|
70
|
+
Royalty service for revenue tracking and earnings management
|
|
71
|
+
|
|
72
|
+
Track royalties earned from heuristic contributions and pending payouts.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self, client):
|
|
76
|
+
self.client = client
|
|
77
|
+
|
|
78
|
+
async def get_revenue_stats(self, agent_did: DID) -> RevenueStats:
|
|
79
|
+
"""
|
|
80
|
+
Get revenue statistics for an agent
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
agent_did: Agent DID to get stats for
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Revenue statistics
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
```python
|
|
90
|
+
stats = await client.royalty.get_revenue_stats("did:agent:evm:0x1234...")
|
|
91
|
+
|
|
92
|
+
print(f"Total earned: ${stats.total_earned_usd}")
|
|
93
|
+
print(f"Heuristics: {stats.heuristic_count}")
|
|
94
|
+
|
|
95
|
+
for h in stats.top_heuristics:
|
|
96
|
+
print(f" {h.domain}: ${h.earned_usd} ({h.usage_count} uses)")
|
|
97
|
+
```
|
|
98
|
+
"""
|
|
99
|
+
from urllib.parse import quote
|
|
100
|
+
|
|
101
|
+
response = await self.client.request(
|
|
102
|
+
"GET", f"/v1/royalty/revenue/{quote(agent_did, safe='')}"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if not response.success or not response.data:
|
|
106
|
+
raise Exception(
|
|
107
|
+
response.error.get("message", "Failed to get revenue stats")
|
|
108
|
+
if response.error
|
|
109
|
+
else "Failed to get revenue stats"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
data = response.data
|
|
113
|
+
return RevenueStats(
|
|
114
|
+
agent_did=data["agentDID"],
|
|
115
|
+
total_earned_usd=data["totalEarnedUSD"],
|
|
116
|
+
heuristic_count=data["heuristicCount"],
|
|
117
|
+
top_heuristics=[
|
|
118
|
+
HeuristicRevenue(
|
|
119
|
+
heuristic_id=h["heuristicId"],
|
|
120
|
+
pattern=h["pattern"],
|
|
121
|
+
domain=h["domain"],
|
|
122
|
+
earned_usd=h["earnedUSD"],
|
|
123
|
+
usage_count=h["usageCount"],
|
|
124
|
+
)
|
|
125
|
+
for h in data.get("topHeuristics", [])
|
|
126
|
+
],
|
|
127
|
+
earnings_by_domain=data.get("earningsByDomain", {}),
|
|
128
|
+
monthly_earnings=[
|
|
129
|
+
MonthlyEarning(month=m["month"], earned_usd=m["earnedUSD"])
|
|
130
|
+
for m in data.get("monthlyEarnings", [])
|
|
131
|
+
],
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
async def get_pending_payouts(self, agent_did: DID) -> Dict:
|
|
135
|
+
"""
|
|
136
|
+
Get pending payouts for an agent
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
agent_did: Agent DID
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Pending payouts with total amount
|
|
143
|
+
|
|
144
|
+
Example:
|
|
145
|
+
```python
|
|
146
|
+
result = await client.royalty.get_pending_payouts("did:agent:evm:0x1234...")
|
|
147
|
+
|
|
148
|
+
print(f"Total pending: ${result['total_pending_amount']}")
|
|
149
|
+
for p in result['pending_payouts']:
|
|
150
|
+
print(f" {p.heuristic_key}: ${p.pending_amount}")
|
|
151
|
+
```
|
|
152
|
+
"""
|
|
153
|
+
from urllib.parse import quote
|
|
154
|
+
|
|
155
|
+
response = await self.client.request(
|
|
156
|
+
"GET", f"/v1/royalty/payouts/{quote(agent_did, safe='')}"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if not response.success or not response.data:
|
|
160
|
+
raise Exception(
|
|
161
|
+
response.error.get("message", "Failed to get pending payouts")
|
|
162
|
+
if response.error
|
|
163
|
+
else "Failed to get pending payouts"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
data = response.data
|
|
167
|
+
return {
|
|
168
|
+
"agent_did": data["agentDID"],
|
|
169
|
+
"pending_payouts": [
|
|
170
|
+
PendingPayout(
|
|
171
|
+
heuristic_id=p["heuristicId"],
|
|
172
|
+
heuristic_key=p["heuristicKey"],
|
|
173
|
+
pending_amount=p["pendingAmount"],
|
|
174
|
+
last_query_at=p["lastQueryAt"],
|
|
175
|
+
query_count=p["queryCount"],
|
|
176
|
+
)
|
|
177
|
+
for p in data.get("pendingPayouts", [])
|
|
178
|
+
],
|
|
179
|
+
"total_pending_count": data.get("totalPendingCount", 0),
|
|
180
|
+
"total_pending_amount": data.get("totalPendingAmount", "0"),
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async def get_platform_revenue(self) -> PlatformRevenue:
|
|
184
|
+
"""
|
|
185
|
+
Get platform-wide revenue statistics
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Platform revenue statistics
|
|
189
|
+
|
|
190
|
+
Example:
|
|
191
|
+
```python
|
|
192
|
+
platform = await client.royalty.get_platform_revenue()
|
|
193
|
+
|
|
194
|
+
print(f"Platform revenue: ${platform.total_revenue}")
|
|
195
|
+
print(f"Active agents: {platform.total_agents}")
|
|
196
|
+
print(f"Total heuristics: {platform.total_heuristics}")
|
|
197
|
+
```
|
|
198
|
+
"""
|
|
199
|
+
response = await self.client.request("GET", "/v1/royalty/platform")
|
|
200
|
+
|
|
201
|
+
if not response.success or not response.data:
|
|
202
|
+
raise Exception(
|
|
203
|
+
response.error.get("message", "Failed to get platform revenue")
|
|
204
|
+
if response.error
|
|
205
|
+
else "Failed to get platform revenue"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
data = response.data
|
|
209
|
+
return PlatformRevenue(
|
|
210
|
+
total_revenue=data["totalRevenue"],
|
|
211
|
+
total_agents=data["totalAgents"],
|
|
212
|
+
total_heuristics=data["totalHeuristics"],
|
|
213
|
+
total_queries=data["totalQueries"],
|
|
214
|
+
average_revenue_per_agent=data["averageRevenuePerAgent"],
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
async def get_top_earners(self, limit: int = 20) -> Dict:
|
|
218
|
+
"""
|
|
219
|
+
Get top earning agents
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
limit: Number of top earners to return (default: 20)
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
List of top earners
|
|
226
|
+
|
|
227
|
+
Example:
|
|
228
|
+
```python
|
|
229
|
+
result = await client.royalty.get_top_earners(10)
|
|
230
|
+
|
|
231
|
+
for earner in result['top_earners']:
|
|
232
|
+
print(f"#{earner.rank} {earner.agent_did}")
|
|
233
|
+
print(f" Earned: ${earner.total_earned_usd}")
|
|
234
|
+
```
|
|
235
|
+
"""
|
|
236
|
+
response = await self.client.request(
|
|
237
|
+
"GET", f"/v1/royalty/top-earners?limit={limit}"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if not response.success or not response.data:
|
|
241
|
+
raise Exception(
|
|
242
|
+
response.error.get("message", "Failed to get top earners")
|
|
243
|
+
if response.error
|
|
244
|
+
else "Failed to get top earners"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
data = response.data
|
|
248
|
+
return {
|
|
249
|
+
"top_earners": [
|
|
250
|
+
TopEarner(
|
|
251
|
+
rank=e["rank"],
|
|
252
|
+
agent_did=e["agentDID"],
|
|
253
|
+
total_earned_usd=e["totalEarnedUSD"],
|
|
254
|
+
heuristic_count=e["heuristicCount"],
|
|
255
|
+
reputation_score=e["reputationScore"],
|
|
256
|
+
)
|
|
257
|
+
for e in data.get("topEarners", [])
|
|
258
|
+
],
|
|
259
|
+
"total": data.get("total", 0),
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async def get_my_revenue(self) -> RevenueStats:
|
|
263
|
+
"""
|
|
264
|
+
Get revenue stats for the current authenticated agent
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Revenue statistics for authenticated agent
|
|
268
|
+
|
|
269
|
+
Example:
|
|
270
|
+
```python
|
|
271
|
+
my_stats = await client.royalty.get_my_revenue()
|
|
272
|
+
print(f"My earnings: ${my_stats.total_earned_usd}")
|
|
273
|
+
```
|
|
274
|
+
"""
|
|
275
|
+
return await self.get_revenue_stats(self.client.did)
|
|
276
|
+
|
|
277
|
+
async def get_my_pending_payouts(self) -> Dict:
|
|
278
|
+
"""
|
|
279
|
+
Get pending payouts for the current authenticated agent
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Pending payouts for authenticated agent
|
|
283
|
+
|
|
284
|
+
Example:
|
|
285
|
+
```python
|
|
286
|
+
result = await client.royalty.get_my_pending_payouts()
|
|
287
|
+
print(f"Pending: ${result['total_pending_amount']}")
|
|
288
|
+
```
|
|
289
|
+
"""
|
|
290
|
+
return await self.get_pending_payouts(self.client.did)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session Service - x402 v2 wallet session management
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, List
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class WalletSession:
|
|
11
|
+
"""Wallet session for pre-authorized payments"""
|
|
12
|
+
session_id: str
|
|
13
|
+
wallet_address: str
|
|
14
|
+
chain: str
|
|
15
|
+
network: str
|
|
16
|
+
created_at: str
|
|
17
|
+
expires_at: str
|
|
18
|
+
max_amount: str
|
|
19
|
+
spent_amount: str
|
|
20
|
+
remaining_amount: str
|
|
21
|
+
scope: List[str]
|
|
22
|
+
active: bool
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class SessionValidation:
|
|
27
|
+
"""Session validation result"""
|
|
28
|
+
valid: bool
|
|
29
|
+
session_id: str
|
|
30
|
+
has_budget: bool
|
|
31
|
+
remaining_amount: str
|
|
32
|
+
reason: Optional[str] = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class CreateSessionOptions:
|
|
37
|
+
"""Options for creating a session"""
|
|
38
|
+
wallet_address: str
|
|
39
|
+
chain: str
|
|
40
|
+
network: str
|
|
41
|
+
duration_seconds: int = 3600 # 1 hour default
|
|
42
|
+
max_amount: Optional[str] = None
|
|
43
|
+
scope: Optional[List[str]] = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SessionService:
|
|
47
|
+
"""
|
|
48
|
+
Session service for x402 v2 wallet session management
|
|
49
|
+
|
|
50
|
+
Sessions allow pre-authorized payments within a budget and time limit,
|
|
51
|
+
reducing the need for per-request wallet signatures.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, client):
|
|
55
|
+
self.client = client
|
|
56
|
+
|
|
57
|
+
async def create(self, options: CreateSessionOptions) -> WalletSession:
|
|
58
|
+
"""
|
|
59
|
+
Create a new wallet session for pre-authorized payments
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
options: Session creation options
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Created wallet session
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
```python
|
|
69
|
+
from xache.services.sessions import CreateSessionOptions
|
|
70
|
+
|
|
71
|
+
session = await client.sessions.create(CreateSessionOptions(
|
|
72
|
+
wallet_address="0x1234...",
|
|
73
|
+
chain="evm",
|
|
74
|
+
network="base-sepolia",
|
|
75
|
+
duration_seconds=3600, # 1 hour
|
|
76
|
+
max_amount="10000000", # 10 USDC
|
|
77
|
+
scope=["memory:store", "memory:retrieve"]
|
|
78
|
+
))
|
|
79
|
+
|
|
80
|
+
print(f"Session ID: {session.session_id}")
|
|
81
|
+
print(f"Expires: {session.expires_at}")
|
|
82
|
+
```
|
|
83
|
+
"""
|
|
84
|
+
body = {
|
|
85
|
+
"walletAddress": options.wallet_address,
|
|
86
|
+
"chain": options.chain,
|
|
87
|
+
"network": options.network,
|
|
88
|
+
"durationSeconds": options.duration_seconds,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if options.max_amount:
|
|
92
|
+
body["maxAmount"] = options.max_amount
|
|
93
|
+
if options.scope:
|
|
94
|
+
body["scope"] = options.scope
|
|
95
|
+
|
|
96
|
+
response = await self.client.request("POST", "/v1/sessions", body)
|
|
97
|
+
|
|
98
|
+
if not response.success or not response.data:
|
|
99
|
+
raise Exception(
|
|
100
|
+
response.error.get("message", "Failed to create session")
|
|
101
|
+
if response.error
|
|
102
|
+
else "Failed to create session"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return self._parse_session(response.data.get("session", response.data))
|
|
106
|
+
|
|
107
|
+
async def get(self, session_id: str) -> Optional[WalletSession]:
|
|
108
|
+
"""
|
|
109
|
+
Get session by ID
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
session_id: Session identifier
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Wallet session or None if not found
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
```python
|
|
119
|
+
session = await client.sessions.get("sess_abc123")
|
|
120
|
+
if session:
|
|
121
|
+
print(f"Remaining: {session.remaining_amount}")
|
|
122
|
+
```
|
|
123
|
+
"""
|
|
124
|
+
response = await self.client.request("GET", f"/v1/sessions/{session_id}")
|
|
125
|
+
|
|
126
|
+
if not response.success:
|
|
127
|
+
if response.error and response.error.get("code") == "NOT_FOUND":
|
|
128
|
+
return None
|
|
129
|
+
raise Exception(
|
|
130
|
+
response.error.get("message", "Failed to get session")
|
|
131
|
+
if response.error
|
|
132
|
+
else "Failed to get session"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if not response.data:
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
return self._parse_session(response.data.get("session", response.data))
|
|
139
|
+
|
|
140
|
+
async def validate(
|
|
141
|
+
self, session_id: str, amount: str, scope: Optional[str] = None
|
|
142
|
+
) -> SessionValidation:
|
|
143
|
+
"""
|
|
144
|
+
Validate session for a specific operation
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
session_id: Session identifier
|
|
148
|
+
amount: Amount to validate (in smallest unit, e.g., microcents)
|
|
149
|
+
scope: Operation scope to validate (e.g., "memory:store")
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Validation result
|
|
153
|
+
|
|
154
|
+
Example:
|
|
155
|
+
```python
|
|
156
|
+
validation = await client.sessions.validate(
|
|
157
|
+
"sess_abc123",
|
|
158
|
+
amount="1000",
|
|
159
|
+
scope="memory:store"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
if validation.valid and validation.has_budget:
|
|
163
|
+
print("Session is valid for this operation")
|
|
164
|
+
else:
|
|
165
|
+
print(f"Invalid: {validation.reason}")
|
|
166
|
+
```
|
|
167
|
+
"""
|
|
168
|
+
body = {"amount": amount}
|
|
169
|
+
if scope:
|
|
170
|
+
body["scope"] = scope
|
|
171
|
+
|
|
172
|
+
response = await self.client.request(
|
|
173
|
+
"POST", f"/v1/sessions/{session_id}/validate", body
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if not response.success or not response.data:
|
|
177
|
+
raise Exception(
|
|
178
|
+
response.error.get("message", "Failed to validate session")
|
|
179
|
+
if response.error
|
|
180
|
+
else "Failed to validate session"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
data = response.data
|
|
184
|
+
return SessionValidation(
|
|
185
|
+
valid=data.get("valid", False),
|
|
186
|
+
session_id=data.get("sessionId", session_id),
|
|
187
|
+
has_budget=data.get("hasBudget", False),
|
|
188
|
+
remaining_amount=data.get("remainingAmount", "0"),
|
|
189
|
+
reason=data.get("reason"),
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
async def revoke(self, session_id: str) -> bool:
|
|
193
|
+
"""
|
|
194
|
+
Revoke a session
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
session_id: Session identifier
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
True if successfully revoked
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
```python
|
|
204
|
+
revoked = await client.sessions.revoke("sess_abc123")
|
|
205
|
+
if revoked:
|
|
206
|
+
print("Session revoked successfully")
|
|
207
|
+
```
|
|
208
|
+
"""
|
|
209
|
+
response = await self.client.request("DELETE", f"/v1/sessions/{session_id}")
|
|
210
|
+
|
|
211
|
+
if not response.success:
|
|
212
|
+
raise Exception(
|
|
213
|
+
response.error.get("message", "Failed to revoke session")
|
|
214
|
+
if response.error
|
|
215
|
+
else "Failed to revoke session"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
return True
|
|
219
|
+
|
|
220
|
+
async def list_active(self, wallet_address: Optional[str] = None) -> List[WalletSession]:
|
|
221
|
+
"""
|
|
222
|
+
List active sessions
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
wallet_address: Optional wallet address to filter by
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
List of active sessions
|
|
229
|
+
|
|
230
|
+
Example:
|
|
231
|
+
```python
|
|
232
|
+
sessions = await client.sessions.list_active()
|
|
233
|
+
print(f"Active sessions: {len(sessions)}")
|
|
234
|
+
for s in sessions:
|
|
235
|
+
print(f" {s.session_id}: expires {s.expires_at}")
|
|
236
|
+
```
|
|
237
|
+
"""
|
|
238
|
+
endpoint = "/v1/sessions"
|
|
239
|
+
if wallet_address:
|
|
240
|
+
endpoint += f"?walletAddress={wallet_address}"
|
|
241
|
+
|
|
242
|
+
response = await self.client.request("GET", endpoint)
|
|
243
|
+
|
|
244
|
+
if not response.success or not response.data:
|
|
245
|
+
raise Exception(
|
|
246
|
+
response.error.get("message", "Failed to list sessions")
|
|
247
|
+
if response.error
|
|
248
|
+
else "Failed to list sessions"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
sessions_data = response.data.get("sessions", [])
|
|
252
|
+
return [self._parse_session(s) for s in sessions_data]
|
|
253
|
+
|
|
254
|
+
def _parse_session(self, data: dict) -> WalletSession:
|
|
255
|
+
"""Parse session data into WalletSession object"""
|
|
256
|
+
return WalletSession(
|
|
257
|
+
session_id=data["sessionId"],
|
|
258
|
+
wallet_address=data["walletAddress"],
|
|
259
|
+
chain=data["chain"],
|
|
260
|
+
network=data["network"],
|
|
261
|
+
created_at=data["createdAt"],
|
|
262
|
+
expires_at=data["expiresAt"],
|
|
263
|
+
max_amount=data.get("maxAmount", "0"),
|
|
264
|
+
spent_amount=data.get("spentAmount", "0"),
|
|
265
|
+
remaining_amount=data.get("remainingAmount", "0"),
|
|
266
|
+
scope=data.get("scope", []),
|
|
267
|
+
active=data.get("active", True),
|
|
268
|
+
)
|