dominusnode 1.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.
- dominusnode/__init__.py +151 -0
- dominusnode/admin.py +252 -0
- dominusnode/agent_wallet.py +240 -0
- dominusnode/auth.py +271 -0
- dominusnode/client.py +457 -0
- dominusnode/constants.py +18 -0
- dominusnode/errors.py +91 -0
- dominusnode/http_client.py +435 -0
- dominusnode/keys.py +88 -0
- dominusnode/plans.py +93 -0
- dominusnode/proxy.py +248 -0
- dominusnode/py.typed +0 -0
- dominusnode/sessions.py +55 -0
- dominusnode/slots.py +60 -0
- dominusnode/teams.py +339 -0
- dominusnode/token_manager.py +235 -0
- dominusnode/types.py +521 -0
- dominusnode/usage.py +230 -0
- dominusnode/wallet.py +189 -0
- dominusnode/wallet_auth.py +241 -0
- dominusnode/x402.py +88 -0
- dominusnode-1.0.0.dist-info/LICENSE +21 -0
- dominusnode-1.0.0.dist-info/METADATA +13 -0
- dominusnode-1.0.0.dist-info/RECORD +26 -0
- dominusnode-1.0.0.dist-info/WHEEL +5 -0
- dominusnode-1.0.0.dist-info/top_level.txt +1 -0
dominusnode/__init__.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Dominus Node Python SDK.
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the Dominus Node rotating proxy-as-a-service platform.
|
|
4
|
+
|
|
5
|
+
Provides both synchronous and asynchronous clients::
|
|
6
|
+
|
|
7
|
+
# Sync
|
|
8
|
+
from dominusnode import DominusNodeClient
|
|
9
|
+
|
|
10
|
+
with DominusNodeClient(base_url="http://localhost:3000") as client:
|
|
11
|
+
client.connect_with_credentials("user@example.com", "s3cret!Pass")
|
|
12
|
+
print(client.wallet.get_balance())
|
|
13
|
+
|
|
14
|
+
# Async
|
|
15
|
+
from dominusnode import AsyncDominusNodeClient
|
|
16
|
+
|
|
17
|
+
async with AsyncDominusNodeClient(base_url="http://localhost:3000") as client:
|
|
18
|
+
await client.connect_with_credentials("user@example.com", "s3cret!Pass")
|
|
19
|
+
print(await client.wallet.get_balance())
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from .client import AsyncDominusNodeClient, DominusNodeClient
|
|
23
|
+
from .constants import SDK_VERSION
|
|
24
|
+
from .errors import (
|
|
25
|
+
AuthenticationError,
|
|
26
|
+
AuthorizationError,
|
|
27
|
+
ConflictError,
|
|
28
|
+
DominusNodeError,
|
|
29
|
+
InsufficientBalanceError,
|
|
30
|
+
NetworkError,
|
|
31
|
+
NotFoundError,
|
|
32
|
+
ProxyError,
|
|
33
|
+
RateLimitError,
|
|
34
|
+
ServerError,
|
|
35
|
+
ValidationError,
|
|
36
|
+
)
|
|
37
|
+
from .x402 import X402Info, X402Facilitator, X402Pricing
|
|
38
|
+
from .wallet_auth import WalletChallenge, WalletVerifyResult, WalletLinkResult
|
|
39
|
+
from .types import (
|
|
40
|
+
ActiveSession,
|
|
41
|
+
AdminUser,
|
|
42
|
+
AdminUserDetail,
|
|
43
|
+
AdminUsersResponse,
|
|
44
|
+
ApiKey,
|
|
45
|
+
CreatedApiKey,
|
|
46
|
+
CryptoInvoice,
|
|
47
|
+
DailyRevenue,
|
|
48
|
+
DailyUsage,
|
|
49
|
+
GeoTargeting,
|
|
50
|
+
LoginResult,
|
|
51
|
+
MfaSetup,
|
|
52
|
+
MfaStatus,
|
|
53
|
+
Pagination,
|
|
54
|
+
Plan,
|
|
55
|
+
ProxyConfig,
|
|
56
|
+
ProxyEndpointConfig,
|
|
57
|
+
ProxyEndpoints,
|
|
58
|
+
ProxyHealth,
|
|
59
|
+
ProxyStatus,
|
|
60
|
+
ProxyUrlOptions,
|
|
61
|
+
ProviderStat,
|
|
62
|
+
RevenueStats,
|
|
63
|
+
StripeCheckout,
|
|
64
|
+
SlotsInfo,
|
|
65
|
+
SystemStats,
|
|
66
|
+
TopHost,
|
|
67
|
+
UsagePagination,
|
|
68
|
+
UsagePeriod,
|
|
69
|
+
UsageRecord,
|
|
70
|
+
UsageResponse,
|
|
71
|
+
UsageSummary,
|
|
72
|
+
User,
|
|
73
|
+
UserPlanInfo,
|
|
74
|
+
UserPlanUsage,
|
|
75
|
+
Wallet,
|
|
76
|
+
WaitlistCount,
|
|
77
|
+
WaitlistJoinResult,
|
|
78
|
+
WalletForecast,
|
|
79
|
+
WalletTransaction,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
__version__ = SDK_VERSION
|
|
83
|
+
|
|
84
|
+
__all__ = [
|
|
85
|
+
# Clients
|
|
86
|
+
"DominusNodeClient",
|
|
87
|
+
"AsyncDominusNodeClient",
|
|
88
|
+
# Errors
|
|
89
|
+
"DominusNodeError",
|
|
90
|
+
"AuthenticationError",
|
|
91
|
+
"AuthorizationError",
|
|
92
|
+
"RateLimitError",
|
|
93
|
+
"InsufficientBalanceError",
|
|
94
|
+
"ValidationError",
|
|
95
|
+
"NotFoundError",
|
|
96
|
+
"ConflictError",
|
|
97
|
+
"ServerError",
|
|
98
|
+
"NetworkError",
|
|
99
|
+
"ProxyError",
|
|
100
|
+
# Types
|
|
101
|
+
"User",
|
|
102
|
+
"LoginResult",
|
|
103
|
+
"MfaStatus",
|
|
104
|
+
"MfaSetup",
|
|
105
|
+
"ApiKey",
|
|
106
|
+
"CreatedApiKey",
|
|
107
|
+
"Wallet",
|
|
108
|
+
"WalletTransaction",
|
|
109
|
+
"WalletForecast",
|
|
110
|
+
"StripeCheckout",
|
|
111
|
+
"CryptoInvoice",
|
|
112
|
+
"UsageSummary",
|
|
113
|
+
"UsageRecord",
|
|
114
|
+
"UsagePagination",
|
|
115
|
+
"UsagePeriod",
|
|
116
|
+
"UsageResponse",
|
|
117
|
+
"DailyUsage",
|
|
118
|
+
"TopHost",
|
|
119
|
+
"Plan",
|
|
120
|
+
"UserPlanUsage",
|
|
121
|
+
"UserPlanInfo",
|
|
122
|
+
"ActiveSession",
|
|
123
|
+
"ProxyUrlOptions",
|
|
124
|
+
"ProxyHealth",
|
|
125
|
+
"ProviderStat",
|
|
126
|
+
"ProxyEndpoints",
|
|
127
|
+
"ProxyStatus",
|
|
128
|
+
"GeoTargeting",
|
|
129
|
+
"ProxyEndpointConfig",
|
|
130
|
+
"ProxyConfig",
|
|
131
|
+
"AdminUser",
|
|
132
|
+
"AdminUserDetail",
|
|
133
|
+
"Pagination",
|
|
134
|
+
"AdminUsersResponse",
|
|
135
|
+
"RevenueStats",
|
|
136
|
+
"DailyRevenue",
|
|
137
|
+
"SlotsInfo",
|
|
138
|
+
"WaitlistJoinResult",
|
|
139
|
+
"WaitlistCount",
|
|
140
|
+
"SystemStats",
|
|
141
|
+
# x402
|
|
142
|
+
"X402Info",
|
|
143
|
+
"X402Facilitator",
|
|
144
|
+
"X402Pricing",
|
|
145
|
+
# Wallet Auth
|
|
146
|
+
"WalletChallenge",
|
|
147
|
+
"WalletVerifyResult",
|
|
148
|
+
"WalletLinkResult",
|
|
149
|
+
# Constants
|
|
150
|
+
"SDK_VERSION",
|
|
151
|
+
]
|
dominusnode/admin.py
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Admin resource -- user management, revenue analytics, and system stats.
|
|
2
|
+
|
|
3
|
+
All admin endpoints require the authenticated user to have admin privileges.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
from urllib.parse import quote
|
|
10
|
+
|
|
11
|
+
from .http_client import AsyncHttpClient, SyncHttpClient
|
|
12
|
+
from .types import (
|
|
13
|
+
AdminUser,
|
|
14
|
+
AdminUserDetail,
|
|
15
|
+
AdminUsersResponse,
|
|
16
|
+
DailyRevenue,
|
|
17
|
+
Pagination,
|
|
18
|
+
RevenueStats,
|
|
19
|
+
SystemStats,
|
|
20
|
+
UsagePeriod,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _parse_admin_user(u: dict) -> AdminUser:
|
|
25
|
+
return AdminUser(
|
|
26
|
+
id=u["id"],
|
|
27
|
+
email=u["email"],
|
|
28
|
+
status=u["status"],
|
|
29
|
+
plan_id=u.get("planId", "payg"),
|
|
30
|
+
balance_cents=u.get("balanceCents", 0),
|
|
31
|
+
created_at=str(u.get("createdAt", "")),
|
|
32
|
+
is_admin=u.get("isAdmin", False),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _parse_admin_user_detail(u: dict) -> AdminUserDetail:
|
|
37
|
+
return AdminUserDetail(
|
|
38
|
+
id=u["id"],
|
|
39
|
+
email=u["email"],
|
|
40
|
+
status=u["status"],
|
|
41
|
+
plan_id=u.get("planId", "payg"),
|
|
42
|
+
balance_cents=u.get("balanceCents", 0),
|
|
43
|
+
created_at=str(u.get("createdAt", "")),
|
|
44
|
+
is_admin=u.get("isAdmin", False),
|
|
45
|
+
api_key_count=u.get("apiKeyCount", 0),
|
|
46
|
+
total_usage_bytes=u.get("totalUsageBytes", 0),
|
|
47
|
+
total_spent_cents=u.get("totalSpentCents", 0),
|
|
48
|
+
last_active=str(u["lastActive"]) if u.get("lastActive") else None,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _build_date_params(since: Optional[str], until: Optional[str]) -> dict:
|
|
53
|
+
params: dict = {}
|
|
54
|
+
if since:
|
|
55
|
+
params["since"] = since
|
|
56
|
+
if until:
|
|
57
|
+
params["until"] = until
|
|
58
|
+
return params
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
62
|
+
# Sync
|
|
63
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AdminResource:
|
|
67
|
+
"""Synchronous admin operations."""
|
|
68
|
+
|
|
69
|
+
def __init__(self, http: SyncHttpClient) -> None:
|
|
70
|
+
self._http = http
|
|
71
|
+
|
|
72
|
+
# ── User Management ──────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
def list_users(self, *, page: int = 1, limit: int = 20) -> AdminUsersResponse:
|
|
75
|
+
"""List all users (paginated)."""
|
|
76
|
+
data = self._http.get("/api/admin/users", params={"page": page, "limit": limit})
|
|
77
|
+
users = [_parse_admin_user(u) for u in data.get("users", [])]
|
|
78
|
+
p = data.get("pagination", {})
|
|
79
|
+
pagination = Pagination(
|
|
80
|
+
page=p.get("page", page),
|
|
81
|
+
limit=p.get("limit", limit),
|
|
82
|
+
total=p.get("total", 0),
|
|
83
|
+
total_pages=p.get("totalPages", 0),
|
|
84
|
+
)
|
|
85
|
+
return AdminUsersResponse(users=users, pagination=pagination)
|
|
86
|
+
|
|
87
|
+
def get_user(self, user_id: str) -> AdminUserDetail:
|
|
88
|
+
"""Get detailed information about a specific user."""
|
|
89
|
+
data = self._http.get(f"/api/admin/users/{quote(user_id, safe='')}")
|
|
90
|
+
return _parse_admin_user_detail(data["user"])
|
|
91
|
+
|
|
92
|
+
def suspend_user(self, user_id: str) -> None:
|
|
93
|
+
"""Suspend a user account."""
|
|
94
|
+
self._http.put(f"/api/admin/users/{quote(user_id, safe='')}/suspend")
|
|
95
|
+
|
|
96
|
+
def activate_user(self, user_id: str) -> None:
|
|
97
|
+
"""Reactivate a suspended user account."""
|
|
98
|
+
self._http.put(f"/api/admin/users/{quote(user_id, safe='')}/activate")
|
|
99
|
+
|
|
100
|
+
def delete_user(self, user_id: str) -> None:
|
|
101
|
+
"""Soft-delete a user account."""
|
|
102
|
+
self._http.delete(f"/api/admin/users/{quote(user_id, safe='')}")
|
|
103
|
+
|
|
104
|
+
# ── Revenue Analytics ────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
def get_revenue(
|
|
107
|
+
self,
|
|
108
|
+
*,
|
|
109
|
+
since: Optional[str] = None,
|
|
110
|
+
until: Optional[str] = None,
|
|
111
|
+
) -> RevenueStats:
|
|
112
|
+
"""Get revenue statistics for the given period."""
|
|
113
|
+
params = _build_date_params(since, until)
|
|
114
|
+
data = self._http.get("/api/admin/revenue", params=params)
|
|
115
|
+
period_data = data.get("period", {})
|
|
116
|
+
return RevenueStats(
|
|
117
|
+
total_revenue_cents=data.get("totalRevenueCents", 0),
|
|
118
|
+
total_revenue_usd=data.get("totalRevenueUsd", 0),
|
|
119
|
+
total_transactions=data.get("totalTransactions", 0),
|
|
120
|
+
avg_transaction_cents=data.get("avgTransactionCents", 0),
|
|
121
|
+
avg_transaction_usd=data.get("avgTransactionUsd", 0),
|
|
122
|
+
top_up_count=data.get("topUpCount", 0),
|
|
123
|
+
unique_paying_users=data.get("uniquePayingUsers", 0),
|
|
124
|
+
period=UsagePeriod(
|
|
125
|
+
since=period_data.get("since", ""),
|
|
126
|
+
until=period_data.get("until", ""),
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def get_daily_revenue(
|
|
131
|
+
self,
|
|
132
|
+
*,
|
|
133
|
+
since: Optional[str] = None,
|
|
134
|
+
until: Optional[str] = None,
|
|
135
|
+
) -> List[DailyRevenue]:
|
|
136
|
+
"""Get daily revenue breakdown."""
|
|
137
|
+
params = _build_date_params(since, until)
|
|
138
|
+
data = self._http.get("/api/admin/revenue/daily", params=params)
|
|
139
|
+
return [
|
|
140
|
+
DailyRevenue(
|
|
141
|
+
date=d["date"],
|
|
142
|
+
revenue_cents=d.get("revenueCents", 0),
|
|
143
|
+
revenue_usd=d.get("revenueUsd", 0),
|
|
144
|
+
transaction_count=d.get("transactionCount", 0),
|
|
145
|
+
)
|
|
146
|
+
for d in data.get("days", [])
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
# ── System Stats ─────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
def get_stats(self) -> SystemStats:
|
|
152
|
+
"""Get system-wide statistics."""
|
|
153
|
+
data = self._http.get("/api/admin/stats")
|
|
154
|
+
return SystemStats(
|
|
155
|
+
total_users=data.get("totalUsers", 0),
|
|
156
|
+
active_users=data.get("activeUsers", 0),
|
|
157
|
+
suspended_users=data.get("suspendedUsers", 0),
|
|
158
|
+
total_api_keys=data.get("totalApiKeys", 0),
|
|
159
|
+
active_api_keys=data.get("activeApiKeys", 0),
|
|
160
|
+
total_sessions=data.get("totalSessions", 0),
|
|
161
|
+
active_sessions=data.get("activeSessions", 0),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
166
|
+
# Async
|
|
167
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class AsyncAdminResource:
|
|
171
|
+
"""Asynchronous admin operations."""
|
|
172
|
+
|
|
173
|
+
def __init__(self, http: AsyncHttpClient) -> None:
|
|
174
|
+
self._http = http
|
|
175
|
+
|
|
176
|
+
async def list_users(self, *, page: int = 1, limit: int = 20) -> AdminUsersResponse:
|
|
177
|
+
data = await self._http.get("/api/admin/users", params={"page": page, "limit": limit})
|
|
178
|
+
users = [_parse_admin_user(u) for u in data.get("users", [])]
|
|
179
|
+
p = data.get("pagination", {})
|
|
180
|
+
pagination = Pagination(
|
|
181
|
+
page=p.get("page", page),
|
|
182
|
+
limit=p.get("limit", limit),
|
|
183
|
+
total=p.get("total", 0),
|
|
184
|
+
total_pages=p.get("totalPages", 0),
|
|
185
|
+
)
|
|
186
|
+
return AdminUsersResponse(users=users, pagination=pagination)
|
|
187
|
+
|
|
188
|
+
async def get_user(self, user_id: str) -> AdminUserDetail:
|
|
189
|
+
data = await self._http.get(f"/api/admin/users/{quote(user_id, safe='')}")
|
|
190
|
+
return _parse_admin_user_detail(data["user"])
|
|
191
|
+
|
|
192
|
+
async def suspend_user(self, user_id: str) -> None:
|
|
193
|
+
await self._http.put(f"/api/admin/users/{quote(user_id, safe='')}/suspend")
|
|
194
|
+
|
|
195
|
+
async def activate_user(self, user_id: str) -> None:
|
|
196
|
+
await self._http.put(f"/api/admin/users/{quote(user_id, safe='')}/activate")
|
|
197
|
+
|
|
198
|
+
async def delete_user(self, user_id: str) -> None:
|
|
199
|
+
await self._http.delete(f"/api/admin/users/{quote(user_id, safe='')}")
|
|
200
|
+
|
|
201
|
+
async def get_revenue(
|
|
202
|
+
self,
|
|
203
|
+
*,
|
|
204
|
+
since: Optional[str] = None,
|
|
205
|
+
until: Optional[str] = None,
|
|
206
|
+
) -> RevenueStats:
|
|
207
|
+
params = _build_date_params(since, until)
|
|
208
|
+
data = await self._http.get("/api/admin/revenue", params=params)
|
|
209
|
+
period_data = data.get("period", {})
|
|
210
|
+
return RevenueStats(
|
|
211
|
+
total_revenue_cents=data.get("totalRevenueCents", 0),
|
|
212
|
+
total_revenue_usd=data.get("totalRevenueUsd", 0),
|
|
213
|
+
total_transactions=data.get("totalTransactions", 0),
|
|
214
|
+
avg_transaction_cents=data.get("avgTransactionCents", 0),
|
|
215
|
+
avg_transaction_usd=data.get("avgTransactionUsd", 0),
|
|
216
|
+
top_up_count=data.get("topUpCount", 0),
|
|
217
|
+
unique_paying_users=data.get("uniquePayingUsers", 0),
|
|
218
|
+
period=UsagePeriod(
|
|
219
|
+
since=period_data.get("since", ""),
|
|
220
|
+
until=period_data.get("until", ""),
|
|
221
|
+
),
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
async def get_daily_revenue(
|
|
225
|
+
self,
|
|
226
|
+
*,
|
|
227
|
+
since: Optional[str] = None,
|
|
228
|
+
until: Optional[str] = None,
|
|
229
|
+
) -> List[DailyRevenue]:
|
|
230
|
+
params = _build_date_params(since, until)
|
|
231
|
+
data = await self._http.get("/api/admin/revenue/daily", params=params)
|
|
232
|
+
return [
|
|
233
|
+
DailyRevenue(
|
|
234
|
+
date=d["date"],
|
|
235
|
+
revenue_cents=d.get("revenueCents", 0),
|
|
236
|
+
revenue_usd=d.get("revenueUsd", 0),
|
|
237
|
+
transaction_count=d.get("transactionCount", 0),
|
|
238
|
+
)
|
|
239
|
+
for d in data.get("days", [])
|
|
240
|
+
]
|
|
241
|
+
|
|
242
|
+
async def get_stats(self) -> SystemStats:
|
|
243
|
+
data = await self._http.get("/api/admin/stats")
|
|
244
|
+
return SystemStats(
|
|
245
|
+
total_users=data.get("totalUsers", 0),
|
|
246
|
+
active_users=data.get("activeUsers", 0),
|
|
247
|
+
suspended_users=data.get("suspendedUsers", 0),
|
|
248
|
+
total_api_keys=data.get("totalApiKeys", 0),
|
|
249
|
+
active_api_keys=data.get("activeApiKeys", 0),
|
|
250
|
+
total_sessions=data.get("totalSessions", 0),
|
|
251
|
+
active_sessions=data.get("activeSessions", 0),
|
|
252
|
+
)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""Agentic wallet resource -- server-side custodial sub-wallets with spending limits."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
from urllib.parse import quote
|
|
7
|
+
|
|
8
|
+
from .http_client import AsyncHttpClient, SyncHttpClient
|
|
9
|
+
from .types import AgenticWallet, AgenticWalletTransaction
|
|
10
|
+
from .wallet import _validate_amount_cents
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _parse_agentic_wallet(data: dict) -> AgenticWallet:
|
|
14
|
+
"""Parse an agentic wallet response dict into an AgenticWallet dataclass."""
|
|
15
|
+
return AgenticWallet(
|
|
16
|
+
id=data["id"],
|
|
17
|
+
label=data["label"],
|
|
18
|
+
balance_cents=data["balanceCents"],
|
|
19
|
+
spending_limit_cents=data["spendingLimitCents"],
|
|
20
|
+
status=data["status"],
|
|
21
|
+
created_at=data.get("createdAt", ""),
|
|
22
|
+
daily_limit_cents=data.get("dailyLimitCents"),
|
|
23
|
+
allowed_domains=data.get("allowedDomains"),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AgenticWalletResource:
|
|
28
|
+
"""Synchronous agentic wallet operations."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, http: SyncHttpClient) -> None:
|
|
31
|
+
self._http = http
|
|
32
|
+
|
|
33
|
+
def create(
|
|
34
|
+
self,
|
|
35
|
+
label: str,
|
|
36
|
+
spending_limit_cents: int = 10000,
|
|
37
|
+
daily_limit_cents: Optional[int] = None,
|
|
38
|
+
allowed_domains: Optional[List[str]] = None,
|
|
39
|
+
) -> AgenticWallet:
|
|
40
|
+
"""Create a new agentic wallet."""
|
|
41
|
+
_validate_amount_cents(spending_limit_cents, "spending_limit_cents")
|
|
42
|
+
body: dict = {
|
|
43
|
+
"label": label,
|
|
44
|
+
"spendingLimitCents": spending_limit_cents,
|
|
45
|
+
}
|
|
46
|
+
if daily_limit_cents is not None:
|
|
47
|
+
_validate_amount_cents(daily_limit_cents, "daily_limit_cents")
|
|
48
|
+
body["dailyLimitCents"] = daily_limit_cents
|
|
49
|
+
if allowed_domains is not None:
|
|
50
|
+
body["allowedDomains"] = allowed_domains
|
|
51
|
+
data = self._http.post("/api/agent-wallet", json=body)
|
|
52
|
+
return _parse_agentic_wallet(data)
|
|
53
|
+
|
|
54
|
+
def list(self) -> List[AgenticWallet]:
|
|
55
|
+
"""List all agentic wallets."""
|
|
56
|
+
data = self._http.get("/api/agent-wallet")
|
|
57
|
+
return [_parse_agentic_wallet(w) for w in data["wallets"]]
|
|
58
|
+
|
|
59
|
+
def get(self, wallet_id: str) -> AgenticWallet:
|
|
60
|
+
"""Get a single agentic wallet."""
|
|
61
|
+
data = self._http.get(f"/api/agent-wallet/{quote(wallet_id, safe='')}")
|
|
62
|
+
return _parse_agentic_wallet(data)
|
|
63
|
+
|
|
64
|
+
def fund(self, wallet_id: str, amount_cents: int) -> AgenticWalletTransaction:
|
|
65
|
+
"""Fund an agentic wallet from the main wallet."""
|
|
66
|
+
_validate_amount_cents(amount_cents, "amount_cents")
|
|
67
|
+
data = self._http.post(f"/api/agent-wallet/{quote(wallet_id, safe='')}/fund", json={
|
|
68
|
+
"amountCents": amount_cents,
|
|
69
|
+
})
|
|
70
|
+
tx = data["transaction"]
|
|
71
|
+
return AgenticWalletTransaction(
|
|
72
|
+
id=tx["id"],
|
|
73
|
+
wallet_id=tx["walletId"],
|
|
74
|
+
type=tx["type"],
|
|
75
|
+
amount_cents=tx["amountCents"],
|
|
76
|
+
description=tx["description"],
|
|
77
|
+
session_id=tx.get("sessionId"),
|
|
78
|
+
created_at=tx["createdAt"],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def transactions(
|
|
82
|
+
self, wallet_id: str, limit: int = 50, offset: int = 0
|
|
83
|
+
) -> List[AgenticWalletTransaction]:
|
|
84
|
+
"""Get transaction history for an agentic wallet."""
|
|
85
|
+
data = self._http.get(
|
|
86
|
+
f"/api/agent-wallet/{quote(wallet_id, safe='')}/transactions?limit={limit}&offset={offset}"
|
|
87
|
+
)
|
|
88
|
+
return [
|
|
89
|
+
AgenticWalletTransaction(
|
|
90
|
+
id=tx["id"],
|
|
91
|
+
wallet_id=tx["walletId"],
|
|
92
|
+
type=tx["type"],
|
|
93
|
+
amount_cents=tx["amountCents"],
|
|
94
|
+
description=tx["description"],
|
|
95
|
+
session_id=tx.get("sessionId"),
|
|
96
|
+
created_at=tx["createdAt"],
|
|
97
|
+
)
|
|
98
|
+
for tx in data["transactions"]
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
def freeze(self, wallet_id: str) -> AgenticWallet:
|
|
102
|
+
"""Freeze an agentic wallet."""
|
|
103
|
+
data = self._http.post(f"/api/agent-wallet/{quote(wallet_id, safe='')}/freeze", json={})
|
|
104
|
+
return _parse_agentic_wallet(data)
|
|
105
|
+
|
|
106
|
+
def unfreeze(self, wallet_id: str) -> AgenticWallet:
|
|
107
|
+
"""Unfreeze an agentic wallet."""
|
|
108
|
+
data = self._http.post(f"/api/agent-wallet/{quote(wallet_id, safe='')}/unfreeze", json={})
|
|
109
|
+
return _parse_agentic_wallet(data)
|
|
110
|
+
|
|
111
|
+
def delete(self, wallet_id: str) -> int:
|
|
112
|
+
"""Delete an agentic wallet and refund remaining balance. Returns refunded cents."""
|
|
113
|
+
data = self._http.delete(f"/api/agent-wallet/{quote(wallet_id, safe='')}")
|
|
114
|
+
return data["refundedCents"]
|
|
115
|
+
|
|
116
|
+
def update_wallet_policy(
|
|
117
|
+
self,
|
|
118
|
+
wallet_id: str,
|
|
119
|
+
daily_limit_cents: Optional[int] = None,
|
|
120
|
+
allowed_domains: Optional[List[str]] = None,
|
|
121
|
+
) -> AgenticWallet:
|
|
122
|
+
"""Update the policy on an agentic wallet."""
|
|
123
|
+
body: dict = {}
|
|
124
|
+
if daily_limit_cents is not None:
|
|
125
|
+
_validate_amount_cents(daily_limit_cents, "daily_limit_cents")
|
|
126
|
+
body["dailyLimitCents"] = daily_limit_cents
|
|
127
|
+
if allowed_domains is not None:
|
|
128
|
+
body["allowedDomains"] = allowed_domains
|
|
129
|
+
data = self._http.patch(
|
|
130
|
+
f"/api/agent-wallet/{quote(wallet_id, safe='')}/policy", json=body
|
|
131
|
+
)
|
|
132
|
+
return _parse_agentic_wallet(data)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class AsyncAgenticWalletResource:
|
|
136
|
+
"""Asynchronous agentic wallet operations."""
|
|
137
|
+
|
|
138
|
+
def __init__(self, http: AsyncHttpClient) -> None:
|
|
139
|
+
self._http = http
|
|
140
|
+
|
|
141
|
+
async def create(
|
|
142
|
+
self,
|
|
143
|
+
label: str,
|
|
144
|
+
spending_limit_cents: int = 10000,
|
|
145
|
+
daily_limit_cents: Optional[int] = None,
|
|
146
|
+
allowed_domains: Optional[List[str]] = None,
|
|
147
|
+
) -> AgenticWallet:
|
|
148
|
+
"""Create a new agentic wallet."""
|
|
149
|
+
_validate_amount_cents(spending_limit_cents, "spending_limit_cents")
|
|
150
|
+
body: dict = {
|
|
151
|
+
"label": label,
|
|
152
|
+
"spendingLimitCents": spending_limit_cents,
|
|
153
|
+
}
|
|
154
|
+
if daily_limit_cents is not None:
|
|
155
|
+
_validate_amount_cents(daily_limit_cents, "daily_limit_cents")
|
|
156
|
+
body["dailyLimitCents"] = daily_limit_cents
|
|
157
|
+
if allowed_domains is not None:
|
|
158
|
+
body["allowedDomains"] = allowed_domains
|
|
159
|
+
data = await self._http.post("/api/agent-wallet", json=body)
|
|
160
|
+
return _parse_agentic_wallet(data)
|
|
161
|
+
|
|
162
|
+
async def list(self) -> List[AgenticWallet]:
|
|
163
|
+
"""List all agentic wallets."""
|
|
164
|
+
data = await self._http.get("/api/agent-wallet")
|
|
165
|
+
return [_parse_agentic_wallet(w) for w in data["wallets"]]
|
|
166
|
+
|
|
167
|
+
async def get(self, wallet_id: str) -> AgenticWallet:
|
|
168
|
+
"""Get a single agentic wallet."""
|
|
169
|
+
data = await self._http.get(f"/api/agent-wallet/{quote(wallet_id, safe='')}")
|
|
170
|
+
return _parse_agentic_wallet(data)
|
|
171
|
+
|
|
172
|
+
async def fund(self, wallet_id: str, amount_cents: int) -> AgenticWalletTransaction:
|
|
173
|
+
"""Fund an agentic wallet from the main wallet."""
|
|
174
|
+
_validate_amount_cents(amount_cents, "amount_cents")
|
|
175
|
+
data = await self._http.post(f"/api/agent-wallet/{quote(wallet_id, safe='')}/fund", json={
|
|
176
|
+
"amountCents": amount_cents,
|
|
177
|
+
})
|
|
178
|
+
tx = data["transaction"]
|
|
179
|
+
return AgenticWalletTransaction(
|
|
180
|
+
id=tx["id"],
|
|
181
|
+
wallet_id=tx["walletId"],
|
|
182
|
+
type=tx["type"],
|
|
183
|
+
amount_cents=tx["amountCents"],
|
|
184
|
+
description=tx["description"],
|
|
185
|
+
session_id=tx.get("sessionId"),
|
|
186
|
+
created_at=tx["createdAt"],
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
async def transactions(
|
|
190
|
+
self, wallet_id: str, limit: int = 50, offset: int = 0
|
|
191
|
+
) -> List[AgenticWalletTransaction]:
|
|
192
|
+
"""Get transaction history for an agentic wallet."""
|
|
193
|
+
data = await self._http.get(
|
|
194
|
+
f"/api/agent-wallet/{quote(wallet_id, safe='')}/transactions?limit={limit}&offset={offset}"
|
|
195
|
+
)
|
|
196
|
+
return [
|
|
197
|
+
AgenticWalletTransaction(
|
|
198
|
+
id=tx["id"],
|
|
199
|
+
wallet_id=tx["walletId"],
|
|
200
|
+
type=tx["type"],
|
|
201
|
+
amount_cents=tx["amountCents"],
|
|
202
|
+
description=tx["description"],
|
|
203
|
+
session_id=tx.get("sessionId"),
|
|
204
|
+
created_at=tx["createdAt"],
|
|
205
|
+
)
|
|
206
|
+
for tx in data["transactions"]
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
async def freeze(self, wallet_id: str) -> AgenticWallet:
|
|
210
|
+
"""Freeze an agentic wallet."""
|
|
211
|
+
data = await self._http.post(f"/api/agent-wallet/{quote(wallet_id, safe='')}/freeze", json={})
|
|
212
|
+
return _parse_agentic_wallet(data)
|
|
213
|
+
|
|
214
|
+
async def unfreeze(self, wallet_id: str) -> AgenticWallet:
|
|
215
|
+
"""Unfreeze an agentic wallet."""
|
|
216
|
+
data = await self._http.post(f"/api/agent-wallet/{quote(wallet_id, safe='')}/unfreeze", json={})
|
|
217
|
+
return _parse_agentic_wallet(data)
|
|
218
|
+
|
|
219
|
+
async def delete(self, wallet_id: str) -> int:
|
|
220
|
+
"""Delete an agentic wallet and refund remaining balance. Returns refunded cents."""
|
|
221
|
+
data = await self._http.delete(f"/api/agent-wallet/{quote(wallet_id, safe='')}")
|
|
222
|
+
return data["refundedCents"]
|
|
223
|
+
|
|
224
|
+
async def update_wallet_policy(
|
|
225
|
+
self,
|
|
226
|
+
wallet_id: str,
|
|
227
|
+
daily_limit_cents: Optional[int] = None,
|
|
228
|
+
allowed_domains: Optional[List[str]] = None,
|
|
229
|
+
) -> AgenticWallet:
|
|
230
|
+
"""Update the policy on an agentic wallet."""
|
|
231
|
+
body: dict = {}
|
|
232
|
+
if daily_limit_cents is not None:
|
|
233
|
+
_validate_amount_cents(daily_limit_cents, "daily_limit_cents")
|
|
234
|
+
body["dailyLimitCents"] = daily_limit_cents
|
|
235
|
+
if allowed_domains is not None:
|
|
236
|
+
body["allowedDomains"] = allowed_domains
|
|
237
|
+
data = await self._http.patch(
|
|
238
|
+
f"/api/agent-wallet/{quote(wallet_id, safe='')}/policy", json=body
|
|
239
|
+
)
|
|
240
|
+
return _parse_agentic_wallet(data)
|