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