@aura-payments/sdk 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +489 -0
- package/dist/index.d.mts +1292 -0
- package/dist/index.d.ts +1292 -0
- package/dist/index.js +1013 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +985 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1013 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto$1 = require('crypto');
|
|
4
|
+
|
|
5
|
+
// src/errors.ts
|
|
6
|
+
var AuraError = class _AuraError extends Error {
|
|
7
|
+
constructor(message, code, statusCode, details) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.details = details;
|
|
12
|
+
this.name = "AuraError";
|
|
13
|
+
Object.setPrototypeOf(this, _AuraError.prototype);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var AuraAPIError = class _AuraAPIError extends AuraError {
|
|
17
|
+
constructor(message, statusCode, code, details) {
|
|
18
|
+
super(message, code, statusCode, details);
|
|
19
|
+
this.name = "AuraAPIError";
|
|
20
|
+
Object.setPrototypeOf(this, _AuraAPIError.prototype);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var AuraNetworkError = class _AuraNetworkError extends AuraError {
|
|
24
|
+
constructor(message, cause) {
|
|
25
|
+
super(message, "NETWORK_ERROR");
|
|
26
|
+
this.cause = cause;
|
|
27
|
+
this.name = "AuraNetworkError";
|
|
28
|
+
Object.setPrototypeOf(this, _AuraNetworkError.prototype);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var AuraTimeoutError = class _AuraTimeoutError extends AuraError {
|
|
32
|
+
constructor(message = "Request timeout") {
|
|
33
|
+
super(message, "TIMEOUT");
|
|
34
|
+
this.name = "AuraTimeoutError";
|
|
35
|
+
Object.setPrototypeOf(this, _AuraTimeoutError.prototype);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var AuraValidationError = class _AuraValidationError extends AuraError {
|
|
39
|
+
constructor(message, details) {
|
|
40
|
+
super(message, "VALIDATION_ERROR", 400, details);
|
|
41
|
+
this.name = "AuraValidationError";
|
|
42
|
+
Object.setPrototypeOf(this, _AuraValidationError.prototype);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var AuraAuthenticationError = class _AuraAuthenticationError extends AuraError {
|
|
46
|
+
constructor(message = "Authentication failed") {
|
|
47
|
+
super(message, "AUTHENTICATION_ERROR", 401);
|
|
48
|
+
this.name = "AuraAuthenticationError";
|
|
49
|
+
Object.setPrototypeOf(this, _AuraAuthenticationError.prototype);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var AuraNotFoundError = class _AuraNotFoundError extends AuraError {
|
|
53
|
+
constructor(resource) {
|
|
54
|
+
super(`${resource} not found`, "NOT_FOUND", 404);
|
|
55
|
+
this.name = "AuraNotFoundError";
|
|
56
|
+
Object.setPrototypeOf(this, _AuraNotFoundError.prototype);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var AuraRateLimitError = class _AuraRateLimitError extends AuraError {
|
|
60
|
+
constructor(message = "Rate limit exceeded", retryAfter) {
|
|
61
|
+
super(message, "RATE_LIMIT_EXCEEDED", 429);
|
|
62
|
+
this.retryAfter = retryAfter;
|
|
63
|
+
this.name = "AuraRateLimitError";
|
|
64
|
+
Object.setPrototypeOf(this, _AuraRateLimitError.prototype);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var AuraFaucetUnavailableError = class _AuraFaucetUnavailableError extends AuraError {
|
|
68
|
+
constructor(message = "Testnet faucet is not available on this deployment") {
|
|
69
|
+
super(message, "FAUCET_UNAVAILABLE", 501);
|
|
70
|
+
this.name = "AuraFaucetUnavailableError";
|
|
71
|
+
Object.setPrototypeOf(this, _AuraFaucetUnavailableError.prototype);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
function isAuraError(error) {
|
|
75
|
+
return error instanceof AuraError;
|
|
76
|
+
}
|
|
77
|
+
function isAuraFaucetUnavailableError(error) {
|
|
78
|
+
return error instanceof AuraFaucetUnavailableError;
|
|
79
|
+
}
|
|
80
|
+
function isAuraAPIError(error) {
|
|
81
|
+
return error instanceof AuraAPIError;
|
|
82
|
+
}
|
|
83
|
+
function isAuraNetworkError(error) {
|
|
84
|
+
return error instanceof AuraNetworkError;
|
|
85
|
+
}
|
|
86
|
+
function isAuraTimeoutError(error) {
|
|
87
|
+
return error instanceof AuraTimeoutError;
|
|
88
|
+
}
|
|
89
|
+
function isRetryableError(error) {
|
|
90
|
+
if (!(error instanceof AuraError)) return false;
|
|
91
|
+
if (error instanceof AuraNetworkError) return true;
|
|
92
|
+
if (error instanceof AuraTimeoutError) return true;
|
|
93
|
+
if (error instanceof AuraRateLimitError) return true;
|
|
94
|
+
if (error.statusCode && error.statusCode >= 500) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/resources/agents.ts
|
|
101
|
+
var Agents = class {
|
|
102
|
+
constructor(client) {
|
|
103
|
+
this.client = client;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create a new AI agent with a dedicated wallet and (optional) initial
|
|
107
|
+
* policy. The platform provisions a Circle wallet on the requested chain
|
|
108
|
+
* during this call; expect a few seconds of latency for the on-chain part.
|
|
109
|
+
*/
|
|
110
|
+
async create(params, idempotencyKey) {
|
|
111
|
+
return this.client["request"](
|
|
112
|
+
"POST",
|
|
113
|
+
"/v1/agents",
|
|
114
|
+
params,
|
|
115
|
+
{ idempotencyKey }
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* List agents for the authenticated account, paginated.
|
|
120
|
+
*/
|
|
121
|
+
async list(params) {
|
|
122
|
+
const query = new URLSearchParams();
|
|
123
|
+
if (params?.status) query.set("status", params.status);
|
|
124
|
+
if (params?.type) query.set("type", params.type);
|
|
125
|
+
if (params?.limit !== void 0) query.set("limit", String(params.limit));
|
|
126
|
+
if (params?.offset !== void 0) query.set("offset", String(params.offset));
|
|
127
|
+
if (params?.orderBy) query.set("orderBy", params.orderBy);
|
|
128
|
+
if (params?.orderDirection) query.set("orderDirection", params.orderDirection);
|
|
129
|
+
const qs = query.toString();
|
|
130
|
+
return this.client["request"](
|
|
131
|
+
"GET",
|
|
132
|
+
qs ? `/v1/agents?${qs}` : "/v1/agents"
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get a single agent by id.
|
|
137
|
+
*/
|
|
138
|
+
async get(agentId) {
|
|
139
|
+
return this.client["request"]("GET", `/v1/agents/${agentId}`);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Patch an agent's metadata fields. Cannot be used to flip status — use
|
|
143
|
+
* `freeze`/`unfreeze` for that so the mandate-cascade and activity events
|
|
144
|
+
* fire correctly.
|
|
145
|
+
*/
|
|
146
|
+
async update(agentId, params, idempotencyKey) {
|
|
147
|
+
return this.client["request"](
|
|
148
|
+
"PATCH",
|
|
149
|
+
`/v1/agents/${agentId}`,
|
|
150
|
+
params,
|
|
151
|
+
{ idempotencyKey }
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Freeze (kill switch) — sets status to `paused` and cascade-rejects every
|
|
156
|
+
* pending mandate the agent has in flight. Idempotent.
|
|
157
|
+
*/
|
|
158
|
+
async freeze(params, idempotencyKey) {
|
|
159
|
+
const { agentId, ...body } = params;
|
|
160
|
+
return this.client["request"](
|
|
161
|
+
"POST",
|
|
162
|
+
`/v1/agents/${agentId}/freeze`,
|
|
163
|
+
body,
|
|
164
|
+
{ idempotencyKey }
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Unfreeze — sets status back to `active`. Previously cascade-rejected
|
|
169
|
+
* mandates are NOT restored; the agent re-creates them on its next loop.
|
|
170
|
+
*/
|
|
171
|
+
async unfreeze(params, idempotencyKey) {
|
|
172
|
+
const { agentId } = params;
|
|
173
|
+
return this.client["request"](
|
|
174
|
+
"POST",
|
|
175
|
+
`/v1/agents/${agentId}/unfreeze`,
|
|
176
|
+
void 0,
|
|
177
|
+
{ idempotencyKey }
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get the agent's wallet balance (USDC, on-chain at request time).
|
|
182
|
+
*/
|
|
183
|
+
async getBalance(agentId) {
|
|
184
|
+
return this.client["request"](
|
|
185
|
+
"GET",
|
|
186
|
+
`/v1/agents/${agentId}/balance`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Run a hypothetical transaction through the policy engine without
|
|
191
|
+
* executing it. Returns the engine's decision (`auto_approve`, `auto_deny`,
|
|
192
|
+
* or `requires_human_approval`) along with the matched rules. When the
|
|
193
|
+
* decision is `requires_human_approval`, the engine creates a mandate row
|
|
194
|
+
* and returns its id so the caller can watch for the operator's decision.
|
|
195
|
+
*/
|
|
196
|
+
async evaluate(params, idempotencyKey) {
|
|
197
|
+
const { agentId, ...body } = params;
|
|
198
|
+
return this.client["request"](
|
|
199
|
+
"POST",
|
|
200
|
+
`/v1/agents/${agentId}/evaluate`,
|
|
201
|
+
body,
|
|
202
|
+
{ idempotencyKey }
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// src/utils.ts
|
|
208
|
+
function generateIdempotencyKey() {
|
|
209
|
+
const timestamp = Date.now();
|
|
210
|
+
const random = Math.random().toString(36).substring(2, 15);
|
|
211
|
+
return `${timestamp}-${random}`;
|
|
212
|
+
}
|
|
213
|
+
function sleep(ms) {
|
|
214
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
215
|
+
}
|
|
216
|
+
function calculateBackoff(attempt, baseDelay = 1e3, maxDelay = 3e4) {
|
|
217
|
+
const exponentialDelay = baseDelay * Math.pow(2, attempt);
|
|
218
|
+
const jitter = exponentialDelay * Math.random() * 0.25;
|
|
219
|
+
return Math.min(exponentialDelay + jitter, maxDelay);
|
|
220
|
+
}
|
|
221
|
+
async function retryWithBackoff(fn, maxRetries = 3, shouldRetry = isRetryableError) {
|
|
222
|
+
let lastError;
|
|
223
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
224
|
+
try {
|
|
225
|
+
return await fn();
|
|
226
|
+
} catch (error) {
|
|
227
|
+
lastError = error;
|
|
228
|
+
if (attempt === maxRetries) {
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
if (!shouldRetry(error)) {
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
const delay = calculateBackoff(attempt);
|
|
235
|
+
await sleep(delay);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
throw lastError;
|
|
239
|
+
}
|
|
240
|
+
async function withTimeout(promise, timeoutMs, timeoutMessage) {
|
|
241
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
242
|
+
setTimeout(() => {
|
|
243
|
+
reject(new AuraTimeoutError(timeoutMessage || `Request timeout after ${timeoutMs}ms`));
|
|
244
|
+
}, timeoutMs);
|
|
245
|
+
});
|
|
246
|
+
return Promise.race([promise, timeoutPromise]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/resources/escrows.ts
|
|
250
|
+
function isEscrowDeployed(e) {
|
|
251
|
+
if (e.blockchain?.deployed) return true;
|
|
252
|
+
return ["deployed", "funded", "locked", "released"].includes(e.state ?? "");
|
|
253
|
+
}
|
|
254
|
+
function isEscrowFunded(e) {
|
|
255
|
+
return ["funded", "locked", "released"].includes(e.state ?? "");
|
|
256
|
+
}
|
|
257
|
+
function parseUsdc(value) {
|
|
258
|
+
const n = Number.parseFloat(value);
|
|
259
|
+
return Number.isFinite(n) ? n : 0;
|
|
260
|
+
}
|
|
261
|
+
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
262
|
+
var Escrows = class {
|
|
263
|
+
constructor(client) {
|
|
264
|
+
this.client = client;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Create a new escrow
|
|
268
|
+
*/
|
|
269
|
+
async create(params, idempotencyKey) {
|
|
270
|
+
return this.client["request"](
|
|
271
|
+
"POST",
|
|
272
|
+
"/v1/escrow/create",
|
|
273
|
+
params,
|
|
274
|
+
{ idempotencyKey }
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get escrow by ID
|
|
279
|
+
*/
|
|
280
|
+
async get(escrowId) {
|
|
281
|
+
return this.client["request"]("GET", `/v1/escrow/${escrowId}`);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* List escrows with optional filters
|
|
285
|
+
*/
|
|
286
|
+
async list(params) {
|
|
287
|
+
const queryParams = new URLSearchParams();
|
|
288
|
+
if (params?.search) queryParams.set("search", params.search);
|
|
289
|
+
if (params?.state) queryParams.set("state", params.state);
|
|
290
|
+
if (params?.sortBy) queryParams.set("sortBy", params.sortBy);
|
|
291
|
+
if (params?.limit) queryParams.set("limit", params.limit.toString());
|
|
292
|
+
if (params?.offset) queryParams.set("offset", params.offset.toString());
|
|
293
|
+
const query = queryParams.toString();
|
|
294
|
+
const path = query ? `/v1/escrow?${query}` : "/v1/escrow";
|
|
295
|
+
return this.client["request"]("GET", path);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Fund an escrow
|
|
299
|
+
*/
|
|
300
|
+
async fund(params, idempotencyKey) {
|
|
301
|
+
const { escrowId, ...body } = params;
|
|
302
|
+
return this.client["request"](
|
|
303
|
+
"POST",
|
|
304
|
+
`/v1/escrow/${escrowId}/fund`,
|
|
305
|
+
body,
|
|
306
|
+
{ idempotencyKey }
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Release an escrow
|
|
311
|
+
*/
|
|
312
|
+
async release(params, idempotencyKey) {
|
|
313
|
+
const { escrowId, ...body } = params;
|
|
314
|
+
return this.client["request"](
|
|
315
|
+
"POST",
|
|
316
|
+
`/v1/escrow/${escrowId}/release`,
|
|
317
|
+
body,
|
|
318
|
+
{ idempotencyKey }
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Refund an escrow
|
|
323
|
+
*/
|
|
324
|
+
async refund(params, idempotencyKey) {
|
|
325
|
+
return this.client["request"](
|
|
326
|
+
"POST",
|
|
327
|
+
`/v1/escrow/refund`,
|
|
328
|
+
params,
|
|
329
|
+
{ idempotencyKey }
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Receive USDC and split it across recipients in a single call (board D4).
|
|
334
|
+
*
|
|
335
|
+
* Composes the existing, crash-safe escrow path:
|
|
336
|
+
* create → poll-until-deployed → pre-flight balance → fund → poll-until-funded → release.
|
|
337
|
+
*
|
|
338
|
+
* Funds move from an Aura wallet owned by `payerOwnerId` (the agent itself or a
|
|
339
|
+
* counterparty). The result's `stage` makes a partial run explicit:
|
|
340
|
+
* - `created` — deployment didn't confirm within `waitForDeploymentMs`
|
|
341
|
+
* - `deployed` — payer wallet lacks funds (see `fundingRequired`); no money moved
|
|
342
|
+
* - `funded` — funded but not released (`autoRelease:false`, or funding didn't
|
|
343
|
+
* settle within `waitForFundingMs` → `fundingPending: true`)
|
|
344
|
+
* - `released` — split to recipients (happy path)
|
|
345
|
+
*
|
|
346
|
+
* Resume note: a partial run is resumed by acting on the returned
|
|
347
|
+
* `escrow.escrowId` (call `fund`/`release` directly) — NOT by re-calling
|
|
348
|
+
* `receiveAndSplit`, which re-`create`s and would hit a duplicate-orderId error.
|
|
349
|
+
* Pass a stable `idempotencyKey` for retry-safe fund/release.
|
|
350
|
+
*
|
|
351
|
+
* Funding is asynchronous and the platform's fund/release endpoints return
|
|
352
|
+
* operation metadata (not a full escrow), so this method re-`get`s the escrow
|
|
353
|
+
* for authoritative state + splits rather than trusting those response bodies.
|
|
354
|
+
*
|
|
355
|
+
* @throws AuraValidationError if split percentages don't sum to 100 (pre-flight).
|
|
356
|
+
*/
|
|
357
|
+
async receiveAndSplit(params) {
|
|
358
|
+
const {
|
|
359
|
+
orderId,
|
|
360
|
+
payerOwnerId,
|
|
361
|
+
amount,
|
|
362
|
+
splits,
|
|
363
|
+
chain,
|
|
364
|
+
actorId,
|
|
365
|
+
autoRelease = true,
|
|
366
|
+
waitForDeploymentMs = 6e4,
|
|
367
|
+
waitForFundingMs = 6e4,
|
|
368
|
+
pollIntervalMs = 2e3,
|
|
369
|
+
idempotencyKey
|
|
370
|
+
} = params;
|
|
371
|
+
const pctSum = splits.reduce((acc, s) => acc + s.percentage, 0);
|
|
372
|
+
if (Math.round(pctSum * 100) !== 1e4) {
|
|
373
|
+
throw new AuraValidationError(
|
|
374
|
+
`Split percentages must sum to 100 (got ${pctSum}).`,
|
|
375
|
+
{ splits }
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
const runKey = idempotencyKey ?? generateIdempotencyKey();
|
|
379
|
+
const pollMs = Math.max(pollIntervalMs, 250);
|
|
380
|
+
const created = await this.create(
|
|
381
|
+
{ orderId, buyerOwnerId: payerOwnerId, amount, chain, splits },
|
|
382
|
+
`${runKey}-create`
|
|
383
|
+
);
|
|
384
|
+
const escrowId = created.escrowId;
|
|
385
|
+
const deployed = await this.pollEscrowUntil(
|
|
386
|
+
escrowId,
|
|
387
|
+
isEscrowDeployed,
|
|
388
|
+
waitForDeploymentMs,
|
|
389
|
+
pollMs,
|
|
390
|
+
created
|
|
391
|
+
);
|
|
392
|
+
if (!isEscrowDeployed(deployed)) {
|
|
393
|
+
return {
|
|
394
|
+
stage: "created",
|
|
395
|
+
escrow: deployed,
|
|
396
|
+
splits: deployed.splits,
|
|
397
|
+
deploymentPending: true
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const payerWalletId = deployed.buyerWalletId;
|
|
401
|
+
const balance = await this.client.wallets.getBalance(payerWalletId);
|
|
402
|
+
if (parseUsdc(balance.balance.usdc) < parseUsdc(amount)) {
|
|
403
|
+
return {
|
|
404
|
+
stage: "deployed",
|
|
405
|
+
escrow: deployed,
|
|
406
|
+
splits: deployed.splits,
|
|
407
|
+
fundingRequired: {
|
|
408
|
+
payerWalletId,
|
|
409
|
+
payerAddress: deployed.buyerWallet.address,
|
|
410
|
+
amount,
|
|
411
|
+
available: balance.balance.usdc
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
await this.fund(
|
|
416
|
+
{ escrowId, buyerWalletId: payerWalletId, amount },
|
|
417
|
+
`${runKey}-fund`
|
|
418
|
+
);
|
|
419
|
+
const funded = await this.pollEscrowUntil(
|
|
420
|
+
escrowId,
|
|
421
|
+
isEscrowFunded,
|
|
422
|
+
waitForFundingMs,
|
|
423
|
+
pollMs,
|
|
424
|
+
deployed
|
|
425
|
+
);
|
|
426
|
+
if (!isEscrowFunded(funded)) {
|
|
427
|
+
return { stage: "funded", escrow: funded, splits: funded.splits, fundingPending: true };
|
|
428
|
+
}
|
|
429
|
+
if (!autoRelease) {
|
|
430
|
+
return { stage: "funded", escrow: funded, splits: funded.splits };
|
|
431
|
+
}
|
|
432
|
+
await this.release(
|
|
433
|
+
{ escrowId, actorId: actorId ?? payerOwnerId, reason: "receiveAndSplit auto-release" },
|
|
434
|
+
`${runKey}-release`
|
|
435
|
+
);
|
|
436
|
+
const final = await this.get(escrowId);
|
|
437
|
+
return { stage: "released", escrow: final, splits: final.splits };
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Poll `get(escrowId)` until `predicate` holds or `budgetMs` elapses. Returns
|
|
441
|
+
* the last-observed escrow; the caller re-checks the predicate to branch.
|
|
442
|
+
* `seed` is the escrow already in hand, checked before any poll so a
|
|
443
|
+
* synchronously-settled escrow returns without a network round-trip.
|
|
444
|
+
*/
|
|
445
|
+
async pollEscrowUntil(escrowId, predicate, budgetMs, pollMs, seed) {
|
|
446
|
+
let escrow = seed;
|
|
447
|
+
if (predicate(escrow)) return escrow;
|
|
448
|
+
const deadline = Date.now() + budgetMs;
|
|
449
|
+
while (Date.now() < deadline) {
|
|
450
|
+
await sleep2(pollMs);
|
|
451
|
+
escrow = await this.get(escrowId);
|
|
452
|
+
if (predicate(escrow)) break;
|
|
453
|
+
}
|
|
454
|
+
return escrow;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Create a dispute for an escrow
|
|
458
|
+
*/
|
|
459
|
+
async createDispute(params, idempotencyKey) {
|
|
460
|
+
return this.client["request"](
|
|
461
|
+
"POST",
|
|
462
|
+
`/v1/escrow/${params.escrowId}/dispute`,
|
|
463
|
+
params,
|
|
464
|
+
{ idempotencyKey }
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Get dispute details
|
|
469
|
+
*/
|
|
470
|
+
async getDispute(escrowId, disputeId) {
|
|
471
|
+
return this.client["request"](
|
|
472
|
+
"GET",
|
|
473
|
+
`/v1/escrow/${escrowId}/dispute/${disputeId}`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
var Mandates = class {
|
|
478
|
+
constructor(client) {
|
|
479
|
+
this.client = client;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* List mandates for the authenticated account. Defaults to `status=pending`
|
|
483
|
+
* so this acts as the operator's inbox out of the box.
|
|
484
|
+
*/
|
|
485
|
+
async list(params) {
|
|
486
|
+
const query = new URLSearchParams();
|
|
487
|
+
if (params?.status) query.set("status", params.status);
|
|
488
|
+
if (params?.agentId) query.set("agentId", params.agentId);
|
|
489
|
+
if (params?.limit !== void 0) query.set("limit", String(params.limit));
|
|
490
|
+
if (params?.cursor) query.set("cursor", params.cursor);
|
|
491
|
+
const qs = query.toString();
|
|
492
|
+
return this.client["request"](
|
|
493
|
+
"GET",
|
|
494
|
+
qs ? `/v1/agent/mandates?${qs}` : "/v1/agent/mandates"
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Convenience wrapper for the most common case: pending mandates in
|
|
499
|
+
* urgency order (most-expiring first).
|
|
500
|
+
*/
|
|
501
|
+
async listPending(extras) {
|
|
502
|
+
return this.list({ ...extras, status: "pending" });
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Get a single mandate by id.
|
|
506
|
+
*/
|
|
507
|
+
async get(mandateId) {
|
|
508
|
+
return this.client["request"](
|
|
509
|
+
"GET",
|
|
510
|
+
`/v1/agent/mandates/${mandateId}`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Approve a pending mandate. Caller is responsible for computing
|
|
515
|
+
* `decisionSignature` — use `MandateSignature.compute()` below.
|
|
516
|
+
*
|
|
517
|
+
* Idempotent: re-approving an already-`approved` mandate returns 200 with
|
|
518
|
+
* the existing decision. Approving a `rejected`/`cancelled` mandate
|
|
519
|
+
* returns 409.
|
|
520
|
+
*/
|
|
521
|
+
async approve(params, idempotencyKey) {
|
|
522
|
+
const { mandateId, ...body } = params;
|
|
523
|
+
return this.client["request"](
|
|
524
|
+
"POST",
|
|
525
|
+
`/v1/agent/mandates/${mandateId}/approve`,
|
|
526
|
+
body,
|
|
527
|
+
{ idempotencyKey }
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Reject a pending mandate. Same signature scheme as `approve`.
|
|
532
|
+
*/
|
|
533
|
+
async reject(params, idempotencyKey) {
|
|
534
|
+
const { mandateId, ...body } = params;
|
|
535
|
+
return this.client["request"](
|
|
536
|
+
"POST",
|
|
537
|
+
`/v1/agent/mandates/${mandateId}/reject`,
|
|
538
|
+
body,
|
|
539
|
+
{ idempotencyKey }
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
var MandateSignature = {
|
|
544
|
+
compute(input) {
|
|
545
|
+
const payload = `${input.mandateId}:${input.decision}:${input.decidedAt}`;
|
|
546
|
+
return crypto$1.createHmac("sha256", input.secret).update(payload).digest("hex");
|
|
547
|
+
},
|
|
548
|
+
verify(input) {
|
|
549
|
+
const expected = MandateSignature.compute({
|
|
550
|
+
mandateId: input.mandateId,
|
|
551
|
+
decision: input.decision,
|
|
552
|
+
decidedAt: input.decidedAt,
|
|
553
|
+
secret: input.secret
|
|
554
|
+
});
|
|
555
|
+
if (expected.length !== input.signature.length) return false;
|
|
556
|
+
let diff = 0;
|
|
557
|
+
for (let i = 0; i < expected.length; i++) {
|
|
558
|
+
diff |= expected.charCodeAt(i) ^ input.signature.charCodeAt(i);
|
|
559
|
+
}
|
|
560
|
+
return diff === 0;
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// src/resources/policies.ts
|
|
565
|
+
var Policies = class {
|
|
566
|
+
constructor(client) {
|
|
567
|
+
this.client = client;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* List all policies attached to an agent.
|
|
571
|
+
*/
|
|
572
|
+
async list(agentId) {
|
|
573
|
+
return this.client["request"](
|
|
574
|
+
"GET",
|
|
575
|
+
`/v1/agents/${agentId}/policies`
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Get a single policy by id (scoped to the parent agent).
|
|
580
|
+
*/
|
|
581
|
+
async get(agentId, policyId) {
|
|
582
|
+
return this.client["request"](
|
|
583
|
+
"GET",
|
|
584
|
+
`/v1/agents/${agentId}/policies/${policyId}`
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Create a new policy for an agent. The agent's policy engine will start
|
|
589
|
+
* matching against this policy on the next evaluation.
|
|
590
|
+
*/
|
|
591
|
+
async create(params, idempotencyKey) {
|
|
592
|
+
const { agentId, ...body } = params;
|
|
593
|
+
return this.client["request"](
|
|
594
|
+
"POST",
|
|
595
|
+
`/v1/agents/${agentId}/policies`,
|
|
596
|
+
body,
|
|
597
|
+
{ idempotencyKey }
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
// src/resources/wallets.ts
|
|
603
|
+
var Wallets = class {
|
|
604
|
+
constructor(client) {
|
|
605
|
+
this.client = client;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Create a new wallet
|
|
609
|
+
*/
|
|
610
|
+
async create(params, idempotencyKey) {
|
|
611
|
+
return this.client["request"](
|
|
612
|
+
"POST",
|
|
613
|
+
"/v1/wallets",
|
|
614
|
+
params,
|
|
615
|
+
{ idempotencyKey }
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Get wallet by ID
|
|
620
|
+
*/
|
|
621
|
+
async get(walletId) {
|
|
622
|
+
return this.client["request"]("GET", `/v1/wallets/${walletId}`);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* List wallets with optional filters
|
|
626
|
+
*/
|
|
627
|
+
async list(params) {
|
|
628
|
+
const queryParams = new URLSearchParams();
|
|
629
|
+
if (params?.entityId) queryParams.set("entityId", params.entityId);
|
|
630
|
+
if (params?.chain) queryParams.set("chain", params.chain);
|
|
631
|
+
if (params?.type) queryParams.set("type", params.type);
|
|
632
|
+
if (params?.page) queryParams.set("page", params.page.toString());
|
|
633
|
+
if (params?.limit) queryParams.set("limit", params.limit.toString());
|
|
634
|
+
const query = queryParams.toString();
|
|
635
|
+
const path = query ? `/v1/wallets?${query}` : "/v1/wallets";
|
|
636
|
+
return this.client["request"]("GET", path);
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Get wallet balance
|
|
640
|
+
*/
|
|
641
|
+
async getBalance(walletId) {
|
|
642
|
+
return this.client["request"](
|
|
643
|
+
"GET",
|
|
644
|
+
`/v1/wallets/${walletId}/balance`
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Transfer funds from wallet
|
|
649
|
+
*/
|
|
650
|
+
async transfer(params) {
|
|
651
|
+
const idempotencyKey = params.idempotencyKey || this.generateIdempotencyKey();
|
|
652
|
+
return this.client["request"](
|
|
653
|
+
"POST",
|
|
654
|
+
`/v1/wallets/${params.fromWalletId}/transfer`,
|
|
655
|
+
params,
|
|
656
|
+
{ idempotencyKey }
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Request testnet funds (USDC) for a wallet from the platform faucet.
|
|
661
|
+
*
|
|
662
|
+
* Testnet only. Backed by `POST /v1/wallets/:id/faucet`, provided by the
|
|
663
|
+
* platform (AURA-010). Until that endpoint ships, this capability-probes and
|
|
664
|
+
* raises {@link AuraFaucetUnavailableError} (rather than a generic 404/501)
|
|
665
|
+
* so callers can degrade gracefully to manual funding.
|
|
666
|
+
*/
|
|
667
|
+
async requestTestnetFunds(walletId, params) {
|
|
668
|
+
try {
|
|
669
|
+
return await this.client["request"](
|
|
670
|
+
"POST",
|
|
671
|
+
`/v1/wallets/${walletId}/faucet`,
|
|
672
|
+
params ?? {}
|
|
673
|
+
);
|
|
674
|
+
} catch (err) {
|
|
675
|
+
if (err instanceof AuraAPIError && err.statusCode === 501) {
|
|
676
|
+
throw new AuraFaucetUnavailableError(
|
|
677
|
+
"Testnet faucet is not enabled on this deployment yet (POST /v1/wallets/:id/faucet returned 501). Fund the wallet manually, or ask the operator to enable the faucet endpoint."
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
throw err;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Get transfer by ID
|
|
685
|
+
*/
|
|
686
|
+
async getTransfer(walletId, transferId) {
|
|
687
|
+
return this.client["request"](
|
|
688
|
+
"GET",
|
|
689
|
+
`/v1/wallets/${walletId}/transfers/${transferId}`
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Generate a unique idempotency key
|
|
694
|
+
*/
|
|
695
|
+
generateIdempotencyKey() {
|
|
696
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
var Webhooks = class {
|
|
700
|
+
constructor(client) {
|
|
701
|
+
this.client = client;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Configure webhook endpoint and events
|
|
705
|
+
*/
|
|
706
|
+
async configure(params) {
|
|
707
|
+
return this.client["request"](
|
|
708
|
+
"POST",
|
|
709
|
+
"/v1/webhooks/config",
|
|
710
|
+
params
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Get current webhook configuration
|
|
715
|
+
*/
|
|
716
|
+
async getConfig() {
|
|
717
|
+
return this.client["request"]("GET", "/v1/webhooks/config");
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Delete webhook configuration
|
|
721
|
+
*/
|
|
722
|
+
async delete() {
|
|
723
|
+
return this.client["request"](
|
|
724
|
+
"DELETE",
|
|
725
|
+
"/v1/webhooks/config"
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Validate webhook signature
|
|
730
|
+
* Use this in your webhook handler to verify authenticity
|
|
731
|
+
*/
|
|
732
|
+
static validateSignature(options) {
|
|
733
|
+
try {
|
|
734
|
+
const { payload, signature, secret, toleranceMs = 3e5 } = options;
|
|
735
|
+
const payloadString = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
736
|
+
const { timestamp, v1 } = this.parseSignatureHeader(signature);
|
|
737
|
+
const ageMs = Math.abs(Date.now() - timestamp);
|
|
738
|
+
if (ageMs > toleranceMs) {
|
|
739
|
+
return { valid: false, error: "Signature timestamp expired" };
|
|
740
|
+
}
|
|
741
|
+
const expectedSignature = this.computeHmacSignature(`${timestamp}.${payloadString}`, secret);
|
|
742
|
+
const valid = this.secureCompare(v1, expectedSignature);
|
|
743
|
+
if (!valid) {
|
|
744
|
+
return {
|
|
745
|
+
valid: false,
|
|
746
|
+
error: "Invalid signature"
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
const event = typeof payload === "string" ? JSON.parse(payload) : payload;
|
|
750
|
+
return {
|
|
751
|
+
valid: true,
|
|
752
|
+
event
|
|
753
|
+
};
|
|
754
|
+
} catch (error) {
|
|
755
|
+
return {
|
|
756
|
+
valid: false,
|
|
757
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Parse signature header: "t=timestamp,v1=signature"
|
|
763
|
+
*/
|
|
764
|
+
static parseSignatureHeader(signatureHeader) {
|
|
765
|
+
const parts = signatureHeader.split(",").map((p) => p.trim());
|
|
766
|
+
const tPart = parts.find((p) => p.startsWith("t="));
|
|
767
|
+
const v1Part = parts.find((p) => p.startsWith("v1="));
|
|
768
|
+
if (!tPart || !v1Part) {
|
|
769
|
+
throw new Error("Invalid signature header format (expected: t=...,v1=...)");
|
|
770
|
+
}
|
|
771
|
+
const timestamp = parseInt(tPart.slice(2), 10);
|
|
772
|
+
const v1 = v1Part.slice(3);
|
|
773
|
+
if (!Number.isFinite(timestamp) || timestamp <= 0) {
|
|
774
|
+
throw new Error("Invalid signature timestamp");
|
|
775
|
+
}
|
|
776
|
+
if (!v1) {
|
|
777
|
+
throw new Error("Invalid signature value");
|
|
778
|
+
}
|
|
779
|
+
return { timestamp, v1 };
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Compute HMAC-SHA256 signature
|
|
783
|
+
*/
|
|
784
|
+
static computeHmacSignature(payload, secret) {
|
|
785
|
+
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
786
|
+
throw new Error("Web Crypto implementation needed for browser");
|
|
787
|
+
}
|
|
788
|
+
return crypto$1.createHmac("sha256", secret).update(payload).digest("hex");
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Constant-time string comparison
|
|
792
|
+
*/
|
|
793
|
+
static secureCompare(a, b) {
|
|
794
|
+
if (a.length !== b.length) {
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
let result = 0;
|
|
798
|
+
for (let i = 0; i < a.length; i++) {
|
|
799
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
800
|
+
}
|
|
801
|
+
return result === 0;
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
// src/client.ts
|
|
806
|
+
var AuraClient = class _AuraClient {
|
|
807
|
+
constructor(config) {
|
|
808
|
+
this.apiKey = config.apiKey;
|
|
809
|
+
this.baseUrl = _AuraClient.normalizeBaseUrl(config.baseUrl);
|
|
810
|
+
this.timeout = config.timeout || 3e4;
|
|
811
|
+
this.maxRetries = config.maxRetries || 3;
|
|
812
|
+
this.autoIdempotency = config.autoIdempotency !== false;
|
|
813
|
+
if (!this.apiKey) {
|
|
814
|
+
throw new AuraError("API key is required", "MISSING_API_KEY");
|
|
815
|
+
}
|
|
816
|
+
this.escrows = new Escrows(this);
|
|
817
|
+
this.wallets = new Wallets(this);
|
|
818
|
+
this.webhooks = new Webhooks(this);
|
|
819
|
+
this.agents = new Agents(this);
|
|
820
|
+
this.policies = new Policies(this);
|
|
821
|
+
this.mandates = new Mandates(this);
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Normalize the base URL so resource paths like `/v1/escrow` reach the
|
|
825
|
+
* platform's `/api/v1/escrow` route regardless of how the caller framed
|
|
826
|
+
* `baseUrl`. Trailing slashes are trimmed; missing `/api` is appended.
|
|
827
|
+
*
|
|
828
|
+
* @internal exported pattern for tests; do not call directly.
|
|
829
|
+
*/
|
|
830
|
+
static normalizeBaseUrl(input) {
|
|
831
|
+
const raw = (input || "https://getaura.sh/api").replace(/\/+$/, "");
|
|
832
|
+
if (/\/api(\/v\d+)?$/.test(raw) || /\/api$/.test(raw)) {
|
|
833
|
+
return raw;
|
|
834
|
+
}
|
|
835
|
+
return `${raw}/api`;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Get the API key
|
|
839
|
+
*/
|
|
840
|
+
getApiKey() {
|
|
841
|
+
return this.apiKey;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Get the base URL
|
|
845
|
+
*/
|
|
846
|
+
getBaseUrl() {
|
|
847
|
+
return this.baseUrl;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Get the timeout value
|
|
851
|
+
*/
|
|
852
|
+
getTimeout() {
|
|
853
|
+
return this.timeout;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Get the max retries value
|
|
857
|
+
*/
|
|
858
|
+
getMaxRetries() {
|
|
859
|
+
return this.maxRetries;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Make an authenticated request to the API with automatic retry logic
|
|
863
|
+
*/
|
|
864
|
+
async request(method, path, body, options) {
|
|
865
|
+
const requestFn = () => this.executeRequest(method, path, body, options);
|
|
866
|
+
if (options?.skipRetry || method === "GET") {
|
|
867
|
+
return requestFn();
|
|
868
|
+
}
|
|
869
|
+
return retryWithBackoff(requestFn, this.maxRetries);
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Execute a single HTTP request (internal)
|
|
873
|
+
*/
|
|
874
|
+
async executeRequest(method, path, body, options) {
|
|
875
|
+
const url = `${this.baseUrl}${path}`;
|
|
876
|
+
const headers = {
|
|
877
|
+
"Content-Type": "application/json",
|
|
878
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
879
|
+
"User-Agent": "@aura-payments/sdk/2.1.0"
|
|
880
|
+
};
|
|
881
|
+
if (options?.idempotencyKey) {
|
|
882
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
883
|
+
} else if (this.autoIdempotency && (method === "POST" || method === "PUT")) {
|
|
884
|
+
headers["Idempotency-Key"] = generateIdempotencyKey();
|
|
885
|
+
}
|
|
886
|
+
try {
|
|
887
|
+
const response = await withTimeout(
|
|
888
|
+
fetch(url, {
|
|
889
|
+
method,
|
|
890
|
+
headers,
|
|
891
|
+
body: body ? JSON.stringify(body) : void 0
|
|
892
|
+
}),
|
|
893
|
+
this.timeout
|
|
894
|
+
);
|
|
895
|
+
if (!response.ok) {
|
|
896
|
+
await this.handleErrorResponse(response);
|
|
897
|
+
}
|
|
898
|
+
if (response.status === 204) {
|
|
899
|
+
return void 0;
|
|
900
|
+
}
|
|
901
|
+
const data = await response.json();
|
|
902
|
+
return this.normalizeResponse(data);
|
|
903
|
+
} catch (error) {
|
|
904
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
905
|
+
throw new AuraNetworkError("Network request failed", error);
|
|
906
|
+
}
|
|
907
|
+
if (error instanceof AuraError) {
|
|
908
|
+
throw error;
|
|
909
|
+
}
|
|
910
|
+
throw new AuraError(
|
|
911
|
+
error instanceof Error ? error.message : "Unknown error occurred",
|
|
912
|
+
"UNKNOWN_ERROR"
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Normalize API responses across different wrapper formats.
|
|
918
|
+
*
|
|
919
|
+
* The platform API currently uses two common success wrappers:
|
|
920
|
+
* - { success: true, data: T, ... }
|
|
921
|
+
* - { data: T, meta: {...} } (and sometimes { data: T[], meta, pagination })
|
|
922
|
+
*
|
|
923
|
+
* SDK methods generally return the inner `data` payload, except for paginated
|
|
924
|
+
* endpoints where pagination metadata is meaningful and preserved.
|
|
925
|
+
*/
|
|
926
|
+
normalizeResponse(payload) {
|
|
927
|
+
if (!payload || typeof payload !== "object") return payload;
|
|
928
|
+
const obj = payload;
|
|
929
|
+
if ("success" in obj && "data" in obj) {
|
|
930
|
+
return obj.data;
|
|
931
|
+
}
|
|
932
|
+
if ("data" in obj && ("meta" in obj || "metadata" in obj)) {
|
|
933
|
+
if ("pagination" in obj) {
|
|
934
|
+
return obj;
|
|
935
|
+
}
|
|
936
|
+
return obj.data;
|
|
937
|
+
}
|
|
938
|
+
if ("data" in obj && Object.keys(obj).length <= 2) {
|
|
939
|
+
return obj.data;
|
|
940
|
+
}
|
|
941
|
+
return payload;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Handle HTTP error responses and throw appropriate errors
|
|
945
|
+
*/
|
|
946
|
+
async handleErrorResponse(response) {
|
|
947
|
+
const statusCode = response.status;
|
|
948
|
+
let errorData = {};
|
|
949
|
+
try {
|
|
950
|
+
const data = await response.json();
|
|
951
|
+
if (typeof data === "object" && data !== null) {
|
|
952
|
+
errorData = data;
|
|
953
|
+
}
|
|
954
|
+
} catch {
|
|
955
|
+
errorData = { message: response.statusText };
|
|
956
|
+
}
|
|
957
|
+
const message = errorData.message || `HTTP ${statusCode} error`;
|
|
958
|
+
const code = errorData.code;
|
|
959
|
+
const details = errorData.details;
|
|
960
|
+
switch (statusCode) {
|
|
961
|
+
case 400:
|
|
962
|
+
throw new AuraValidationError(message, details);
|
|
963
|
+
case 401:
|
|
964
|
+
throw new AuraAuthenticationError(message);
|
|
965
|
+
case 404:
|
|
966
|
+
throw new AuraNotFoundError(message);
|
|
967
|
+
case 429: {
|
|
968
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
969
|
+
throw new AuraRateLimitError(message, retryAfter ? parseInt(retryAfter) : void 0);
|
|
970
|
+
}
|
|
971
|
+
case 408:
|
|
972
|
+
throw new AuraTimeoutError(message);
|
|
973
|
+
default:
|
|
974
|
+
throw new AuraAPIError(message, statusCode, code, details);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Get the auto-idempotency setting
|
|
979
|
+
*/
|
|
980
|
+
getAutoIdempotency() {
|
|
981
|
+
return this.autoIdempotency;
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
exports.Agents = Agents;
|
|
986
|
+
exports.AuraAPIError = AuraAPIError;
|
|
987
|
+
exports.AuraAuthenticationError = AuraAuthenticationError;
|
|
988
|
+
exports.AuraClient = AuraClient;
|
|
989
|
+
exports.AuraError = AuraError;
|
|
990
|
+
exports.AuraFaucetUnavailableError = AuraFaucetUnavailableError;
|
|
991
|
+
exports.AuraNetworkError = AuraNetworkError;
|
|
992
|
+
exports.AuraNotFoundError = AuraNotFoundError;
|
|
993
|
+
exports.AuraRateLimitError = AuraRateLimitError;
|
|
994
|
+
exports.AuraTimeoutError = AuraTimeoutError;
|
|
995
|
+
exports.AuraValidationError = AuraValidationError;
|
|
996
|
+
exports.Escrows = Escrows;
|
|
997
|
+
exports.MandateSignature = MandateSignature;
|
|
998
|
+
exports.Mandates = Mandates;
|
|
999
|
+
exports.Policies = Policies;
|
|
1000
|
+
exports.Wallets = Wallets;
|
|
1001
|
+
exports.Webhooks = Webhooks;
|
|
1002
|
+
exports.calculateBackoff = calculateBackoff;
|
|
1003
|
+
exports.generateIdempotencyKey = generateIdempotencyKey;
|
|
1004
|
+
exports.isAuraAPIError = isAuraAPIError;
|
|
1005
|
+
exports.isAuraError = isAuraError;
|
|
1006
|
+
exports.isAuraFaucetUnavailableError = isAuraFaucetUnavailableError;
|
|
1007
|
+
exports.isAuraNetworkError = isAuraNetworkError;
|
|
1008
|
+
exports.isAuraTimeoutError = isAuraTimeoutError;
|
|
1009
|
+
exports.isRetryableError = isRetryableError;
|
|
1010
|
+
exports.retryWithBackoff = retryWithBackoff;
|
|
1011
|
+
exports.withTimeout = withTimeout;
|
|
1012
|
+
//# sourceMappingURL=index.js.map
|
|
1013
|
+
//# sourceMappingURL=index.js.map
|