@tallyforagents/sdk 0.1.1 → 0.3.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/dist/index.cjs +457 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +397 -2
- package/dist/index.d.ts +397 -2
- package/dist/index.js +457 -2
- package/dist/index.js.map +1 -1
- package/package.json +8 -6
package/dist/index.js
CHANGED
|
@@ -64,6 +64,22 @@ function makeError(payload, status) {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// src/client.ts
|
|
67
|
+
var warnedRotations = /* @__PURE__ */ new Set();
|
|
68
|
+
function warnRotationOnce(key, until) {
|
|
69
|
+
const id = `${key.slice(0, 12)}:${until}`;
|
|
70
|
+
if (warnedRotations.has(id)) return;
|
|
71
|
+
warnedRotations.add(id);
|
|
72
|
+
const remaining = (() => {
|
|
73
|
+
const ms = new Date(until).getTime() - Date.now();
|
|
74
|
+
if (ms <= 0) return "shortly";
|
|
75
|
+
const h = Math.floor(ms / 36e5);
|
|
76
|
+
const m = Math.floor(ms % 36e5 / 6e4);
|
|
77
|
+
return h > 0 ? `${h}h ${m}m` : `${m}m`;
|
|
78
|
+
})();
|
|
79
|
+
console.warn(
|
|
80
|
+
`[tally] API key is in its rotation grace window (ends in ~${remaining}, at ${until}). Switch to the rotated key before then to avoid 401s.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
67
83
|
var TallyClient = class {
|
|
68
84
|
apiKey;
|
|
69
85
|
baseUrl;
|
|
@@ -98,6 +114,8 @@ var TallyClient = class {
|
|
|
98
114
|
},
|
|
99
115
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
100
116
|
});
|
|
117
|
+
const graceUntil = res.headers.get("tally-rotation-grace-until");
|
|
118
|
+
if (graceUntil) warnRotationOnce(this.apiKey, graceUntil);
|
|
101
119
|
if (!res.ok) {
|
|
102
120
|
let payload = {};
|
|
103
121
|
try {
|
|
@@ -149,6 +167,88 @@ var AgentsResource = class {
|
|
|
149
167
|
);
|
|
150
168
|
return agent;
|
|
151
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Soft-deletes an agent. The row stays in the DB so historical
|
|
172
|
+
* transactions keep their attribution; future list/get responses
|
|
173
|
+
* hide it.
|
|
174
|
+
*
|
|
175
|
+
* Throws `ConflictError` (HTTP 409, `code: "has_active_grants"`) if
|
|
176
|
+
* the agent has any active permissions — revoke them first.
|
|
177
|
+
*
|
|
178
|
+
* Re-creating an agent with the same `id` via `upsert()` is the
|
|
179
|
+
* supported way to bring a deleted agent back.
|
|
180
|
+
*/
|
|
181
|
+
async delete(id) {
|
|
182
|
+
await this.client.request(
|
|
183
|
+
"DELETE",
|
|
184
|
+
`/v1/agents/${encodeURIComponent(id)}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// src/pagination.ts
|
|
190
|
+
var AsyncResourcePage = class {
|
|
191
|
+
constructor(fetchPage) {
|
|
192
|
+
this.fetchPage = fetchPage;
|
|
193
|
+
}
|
|
194
|
+
fetchPage;
|
|
195
|
+
/**
|
|
196
|
+
* Async-iterates every item across all pages. Stops when the server
|
|
197
|
+
* returns `next_cursor: null`.
|
|
198
|
+
*
|
|
199
|
+
* ```ts
|
|
200
|
+
* for await (const p of tally.payments.list({ status: "confirmed" })) {
|
|
201
|
+
* console.log(p.id);
|
|
202
|
+
* }
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
async *[Symbol.asyncIterator]() {
|
|
206
|
+
let cursor = null;
|
|
207
|
+
while (true) {
|
|
208
|
+
const page = await this.fetchPage(cursor);
|
|
209
|
+
for (const item of page.data) yield item;
|
|
210
|
+
if (!page.next_cursor) break;
|
|
211
|
+
cursor = page.next_cursor;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Collects items into an array, optionally bounded. `toArray()` with
|
|
216
|
+
* no argument pulls every page; `toArray(20)` stops after the first
|
|
217
|
+
* 20 items (across whatever page boundaries that crosses).
|
|
218
|
+
*
|
|
219
|
+
* ```ts
|
|
220
|
+
* const recent = await tally.payments.list().toArray(20);
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
async toArray(maxItems) {
|
|
224
|
+
const out = [];
|
|
225
|
+
for await (const item of this) {
|
|
226
|
+
out.push(item);
|
|
227
|
+
if (maxItems !== void 0 && out.length >= maxItems) break;
|
|
228
|
+
}
|
|
229
|
+
return out;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Returns just the first page (no auto-pagination). Use when the
|
|
233
|
+
* cursor itself matters — e.g., persisting it across runs to resume
|
|
234
|
+
* iteration later.
|
|
235
|
+
*
|
|
236
|
+
* ```ts
|
|
237
|
+
* const { data, next_cursor } = await tally.payments.list().firstPage();
|
|
238
|
+
* // … store next_cursor; next run: tally.payments.list().pageAfter(saved_cursor)
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
async firstPage() {
|
|
242
|
+
return this.fetchPage(null);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Returns the page that follows the given cursor. Useful for resuming
|
|
246
|
+
* iteration from a previously-persisted cursor, or for manual paging
|
|
247
|
+
* when the auto-iterator's eager fetching isn't a fit.
|
|
248
|
+
*/
|
|
249
|
+
async pageAfter(cursor) {
|
|
250
|
+
return this.fetchPage(cursor);
|
|
251
|
+
}
|
|
152
252
|
};
|
|
153
253
|
|
|
154
254
|
// src/resources/payments.ts
|
|
@@ -192,10 +292,126 @@ var PaymentsResource = class {
|
|
|
192
292
|
);
|
|
193
293
|
return payment;
|
|
194
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Lists payments in the API key's account + mode, auto-paginated.
|
|
297
|
+
* Returns an `AsyncResourcePage` you can `for await` over to iterate
|
|
298
|
+
* every payment, or call `.toArray(n)` for a bounded read.
|
|
299
|
+
*
|
|
300
|
+
* Pending outbound rows are lazily refreshed from the chain on read,
|
|
301
|
+
* matching the dashboard's behavior — polling `list({ status: "pending" })`
|
|
302
|
+
* is a valid way to wait for confirmation.
|
|
303
|
+
*
|
|
304
|
+
* ```ts
|
|
305
|
+
* for await (const p of tally.payments.list({ status: "confirmed" })) {
|
|
306
|
+
* console.log(p.id, p.amount_usdc, p.to);
|
|
307
|
+
* }
|
|
308
|
+
*
|
|
309
|
+
* // Or bounded:
|
|
310
|
+
* const last20 = await tally.payments.list({ direction: "outbound" }).toArray(20);
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
list(filters = {}) {
|
|
314
|
+
const { limit, status, direction, agent_id, wallet, q } = filters;
|
|
315
|
+
return new AsyncResourcePage(async (cursor) => {
|
|
316
|
+
const qs = new URLSearchParams();
|
|
317
|
+
if (cursor) qs.set("cursor", cursor);
|
|
318
|
+
if (limit !== void 0) qs.set("limit", String(limit));
|
|
319
|
+
if (status) qs.set("status", status);
|
|
320
|
+
if (direction) qs.set("direction", direction);
|
|
321
|
+
if (agent_id) qs.set("agent_id", agent_id);
|
|
322
|
+
if (wallet) qs.set("wallet", wallet);
|
|
323
|
+
if (q) qs.set("q", q);
|
|
324
|
+
const query = qs.toString();
|
|
325
|
+
const path = query ? `/v1/payments?${query}` : "/v1/payments";
|
|
326
|
+
return this.client.request(
|
|
327
|
+
"GET",
|
|
328
|
+
path
|
|
329
|
+
);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// src/resources/permissions.ts
|
|
335
|
+
var PermissionsResource = class {
|
|
336
|
+
constructor(client) {
|
|
337
|
+
this.client = client;
|
|
338
|
+
}
|
|
339
|
+
client;
|
|
340
|
+
/**
|
|
341
|
+
* Lists active grants in the API key's account + mode, auto-paginated.
|
|
342
|
+
* Returns an `AsyncResourcePage` you can `for await` over to iterate
|
|
343
|
+
* every permission, or call `.toArray(n)` for a bounded read.
|
|
344
|
+
*
|
|
345
|
+
* Each row includes the granted caps, today's usage, and the rolling
|
|
346
|
+
* remaining-daily-allowance, so agent code can preflight a payment
|
|
347
|
+
* without round-tripping the policy bounds.
|
|
348
|
+
*
|
|
349
|
+
* ```ts
|
|
350
|
+
* for await (const p of tally.permissions.list({ agent_id: "research-bot" })) {
|
|
351
|
+
* console.log(`${p.wallet_display_name}: $${p.remaining_today_usdc} left`);
|
|
352
|
+
* }
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
list(filters = {}) {
|
|
356
|
+
const { limit, agent_id } = filters;
|
|
357
|
+
return new AsyncResourcePage(async (cursor) => {
|
|
358
|
+
const qs = new URLSearchParams();
|
|
359
|
+
if (cursor) qs.set("cursor", cursor);
|
|
360
|
+
if (limit !== void 0) qs.set("limit", String(limit));
|
|
361
|
+
if (agent_id) qs.set("agent_id", agent_id);
|
|
362
|
+
const query = qs.toString();
|
|
363
|
+
const path = query ? `/v1/permissions?${query}` : "/v1/permissions";
|
|
364
|
+
return this.client.request("GET", path);
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// src/resources/wallets.ts
|
|
370
|
+
var WalletsResource = class {
|
|
371
|
+
constructor(client) {
|
|
372
|
+
this.client = client;
|
|
373
|
+
}
|
|
374
|
+
client;
|
|
375
|
+
/**
|
|
376
|
+
* Lists every wallet in the API key's account + mode. Wallets are
|
|
377
|
+
* returned oldest-first (so the "Main Wallet" auto-provisioned at
|
|
378
|
+
* sign-in shows up first).
|
|
379
|
+
*/
|
|
380
|
+
async list() {
|
|
381
|
+
const { wallets } = await this.client.request(
|
|
382
|
+
"GET",
|
|
383
|
+
"/v1/wallets"
|
|
384
|
+
);
|
|
385
|
+
return wallets;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Provisions a new Privy server wallet in the API key's account + mode.
|
|
389
|
+
* The wallet is owned (in Privy) by the oldest `owner`-role member of
|
|
390
|
+
* the account, so SDK-created wallets behave identically to dashboard-
|
|
391
|
+
* created ones: the same passkey approves any future signer changes.
|
|
392
|
+
*/
|
|
393
|
+
async create(input) {
|
|
394
|
+
const { wallet } = await this.client.request(
|
|
395
|
+
"POST",
|
|
396
|
+
"/v1/wallets",
|
|
397
|
+
input
|
|
398
|
+
);
|
|
399
|
+
return wallet;
|
|
400
|
+
}
|
|
195
401
|
};
|
|
196
402
|
var SIGNATURE_VERSION = "v1";
|
|
197
403
|
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
198
404
|
var WebhooksResource = class {
|
|
405
|
+
/**
|
|
406
|
+
* The TallyClient is optional so legacy zero-arg construction
|
|
407
|
+
* (`new WebhooksResource()`) still works for non-class consumers who
|
|
408
|
+
* only use `verifySignature`. List / create / revoke require a
|
|
409
|
+
* client; calling them on a clientless instance throws.
|
|
410
|
+
*/
|
|
411
|
+
constructor(client) {
|
|
412
|
+
this.client = client;
|
|
413
|
+
}
|
|
414
|
+
client;
|
|
199
415
|
/**
|
|
200
416
|
* Verify a `tally-signature` header against the request body. Use in
|
|
201
417
|
* your webhook handler before processing the payload.
|
|
@@ -216,6 +432,58 @@ var WebhooksResource = class {
|
|
|
216
432
|
verifySignature(input) {
|
|
217
433
|
return verifySignature(input);
|
|
218
434
|
}
|
|
435
|
+
requireClient() {
|
|
436
|
+
if (!this.client) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
"WebhooksResource was constructed without a TallyClient. Use `new Tally({ apiKey })` to enable list/create/revoke."
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
return this.client;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Lists every webhook endpoint in the API key's account + mode.
|
|
445
|
+
* Revoked endpoints are included (with `revoked_at` set) for audit.
|
|
446
|
+
*
|
|
447
|
+
* ```ts
|
|
448
|
+
* for (const w of await tally.webhooks.list()) {
|
|
449
|
+
* console.log(`${w.url} → ${w.events.join(",")}`);
|
|
450
|
+
* }
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
async list() {
|
|
454
|
+
const { webhooks } = await this.requireClient().request("GET", "/v1/webhooks");
|
|
455
|
+
return webhooks;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Creates a new webhook endpoint. The signing secret is returned in
|
|
459
|
+
* `webhook.secret` **exactly once** — store it now; it's not
|
|
460
|
+
* recoverable later. If you lose it, revoke and recreate.
|
|
461
|
+
*
|
|
462
|
+
* ```ts
|
|
463
|
+
* const webhook = await tally.webhooks.create({
|
|
464
|
+
* url: "https://example.com/tally/events",
|
|
465
|
+
* events: ["payment.confirmed", "payment.failed"],
|
|
466
|
+
* });
|
|
467
|
+
* console.log(webhook.secret); // shown once
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
async create(input) {
|
|
471
|
+
const { webhook } = await this.requireClient().request("POST", "/v1/webhooks", input);
|
|
472
|
+
return webhook;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Revokes a webhook endpoint by id. No further deliveries will be
|
|
476
|
+
* enqueued; deliveries already in-flight are not cancelled.
|
|
477
|
+
* Idempotent: revoking an already-revoked endpoint is a no-op.
|
|
478
|
+
*
|
|
479
|
+
* ```ts
|
|
480
|
+
* await tally.webhooks.revoke("whk_01HXYZ...");
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
async revoke(id) {
|
|
484
|
+
const { webhook } = await this.requireClient().request("POST", `/v1/webhooks/${encodeURIComponent(id)}/revoke`);
|
|
485
|
+
return webhook;
|
|
486
|
+
}
|
|
219
487
|
};
|
|
220
488
|
function verifySignature(input) {
|
|
221
489
|
const { body, header, secret } = input;
|
|
@@ -250,11 +518,195 @@ function verifySignature(input) {
|
|
|
250
518
|
return { ok: true };
|
|
251
519
|
}
|
|
252
520
|
|
|
521
|
+
// src/resources/x402.ts
|
|
522
|
+
var X402_HEADER = "x-payment";
|
|
523
|
+
var SUPPORTED_NETWORKS = /* @__PURE__ */ new Set(["base-sepolia", "base"]);
|
|
524
|
+
var X402Resource = class {
|
|
525
|
+
#payments;
|
|
526
|
+
constructor(client) {
|
|
527
|
+
this.#payments = new PaymentsResource(client);
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Fetch a URL that may be paywalled by the x402 protocol. If the
|
|
531
|
+
* service returns 402, the SDK pays via Tally and retries
|
|
532
|
+
* automatically. The agent code sees one call.
|
|
533
|
+
*
|
|
534
|
+
* ```ts
|
|
535
|
+
* const { response, payment } = await tally.x402.fetch(
|
|
536
|
+
* "https://api.example.com/weather?city=Tokyo",
|
|
537
|
+
* { agent_id: "hermes", wallet: agent.wallets[0].address }
|
|
538
|
+
* );
|
|
539
|
+
*
|
|
540
|
+
* if (response.ok) {
|
|
541
|
+
* const data = await response.json();
|
|
542
|
+
* if (payment) console.log(`paid ${payment.amount_usdc} USDC — ${payment.tx_hash}`);
|
|
543
|
+
* }
|
|
544
|
+
* ```
|
|
545
|
+
*
|
|
546
|
+
* Throws `TallyError` for SDK-side problems (network unreachable,
|
|
547
|
+
* malformed 402 body, unsupported network, payment refused by
|
|
548
|
+
* Tally, etc.). Lets non-200 retry responses pass through as-is.
|
|
549
|
+
*/
|
|
550
|
+
async fetch(url, input) {
|
|
551
|
+
const initial = await this.#doFetch(
|
|
552
|
+
url,
|
|
553
|
+
input,
|
|
554
|
+
/*paymentTxHash*/
|
|
555
|
+
null
|
|
556
|
+
);
|
|
557
|
+
if (initial.status !== 402) {
|
|
558
|
+
return { response: initial, payment: null };
|
|
559
|
+
}
|
|
560
|
+
let parsed;
|
|
561
|
+
try {
|
|
562
|
+
parsed = await initial.json();
|
|
563
|
+
} catch {
|
|
564
|
+
throw new TallyError({
|
|
565
|
+
type: "x402_protocol",
|
|
566
|
+
message: "Service returned 402 with a non-JSON body.",
|
|
567
|
+
status: 0
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
const terms = parsed.accepts?.[0];
|
|
571
|
+
if (!terms) {
|
|
572
|
+
throw new TallyError({
|
|
573
|
+
type: "x402_protocol",
|
|
574
|
+
message: "402 response had no `accepts[]` payment terms.",
|
|
575
|
+
status: 0,
|
|
576
|
+
details: { x402Version: parsed.x402Version, error: parsed.error }
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
if (!SUPPORTED_NETWORKS.has(terms.network)) {
|
|
580
|
+
throw new TallyError({
|
|
581
|
+
type: "x402_protocol",
|
|
582
|
+
message: `x402 service requested unsupported network: ${terms.network}. Tally supports base-sepolia (test) and base (live).`,
|
|
583
|
+
status: 0,
|
|
584
|
+
details: { network: terms.network }
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
const atomicAmount = (() => {
|
|
588
|
+
try {
|
|
589
|
+
return BigInt(terms.maxAmountRequired);
|
|
590
|
+
} catch {
|
|
591
|
+
throw new TallyError({
|
|
592
|
+
type: "x402_protocol",
|
|
593
|
+
message: `x402 service quoted an unparseable amount: ${terms.maxAmountRequired}`,
|
|
594
|
+
status: 0
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
})();
|
|
598
|
+
const decimalAmount = formatAtomicUSDC(atomicAmount);
|
|
599
|
+
if (input.max_amount_usdc !== void 0) {
|
|
600
|
+
const capAtomic = parseDecimalUSDC(input.max_amount_usdc);
|
|
601
|
+
if (atomicAmount > capAtomic) {
|
|
602
|
+
throw new TallyError({
|
|
603
|
+
type: "x402_amount_exceeds_cap",
|
|
604
|
+
message: `x402 service requested ${decimalAmount} USDC, which exceeds the caller-supplied max_amount_usdc of ${input.max_amount_usdc}.`,
|
|
605
|
+
status: 0,
|
|
606
|
+
details: { requested_usdc: decimalAmount, max_usdc: input.max_amount_usdc }
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const memo = input.memo ?? defaultMemo(url);
|
|
611
|
+
const idempotencyKey = input.idempotency_key ?? defaultIdempotencyKey(url);
|
|
612
|
+
let payment;
|
|
613
|
+
try {
|
|
614
|
+
payment = await this.#payments.create({
|
|
615
|
+
agent_id: input.agent_id,
|
|
616
|
+
wallet: input.wallet,
|
|
617
|
+
to: terms.payTo,
|
|
618
|
+
amount_usdc: decimalAmount,
|
|
619
|
+
memo,
|
|
620
|
+
idempotency_key: idempotencyKey
|
|
621
|
+
});
|
|
622
|
+
} catch (e) {
|
|
623
|
+
throw e;
|
|
624
|
+
}
|
|
625
|
+
if (!payment.tx_hash) {
|
|
626
|
+
throw new TallyError({
|
|
627
|
+
type: "x402_payment_missing_tx_hash",
|
|
628
|
+
message: "Tally accepted the payment but returned no tx_hash.",
|
|
629
|
+
status: 0,
|
|
630
|
+
details: { payment_id: payment.id }
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
const retry = await this.#doFetch(url, input, payment.tx_hash);
|
|
634
|
+
return {
|
|
635
|
+
response: retry,
|
|
636
|
+
payment: {
|
|
637
|
+
id: payment.id,
|
|
638
|
+
tx_hash: payment.tx_hash,
|
|
639
|
+
amount_usdc: decimalAmount,
|
|
640
|
+
to: terms.payTo,
|
|
641
|
+
network: terms.network,
|
|
642
|
+
memo: payment.memo
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
async #doFetch(url, input, paymentTxHash) {
|
|
647
|
+
const headers = { ...input.headers ?? {} };
|
|
648
|
+
if (paymentTxHash) headers[X402_HEADER] = paymentTxHash;
|
|
649
|
+
const init = {
|
|
650
|
+
method: input.method ?? "GET",
|
|
651
|
+
headers,
|
|
652
|
+
body: input.body ?? void 0
|
|
653
|
+
};
|
|
654
|
+
if (input.timeout_ms !== void 0) {
|
|
655
|
+
const controller = new AbortController();
|
|
656
|
+
const timeoutId = setTimeout(() => controller.abort(), input.timeout_ms);
|
|
657
|
+
init.signal = controller.signal;
|
|
658
|
+
try {
|
|
659
|
+
return await fetch(url, init);
|
|
660
|
+
} finally {
|
|
661
|
+
clearTimeout(timeoutId);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return await fetch(url, init);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
function defaultMemo(url) {
|
|
668
|
+
try {
|
|
669
|
+
const u = new URL(url);
|
|
670
|
+
return `x402:${u.host}${u.pathname}`.slice(0, 200);
|
|
671
|
+
} catch {
|
|
672
|
+
return "x402";
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
function defaultIdempotencyKey(url) {
|
|
676
|
+
try {
|
|
677
|
+
const u = new URL(url);
|
|
678
|
+
return `x402:${u.host}${u.pathname}:${Date.now()}`.slice(0, 64);
|
|
679
|
+
} catch {
|
|
680
|
+
return `x402:${Date.now()}`;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function formatAtomicUSDC(atomic) {
|
|
684
|
+
const whole = atomic / 1000000n;
|
|
685
|
+
const frac = atomic % 1000000n;
|
|
686
|
+
const fracStr = frac.toString().padStart(6, "0").replace(/0+$/, "");
|
|
687
|
+
return fracStr ? `${whole}.${fracStr}` : `${whole}`;
|
|
688
|
+
}
|
|
689
|
+
function parseDecimalUSDC(decimal) {
|
|
690
|
+
if (!/^\d+(\.\d{1,6})?$/.test(decimal.trim())) {
|
|
691
|
+
throw new TallyError({
|
|
692
|
+
type: "validation_failed",
|
|
693
|
+
message: `Invalid decimal USDC amount: "${decimal}". Expected up to 6 fractional digits.`,
|
|
694
|
+
status: 0
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
const [whole, frac = ""] = decimal.trim().split(".");
|
|
698
|
+
const fracPadded = (frac + "000000").slice(0, 6);
|
|
699
|
+
return BigInt(whole) * 1000000n + BigInt(fracPadded);
|
|
700
|
+
}
|
|
701
|
+
|
|
253
702
|
// src/index.ts
|
|
254
703
|
var Tally = class {
|
|
255
704
|
agents;
|
|
256
705
|
payments;
|
|
706
|
+
permissions;
|
|
707
|
+
wallets;
|
|
257
708
|
webhooks;
|
|
709
|
+
x402;
|
|
258
710
|
// Expose the underlying client for advanced use (custom retries, etc.).
|
|
259
711
|
// Internal callers go through the resource classes instead.
|
|
260
712
|
client;
|
|
@@ -262,10 +714,13 @@ var Tally = class {
|
|
|
262
714
|
this.client = new TallyClient(opts);
|
|
263
715
|
this.agents = new AgentsResource(this.client);
|
|
264
716
|
this.payments = new PaymentsResource(this.client);
|
|
265
|
-
this.
|
|
717
|
+
this.permissions = new PermissionsResource(this.client);
|
|
718
|
+
this.wallets = new WalletsResource(this.client);
|
|
719
|
+
this.webhooks = new WebhooksResource(this.client);
|
|
720
|
+
this.x402 = new X402Resource(this.client);
|
|
266
721
|
}
|
|
267
722
|
};
|
|
268
723
|
|
|
269
|
-
export { AuthenticationError, ConflictError, NotFoundError, RateLimitError, Tally, TallyError, ValidationError, verifySignature };
|
|
724
|
+
export { AsyncResourcePage, AuthenticationError, ConflictError, NotFoundError, RateLimitError, Tally, TallyError, ValidationError, verifySignature };
|
|
270
725
|
//# sourceMappingURL=index.js.map
|
|
271
726
|
//# sourceMappingURL=index.js.map
|