@tallyforagents/sdk 0.1.1 → 0.2.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 +274 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +292 -2
- package/dist/index.d.ts +292 -2
- package/dist/index.js +274 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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;
|
|
@@ -254,6 +522,8 @@ function verifySignature(input) {
|
|
|
254
522
|
var Tally = class {
|
|
255
523
|
agents;
|
|
256
524
|
payments;
|
|
525
|
+
permissions;
|
|
526
|
+
wallets;
|
|
257
527
|
webhooks;
|
|
258
528
|
// Expose the underlying client for advanced use (custom retries, etc.).
|
|
259
529
|
// Internal callers go through the resource classes instead.
|
|
@@ -262,10 +532,12 @@ var Tally = class {
|
|
|
262
532
|
this.client = new TallyClient(opts);
|
|
263
533
|
this.agents = new AgentsResource(this.client);
|
|
264
534
|
this.payments = new PaymentsResource(this.client);
|
|
265
|
-
this.
|
|
535
|
+
this.permissions = new PermissionsResource(this.client);
|
|
536
|
+
this.wallets = new WalletsResource(this.client);
|
|
537
|
+
this.webhooks = new WebhooksResource(this.client);
|
|
266
538
|
}
|
|
267
539
|
};
|
|
268
540
|
|
|
269
|
-
export { AuthenticationError, ConflictError, NotFoundError, RateLimitError, Tally, TallyError, ValidationError, verifySignature };
|
|
541
|
+
export { AsyncResourcePage, AuthenticationError, ConflictError, NotFoundError, RateLimitError, Tally, TallyError, ValidationError, verifySignature };
|
|
270
542
|
//# sourceMappingURL=index.js.map
|
|
271
543
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/resources/agents.ts","../src/resources/payments.ts","../src/resources/webhooks.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Tally SDK error types. The server returns a consistent shape on every\n// failure — see src/lib/api-errors.ts in the web app — and the SDK turns\n// it into typed exceptions consumers can catch and branch on.\n//\n// Usage in consumer code:\n//\n// try {\n// await tally.agents.upsert({ id: \"research-bot\", display_name: \"...\" });\n// } catch (e) {\n// if (e instanceof AuthenticationError) { /* rotate key */ }\n// if (e instanceof ValidationError) { /* fix input */ }\n// throw e;\n// }\n\nexport type ApiErrorPayload = {\n type: string;\n message: string;\n code?: string;\n details?: unknown;\n};\n\nexport class TallyError extends Error {\n readonly type: string;\n readonly code?: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(opts: {\n type: string;\n message: string;\n status: number;\n code?: string;\n details?: unknown;\n }) {\n super(opts.message);\n this.name = \"TallyError\";\n this.type = opts.type;\n this.code = opts.code;\n this.status = opts.status;\n this.details = opts.details;\n }\n}\n\nexport class AuthenticationError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class NotFoundError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"NotFoundError\";\n }\n}\n\nexport class ValidationError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"ValidationError\";\n }\n}\n\nexport class RateLimitError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"RateLimitError\";\n }\n}\n\nexport class ConflictError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"ConflictError\";\n }\n}\n\n// Maps server error.type → typed exception class.\nexport function makeError(payload: ApiErrorPayload, status: number): TallyError {\n switch (payload.type) {\n case \"unauthenticated\":\n case \"forbidden\":\n return new AuthenticationError(payload, status);\n case \"not_found\":\n return new NotFoundError(payload, status);\n case \"validation_failed\":\n return new ValidationError(payload, status);\n case \"rate_limited\":\n return new RateLimitError(payload, status);\n case \"conflict\":\n return new ConflictError(payload, status);\n default:\n return new TallyError({ ...payload, status });\n }\n}\n","import { makeError, TallyError, type ApiErrorPayload } from \"./errors\";\n\n// Thin HTTP client used by every resource. Centralizes:\n// - Authorization header\n// - JSON request/response handling\n// - Error mapping (server error.type → typed exception)\n//\n// Uses Node 18+'s native fetch. Works in browsers too if you ever want to\n// hit the API from one — though the more common pattern is server-to-server\n// from the consumer's agent infrastructure.\n\nexport type ClientOptions = {\n /** Tally API key (begins with `tly_`). */\n apiKey: string;\n /** Base URL of the Tally API. Defaults to https://app.tallyforagents.com. Override for local dev or self-hosting. */\n baseUrl?: string;\n /** Custom fetch implementation. Useful for tests or non-Node runtimes. */\n fetch?: typeof fetch;\n};\n\nexport class TallyClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly #fetch: typeof fetch;\n\n constructor(opts: ClientOptions) {\n if (\n !opts.apiKey ||\n (!opts.apiKey.startsWith(\"tly_test_\") && !opts.apiKey.startsWith(\"tly_live_\"))\n ) {\n throw new TallyError({\n type: \"validation_failed\",\n message:\n \"Invalid API key. Tally keys start with 'tly_test_' (test mode) or 'tly_live_' (live mode).\",\n status: 0,\n });\n }\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? \"https://app.tallyforagents.com\").replace(/\\/$/, \"\");\n this.#fetch = opts.fetch ?? globalThis.fetch;\n if (typeof this.#fetch !== \"function\") {\n throw new TallyError({\n type: \"internal\",\n message: \"fetch is not available in this runtime. Provide one via `fetch` option.\",\n status: 0,\n });\n }\n }\n\n async request<T>(\n method: \"GET\" | \"POST\" | \"DELETE\",\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const res = await this.#fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (!res.ok) {\n let payload: { error?: ApiErrorPayload } = {};\n try {\n payload = await res.json();\n } catch {\n // Non-JSON error body — fall back to a generic message.\n }\n const err = payload.error ?? {\n type: \"internal\",\n message: `Request failed with status ${res.status}.`,\n };\n throw makeError(err, res.status);\n }\n\n // 204 No Content (e.g., future delete endpoints)\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n}\n","import type { TallyClient } from \"../client\";\n\nexport type AgentWallet = {\n /** Wallet address (lowercased EVM hex) the agent can spend from. */\n address: string;\n /** Friendly name the wallet was given in the dashboard. */\n display_name: string;\n /** Optional role label (e.g., \"main\", \"treasury\"); null if unset. */\n role_label: string | null;\n /** Max per-tx spend in USDC as a decimal string. */\n max_per_tx_usdc: string;\n /** Daily cap in USDC as a decimal string, null if uncapped. */\n daily_cap_usdc: string | null;\n};\n\nexport type Agent = {\n /** Stable user-provided identifier (e.g., \"research-bot\"). */\n id: string;\n /** \"active\" — at least one activated (non-revoked) signer is attached.\n * \"no_permissions\" — no activated signer (may still have pending\n * grants — see pending_signers). */\n status: \"no_permissions\" | \"active\";\n mode: \"test\" | \"live\";\n /** ISO 8601 timestamp. */\n created_at: string;\n /** The account slug this agent belongs to. Surfaced so callers can\n * build dashboard deep-links (e.g., when telling the user to grant\n * a permission). */\n account_slug: string;\n /** Count of non-revoked, activated signers. */\n active_signers: number;\n /** Count of non-revoked, not-yet-activated signers. */\n pending_signers: number;\n /** Wallets this agent has active permission to spend from, with the\n * per-wallet caps from the permission. Empty array if no active\n * grants. Pick one to pass as `wallet` to tally.payments.create(). */\n wallets: AgentWallet[];\n};\n\nexport type AgentUpsertInput = {\n /** User-provided stable string identifier (e.g., \"research-bot\"). */\n id: string;\n};\n\n// agents.upsert / list / get — Phase 2's full surface.\n// Future phases will add: agents.delete, agents.permissions.*, etc.\n\nexport class AgentsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Creates the agent if it doesn't exist; no-op if it does.\n *\n * Scope: the active API key's account + mode. Same id can exist\n * independently in test and live mode for the same account.\n */\n async upsert(input: AgentUpsertInput): Promise<Agent> {\n const { agent } = await this.client.request<{ agent: Agent }>(\n \"POST\",\n \"/v1/agents\",\n input,\n );\n return agent;\n }\n\n async list(): Promise<Agent[]> {\n const { agents } = await this.client.request<{ agents: Agent[] }>(\n \"GET\",\n \"/v1/agents\",\n );\n return agents;\n }\n\n async get(id: string): Promise<Agent> {\n const { agent } = await this.client.request<{ agent: Agent }>(\n \"GET\",\n `/v1/agents/${encodeURIComponent(id)}`,\n );\n return agent;\n }\n}\n","import type { TallyClient } from \"../client\";\n\nexport type Payment = {\n id: string;\n /** \"pending\" until the on-chain tx confirms; updated by a follow-up\n * GET (planned) or webhook (Phase 6). */\n status: \"pending\" | \"confirmed\" | \"failed\";\n /** Transaction hash on the underlying chain. Set as soon as Privy\n * accepts the signed RPC; the SDK does not wait for receipt. */\n tx_hash: string | null;\n amount_usdc: string;\n to: string;\n from: string;\n memo: string | null;\n idempotency_key: string | null;\n created_at: string;\n};\n\nexport type PaymentCreateInput = {\n /** Agent's user-provided id (the externalId), e.g. \"research-bot\". */\n agent_id: string;\n /** Sender wallet address. Must be in this API key's account+mode and\n * must have an active permission grant for the agent. */\n wallet: string;\n /** Recipient EVM address. */\n to: string;\n /** Decimal USDC amount as a string, e.g. \"10\" or \"2.50\". */\n amount_usdc: string;\n /** Optional memo (max 200 chars), stored alongside the transaction. */\n memo?: string;\n /** Optional idempotency key (max 64 chars). Scoped to (account, mode);\n * retries with the same key return the original Payment without\n * resubmitting on-chain. */\n idempotency_key?: string;\n};\n\n// payments.create — single endpoint for Phase 4.\n// Future phases will add: list, get (with chain status refresh), retry.\n\nexport class PaymentsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Submits a USDC payment from one of the agent's granted wallets.\n *\n * Enforcement happens in two layers:\n * 1. Tally checks policy (per-tx max, recipient/contract allowlist,\n * expiry, daily cap) and rejects with a structured error before\n * any Privy call.\n * 2. Privy's secure enclave independently checks the per-tx max and\n * recipient/contract conditions encoded in the signer's policy.\n *\n * Returns immediately with `status: \"pending\"` and the on-chain\n * `tx_hash` once Privy accepts the signed RPC. The SDK does not wait\n * for block confirmation; the Transaction row is updated by a\n * separate confirmation flow (forthcoming).\n */\n async create(input: PaymentCreateInput): Promise<Payment> {\n const { payment } = await this.client.request<{ payment: Payment }>(\n \"POST\",\n \"/v1/payments\",\n input,\n );\n return payment;\n }\n\n /**\n * Fetches the current state of a payment by id. If still pending on\n * Tally's side, this call lazily refreshes from the chain — so polling\n * this is the canonical way to wait for `confirmed` / `failed`.\n */\n async get(id: string): Promise<Payment> {\n const { payment } = await this.client.request<{ payment: Payment }>(\n \"GET\",\n `/v1/payments/${encodeURIComponent(id)}`,\n );\n return payment;\n }\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n// Webhook helpers for SDK consumers. Tally signs every delivery with\n// HMAC-SHA256 over `${timestamp}.${body}` and sends the signature in\n// the `tally-signature` header:\n//\n// tally-signature: t=<unix_seconds>,v1=<hex_lowercase>\n//\n// Receivers MUST verify before trusting any payload — otherwise a\n// known URL is forge-able. Pass the RAW request body (not the\n// JSON-parsed object) so the bytes match what Tally signed.\n//\n// The verifier is intentionally duplicated from the server-side\n// implementation (src/lib/webhook-sign.ts) to keep the SDK\n// self-contained — external consumers shouldn't depend on Tally's\n// server code.\n\nconst SIGNATURE_VERSION = \"v1\";\nexport const DEFAULT_TOLERANCE_SECONDS = 300; // 5 minutes\n\nexport type WebhookVerifyResult =\n | { ok: true }\n | {\n ok: false;\n reason:\n | \"missing_header\"\n | \"malformed_header\"\n | \"no_v1_signature\"\n | \"timestamp_out_of_tolerance\"\n | \"signature_mismatch\";\n };\n\nexport type WebhookVerifyInput = {\n /** Raw request body the receiver got — string, exactly the bytes\n * Tally signed. Don't pass a JSON-parsed object. */\n body: string;\n /** Value of the `tally-signature` header on the incoming request. */\n header: string | null | undefined;\n /** The plaintext signing secret you saved at webhook creation\n * (`whsec_<mode>_<base64url>`). */\n secret: string;\n /** Unix seconds. Defaults to now. Pin for tests. */\n now?: number;\n /** How far the header timestamp may drift from `now`. Defaults to\n * 300s (5 min). Tighten or loosen to taste; the wider the window,\n * the longer a stolen signature stays replayable. */\n toleranceSeconds?: number;\n};\n\nexport class WebhooksResource {\n /**\n * Verify a `tally-signature` header against the request body. Use in\n * your webhook handler before processing the payload.\n *\n * @example\n * ```ts\n * const body = await req.text(); // raw, not JSON-parsed\n * const result = tally.webhooks.verifySignature({\n * body,\n * header: req.headers.get(\"tally-signature\"),\n * secret: process.env.TALLY_WEBHOOK_SECRET!,\n * });\n * if (!result.ok) return new Response(\"invalid signature\", { status: 400 });\n * const event = JSON.parse(body);\n * // ... handle event\n * ```\n */\n verifySignature(input: WebhookVerifyInput): WebhookVerifyResult {\n return verifySignature(input);\n }\n}\n\nexport function verifySignature(input: WebhookVerifyInput): WebhookVerifyResult {\n const { body, header, secret } = input;\n const now = input.now ?? Math.floor(Date.now() / 1000);\n const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n\n if (!header) return { ok: false, reason: \"missing_header\" };\n\n let timestamp: number | null = null;\n let v1: string | null = null;\n for (const part of header.split(\",\")) {\n const [k, v] = part.split(\"=\", 2);\n if (k === \"t\") {\n const n = Number(v);\n if (Number.isFinite(n)) timestamp = n;\n } else if (k === SIGNATURE_VERSION) {\n v1 = v;\n }\n }\n\n if (timestamp === null) return { ok: false, reason: \"malformed_header\" };\n if (!v1) return { ok: false, reason: \"no_v1_signature\" };\n\n if (Math.abs(now - timestamp) > tolerance) {\n return { ok: false, reason: \"timestamp_out_of_tolerance\" };\n }\n\n const expected = createHmac(\"sha256\", secret)\n .update(`${timestamp}.${body}`)\n .digest(\"hex\");\n\n if (expected.length !== v1.length) {\n return { ok: false, reason: \"signature_mismatch\" };\n }\n const a = Buffer.from(expected, \"utf8\");\n const b = Buffer.from(v1, \"utf8\");\n if (!timingSafeEqual(a, b)) {\n return { ok: false, reason: \"signature_mismatch\" };\n }\n return { ok: true };\n}\n","// @tallyforagents/sdk — the financial OS for AI agents.\n//\n// Usage:\n// import { Tally } from \"@tallyforagents/sdk\";\n//\n// const tally = new Tally({ apiKey: process.env.TALLY_API_KEY! });\n// await tally.agents.upsert({ id: \"research-bot\", display_name: \"Research Bot\" });\n\nimport { TallyClient, type ClientOptions } from \"./client\";\nimport { AgentsResource } from \"./resources/agents\";\nimport { PaymentsResource } from \"./resources/payments\";\nimport { WebhooksResource } from \"./resources/webhooks\";\n\nexport class Tally {\n readonly agents: AgentsResource;\n readonly payments: PaymentsResource;\n readonly webhooks: WebhooksResource;\n\n // Expose the underlying client for advanced use (custom retries, etc.).\n // Internal callers go through the resource classes instead.\n private readonly client: TallyClient;\n\n constructor(opts: ClientOptions) {\n this.client = new TallyClient(opts);\n this.agents = new AgentsResource(this.client);\n this.payments = new PaymentsResource(this.client);\n this.webhooks = new WebhooksResource();\n }\n}\n\nexport type { ClientOptions } from \"./client\";\nexport type { Agent, AgentUpsertInput, AgentWallet } from \"./resources/agents\";\nexport type { Payment, PaymentCreateInput } from \"./resources/payments\";\nexport type {\n WebhookVerifyInput,\n WebhookVerifyResult,\n} from \"./resources/webhooks\";\nexport { verifySignature } from \"./resources/webhooks\";\nexport {\n TallyError,\n AuthenticationError,\n NotFoundError,\n ValidationError,\n RateLimitError,\n ConflictError,\n} from \"./errors\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/resources/agents.ts","../src/pagination.ts","../src/resources/payments.ts","../src/resources/permissions.ts","../src/resources/wallets.ts","../src/resources/webhooks.ts","../src/index.ts"],"names":[],"mappings":";;;AAqBO,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EAC3B,IAAA;AAAA,EACA,IAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EAET,YAAY,IAAA,EAMT;AACD,IAAA,KAAA,CAAM,KAAK,OAAO,CAAA;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AACZ,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AAAA,EACtB;AACF;AAEO,IAAM,mBAAA,GAAN,cAAkC,UAAA,CAAW;AAAA,EAClD,WAAA,CAAY,SAA0B,MAAA,EAAgB;AACpD,IAAA,KAAA,CAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAEO,IAAM,aAAA,GAAN,cAA4B,UAAA,CAAW;AAAA,EAC5C,WAAA,CAAY,SAA0B,MAAA,EAAgB;AACpD,IAAA,KAAA,CAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AAAA,EACd;AACF;AAEO,IAAM,eAAA,GAAN,cAA8B,UAAA,CAAW;AAAA,EAC9C,WAAA,CAAY,SAA0B,MAAA,EAAgB;AACpD,IAAA,KAAA,CAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AAEO,IAAM,cAAA,GAAN,cAA6B,UAAA,CAAW;AAAA,EAC7C,WAAA,CAAY,SAA0B,MAAA,EAAgB;AACpD,IAAA,KAAA,CAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AAAA,EACd;AACF;AAEO,IAAM,aAAA,GAAN,cAA4B,UAAA,CAAW;AAAA,EAC5C,WAAA,CAAY,SAA0B,MAAA,EAAgB;AACpD,IAAA,KAAA,CAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,CAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AAAA,EACd;AACF;AAGO,SAAS,SAAA,CAAU,SAA0B,MAAA,EAA4B;AAC9E,EAAA,QAAQ,QAAQ,IAAA;AAAM,IACpB,KAAK,iBAAA;AAAA,IACL,KAAK,WAAA;AACH,MAAA,OAAO,IAAI,mBAAA,CAAoB,OAAA,EAAS,MAAM,CAAA;AAAA,IAChD,KAAK,WAAA;AACH,MAAA,OAAO,IAAI,aAAA,CAAc,OAAA,EAAS,MAAM,CAAA;AAAA,IAC1C,KAAK,mBAAA;AACH,MAAA,OAAO,IAAI,eAAA,CAAgB,OAAA,EAAS,MAAM,CAAA;AAAA,IAC5C,KAAK,cAAA;AACH,MAAA,OAAO,IAAI,cAAA,CAAe,OAAA,EAAS,MAAM,CAAA;AAAA,IAC3C,KAAK,UAAA;AACH,MAAA,OAAO,IAAI,aAAA,CAAc,OAAA,EAAS,MAAM,CAAA;AAAA,IAC1C;AACE,MAAA,OAAO,IAAI,UAAA,CAAW,EAAE,GAAG,OAAA,EAAS,QAAQ,CAAA;AAAA;AAElD;;;ACvFA,IAAM,eAAA,uBAAsB,GAAA,EAAY;AACxC,SAAS,gBAAA,CAAiB,KAAa,KAAA,EAAe;AACpD,EAAA,MAAM,EAAA,GAAK,GAAG,GAAA,CAAI,KAAA,CAAM,GAAG,EAAE,CAAC,IAAI,KAAK,CAAA,CAAA;AACvC,EAAA,IAAI,eAAA,CAAgB,GAAA,CAAI,EAAE,CAAA,EAAG;AAC7B,EAAA,eAAA,CAAgB,IAAI,EAAE,CAAA;AACtB,EAAA,MAAM,aAAa,MAAM;AACvB,IAAA,MAAM,EAAA,GAAK,IAAI,IAAA,CAAK,KAAK,EAAE,OAAA,EAAQ,GAAI,KAAK,GAAA,EAAI;AAChD,IAAA,IAAI,EAAA,IAAM,GAAG,OAAO,SAAA;AACpB,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,IAAS,CAAA;AACnC,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAO,EAAA,GAAK,OAAa,GAAM,CAAA;AAC9C,IAAA,OAAO,CAAA,GAAI,IAAI,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA,CAAA,CAAA,GAAM,GAAG,CAAC,CAAA,CAAA,CAAA;AAAA,EACrC,CAAA,GAAG;AAGH,EAAA,OAAA,CAAQ,IAAA;AAAA,IACN,CAAA,0DAAA,EAA6D,SAAS,CAAA,KAAA,EAAQ,KAAK,CAAA,uDAAA;AAAA,GACrF;AACF;AAoBO,IAAM,cAAN,MAAkB;AAAA,EACd,MAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EAET,YAAY,IAAA,EAAqB;AAC/B,IAAA,IACE,CAAC,IAAA,CAAK,MAAA,IACL,CAAC,KAAK,MAAA,CAAO,UAAA,CAAW,WAAW,CAAA,IAAK,CAAC,IAAA,CAAK,MAAA,CAAO,UAAA,CAAW,WAAW,CAAA,EAC5E;AACA,MAAA,MAAM,IAAI,UAAA,CAAW;AAAA,QACnB,IAAA,EAAM,mBAAA;AAAA,QACN,OAAA,EACE,4FAAA;AAAA,QACF,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH;AACA,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,WAAW,IAAA,CAAK,OAAA,IAAW,gCAAA,EAAkC,OAAA,CAAQ,OAAO,EAAE,CAAA;AACnF,IAAA,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,KAAA,IAAS,UAAA,CAAW,KAAA;AACvC,IAAA,IAAI,OAAO,IAAA,CAAK,MAAA,KAAW,UAAA,EAAY;AACrC,MAAA,MAAM,IAAI,UAAA,CAAW;AAAA,QACnB,IAAA,EAAM,UAAA;AAAA,QACN,OAAA,EAAS,yEAAA;AAAA,QACT,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,OAAA,CACJ,MAAA,EACA,IAAA,EACA,IAAA,EACY;AACZ,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,GAAG,IAAI,CAAA,CAAA;AAClC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,EAAK;AAAA,MACjC,MAAA;AAAA,MACA,OAAA,EAAS;AAAA,QACP,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,QACpC,cAAA,EAAgB,kBAAA;AAAA,QAChB,MAAA,EAAQ;AAAA,OACV;AAAA,MACA,MAAM,IAAA,KAAS,MAAA,GAAY,MAAA,GAAY,IAAA,CAAK,UAAU,IAAI;AAAA,KAC3D,CAAA;AAKD,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,4BAA4B,CAAA;AAC/D,IAAA,IAAI,UAAA,EAAY,gBAAA,CAAiB,IAAA,CAAK,MAAA,EAAQ,UAAU,CAAA;AAExD,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,IAAI,UAAuC,EAAC;AAC5C,MAAA,IAAI;AACF,QAAA,OAAA,GAAU,MAAM,IAAI,IAAA,EAAK;AAAA,MAC3B,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,MAAM,GAAA,GAAM,QAAQ,KAAA,IAAS;AAAA,QAC3B,IAAA,EAAM,UAAA;AAAA,QACN,OAAA,EAAS,CAAA,2BAAA,EAA8B,GAAA,CAAI,MAAM,CAAA,CAAA;AAAA,OACnD;AACA,MAAA,MAAM,SAAA,CAAU,GAAA,EAAK,GAAA,CAAI,MAAM,CAAA;AAAA,IACjC;AAGA,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,EAAK,OAAO,MAAA;AAC/B,IAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAAA,EACzB;AACF,CAAA;;;ACnEO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,YAA6B,MAAA,EAAqB;AAArB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAsB;AAAA,EAAtB,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ7B,MAAM,OAAO,KAAA,EAAyC;AACpD,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,KAAK,MAAA,CAAO,OAAA;AAAA,MAClC,MAAA;AAAA,MACA,YAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,MAAM,IAAA,GAAyB;AAC7B,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,MAAM,KAAK,MAAA,CAAO,OAAA;AAAA,MACnC,KAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,EAAA,EAA4B;AACpC,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,KAAK,MAAA,CAAO,OAAA;AAAA,MAClC,KAAA;AAAA,MACA,CAAA,WAAA,EAAc,kBAAA,CAAmB,EAAE,CAAC,CAAA;AAAA,KACtC;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,OAAO,EAAA,EAA2B;AACtC,IAAA,MAAM,KAAK,MAAA,CAAO,OAAA;AAAA,MAChB,QAAA;AAAA,MACA,CAAA,WAAA,EAAc,kBAAA,CAAmB,EAAE,CAAC,CAAA;AAAA,KACtC;AAAA,EACF;AACF,CAAA;;;AClFO,IAAM,oBAAN,MAAuD;AAAA,EAC5D,YAA6B,SAAA,EAA2B;AAA3B,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AAAA,EAA4B;AAAA,EAA5B,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY7B,QAAQ,MAAA,CAAO,aAAa,CAAA,GAAuB;AACjD,IAAA,IAAI,MAAA,GAAwB,IAAA;AAC5B,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA;AACxC,MAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,CAAK,IAAA,EAAM,MAAM,IAAA;AACpC,MAAA,IAAI,CAAC,KAAK,WAAA,EAAa;AACvB,MAAA,MAAA,GAAS,IAAA,CAAK,WAAA;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,QAAQ,QAAA,EAAiC;AAC7C,IAAA,MAAM,MAAW,EAAC;AAClB,IAAA,WAAA,MAAiB,QAAQ,IAAA,EAAM;AAC7B,MAAA,GAAA,CAAI,KAAK,IAAI,CAAA;AACb,MAAA,IAAI,QAAA,KAAa,MAAA,IAAa,GAAA,CAAI,MAAA,IAAU,QAAA,EAAU;AAAA,IACxD;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,SAAA,GAAgE;AACpE,IAAA,OAAO,IAAA,CAAK,UAAU,IAAI,CAAA;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UACJ,MAAA,EACoD;AACpD,IAAA,OAAO,IAAA,CAAK,UAAU,MAAM,CAAA;AAAA,EAC9B;AACF;;;ACNO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YAA6B,MAAA,EAAqB;AAArB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAsB;AAAA,EAAtB,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiB7B,MAAM,OAAO,KAAA,EAA6C;AACxD,IAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,KAAK,MAAA,CAAO,OAAA;AAAA,MACpC,MAAA;AAAA,MACA,cAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IAAI,EAAA,EAA8B;AACtC,IAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,KAAK,MAAA,CAAO,OAAA;AAAA,MACpC,KAAA;AAAA,MACA,CAAA,aAAA,EAAgB,kBAAA,CAAmB,EAAE,CAAC,CAAA;AAAA,KACxC;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,IAAA,CAAK,OAAA,GAA8B,EAAC,EAA+B;AACjE,IAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,WAAW,QAAA,EAAU,MAAA,EAAQ,GAAE,GAAI,OAAA;AAC1D,IAAA,OAAO,IAAI,iBAAA,CAA2B,OAAO,MAAA,KAAW;AACtD,MAAA,MAAM,EAAA,GAAK,IAAI,eAAA,EAAgB;AAC/B,MAAA,IAAI,MAAA,EAAQ,EAAA,CAAG,GAAA,CAAI,QAAA,EAAU,MAAM,CAAA;AACnC,MAAA,IAAI,UAAU,MAAA,EAAW,EAAA,CAAG,IAAI,OAAA,EAAS,MAAA,CAAO,KAAK,CAAC,CAAA;AACtD,MAAA,IAAI,MAAA,EAAQ,EAAA,CAAG,GAAA,CAAI,QAAA,EAAU,MAAM,CAAA;AACnC,MAAA,IAAI,SAAA,EAAW,EAAA,CAAG,GAAA,CAAI,WAAA,EAAa,SAAS,CAAA;AAC5C,MAAA,IAAI,QAAA,EAAU,EAAA,CAAG,GAAA,CAAI,UAAA,EAAY,QAAQ,CAAA;AACzC,MAAA,IAAI,MAAA,EAAQ,EAAA,CAAG,GAAA,CAAI,QAAA,EAAU,MAAM,CAAA;AACnC,MAAA,IAAI,CAAA,EAAG,EAAA,CAAG,GAAA,CAAI,GAAA,EAAK,CAAC,CAAA;AACpB,MAAA,MAAM,KAAA,GAAQ,GAAG,QAAA,EAAS;AAC1B,MAAA,MAAM,IAAA,GAAO,KAAA,GAAQ,CAAA,aAAA,EAAgB,KAAK,CAAA,CAAA,GAAK,cAAA;AAC/C,MAAA,OAAO,KAAK,MAAA,CAAO,OAAA;AAAA,QACjB,KAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AACF,CAAA;;;AC/GO,IAAM,sBAAN,MAA0B;AAAA,EAC/B,YAA6B,MAAA,EAAqB;AAArB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAsB;AAAA,EAAtB,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiB7B,IAAA,CAAK,OAAA,GAAiC,EAAC,EAAkC;AACvE,IAAA,MAAM,EAAE,KAAA,EAAO,QAAA,EAAS,GAAI,OAAA;AAC5B,IAAA,OAAO,IAAI,iBAAA,CAA8B,OAAO,MAAA,KAAW;AACzD,MAAA,MAAM,EAAA,GAAK,IAAI,eAAA,EAAgB;AAC/B,MAAA,IAAI,MAAA,EAAQ,EAAA,CAAG,GAAA,CAAI,QAAA,EAAU,MAAM,CAAA;AACnC,MAAA,IAAI,UAAU,MAAA,EAAW,EAAA,CAAG,IAAI,OAAA,EAAS,MAAA,CAAO,KAAK,CAAC,CAAA;AACtD,MAAA,IAAI,QAAA,EAAU,EAAA,CAAG,GAAA,CAAI,UAAA,EAAY,QAAQ,CAAA;AACzC,MAAA,MAAM,KAAA,GAAQ,GAAG,QAAA,EAAS;AAC1B,MAAA,MAAM,IAAA,GAAO,KAAA,GAAQ,CAAA,gBAAA,EAAmB,KAAK,CAAA,CAAA,GAAK,iBAAA;AAClD,MAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAGhB,KAAA,EAAO,IAAI,CAAA;AAAA,IAChB,CAAC,CAAA;AAAA,EACH;AACF,CAAA;;;AClDO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAA6B,MAAA,EAAqB;AAArB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAsB;AAAA,EAAtB,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7B,MAAM,IAAA,GAA0B;AAC9B,IAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,KAAK,MAAA,CAAO,OAAA;AAAA,MACpC,KAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,KAAA,EAA2C;AACtD,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,MAAM,KAAK,MAAA,CAAO,OAAA;AAAA,MACnC,MAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAA;ACpCA,IAAM,iBAAA,GAAoB,IAAA;AACnB,IAAM,yBAAA,GAA4B,GAAA;AAuElC,IAAM,mBAAN,MAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO5B,YAA6B,MAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAuB;AAAA,EAAvB,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmB7B,gBAAgB,KAAA,EAAgD;AAC9D,IAAA,OAAO,gBAAgB,KAAK,CAAA;AAAA,EAC9B;AAAA,EAEQ,aAAA,GAA6B;AACnC,IAAA,IAAI,CAAC,KAAK,MAAA,EAAQ;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,IAAA,GAA2B;AAC/B,IAAA,MAAM,EAAE,UAAS,GAAI,MAAM,KAAK,aAAA,EAAc,CAAE,OAAA,CAE7C,KAAA,EAAO,cAAc,CAAA;AACxB,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,OAAO,KAAA,EAAoD;AAC/D,IAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,IAAA,CAAK,eAAc,CAAE,OAAA,CAE5C,MAAA,EAAQ,cAAA,EAAgB,KAAK,CAAA;AAChC,IAAA,OAAO,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,OAAO,EAAA,EAA8B;AACzC,IAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,IAAA,CAAK,aAAA,EAAc,CAAE,OAAA,CAE5C,MAAA,EAAQ,CAAA,aAAA,EAAgB,kBAAA,CAAmB,EAAE,CAAC,CAAA,OAAA,CAAS,CAAA;AAC1D,IAAA,OAAO,OAAA;AAAA,EACT;AACF,CAAA;AAEO,SAAS,gBAAgB,KAAA,EAAgD;AAC9E,EAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAO,GAAI,KAAA;AACjC,EAAA,MAAM,GAAA,GAAM,MAAM,GAAA,IAAO,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AACrD,EAAA,MAAM,SAAA,GAAY,MAAM,gBAAA,IAAoB,yBAAA;AAE5C,EAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,gBAAA,EAAiB;AAE1D,EAAA,IAAI,SAAA,GAA2B,IAAA;AAC/B,EAAA,IAAI,EAAA,GAAoB,IAAA;AACxB,EAAA,KAAA,MAAW,IAAA,IAAQ,MAAA,CAAO,KAAA,CAAM,GAAG,CAAA,EAAG;AACpC,IAAA,MAAM,CAAC,CAAA,EAAG,CAAC,IAAI,IAAA,CAAK,KAAA,CAAM,KAAK,CAAC,CAAA;AAChC,IAAA,IAAI,MAAM,GAAA,EAAK;AACb,MAAA,MAAM,CAAA,GAAI,OAAO,CAAC,CAAA;AAClB,MAAA,IAAI,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,EAAG,SAAA,GAAY,CAAA;AAAA,IACtC,CAAA,MAAA,IAAW,MAAM,iBAAA,EAAmB;AAClC,MAAA,EAAA,GAAK,CAAA;AAAA,IACP;AAAA,EACF;AAEA,EAAA,IAAI,cAAc,IAAA,EAAM,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,kBAAA,EAAmB;AACvE,EAAA,IAAI,CAAC,EAAA,EAAI,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,iBAAA,EAAkB;AAEvD,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,GAAA,GAAM,SAAS,IAAI,SAAA,EAAW;AACzC,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,4BAAA,EAA6B;AAAA,EAC3D;AAEA,EAAA,MAAM,QAAA,GAAW,UAAA,CAAW,QAAA,EAAU,MAAM,CAAA,CACzC,MAAA,CAAO,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAA,CAC7B,OAAO,KAAK,CAAA;AAEf,EAAA,IAAI,QAAA,CAAS,MAAA,KAAW,EAAA,CAAG,MAAA,EAAQ;AACjC,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,oBAAA,EAAqB;AAAA,EACnD;AACA,EAAA,MAAM,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,QAAA,EAAU,MAAM,CAAA;AACtC,EAAA,MAAM,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,EAAA,EAAI,MAAM,CAAA;AAChC,EAAA,IAAI,CAAC,eAAA,CAAgB,CAAA,EAAG,CAAC,CAAA,EAAG;AAC1B,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,oBAAA,EAAqB;AAAA,EACnD;AACA,EAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AACpB;;;AC/MO,IAAM,QAAN,MAAY;AAAA,EACR,MAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA;AAAA;AAAA,EAIQ,MAAA;AAAA,EAEjB,YAAY,IAAA,EAAqB;AAC/B,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,WAAA,CAAY,IAAI,CAAA;AAClC,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,cAAA,CAAe,IAAA,CAAK,MAAM,CAAA;AAC5C,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,gBAAA,CAAiB,IAAA,CAAK,MAAM,CAAA;AAChD,IAAA,IAAA,CAAK,WAAA,GAAc,IAAI,mBAAA,CAAoB,IAAA,CAAK,MAAM,CAAA;AACtD,IAAA,IAAA,CAAK,OAAA,GAAU,IAAI,eAAA,CAAgB,IAAA,CAAK,MAAM,CAAA;AAC9C,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,gBAAA,CAAiB,IAAA,CAAK,MAAM,CAAA;AAAA,EAClD;AACF","file":"index.js","sourcesContent":["// Tally SDK error types. The server returns a consistent shape on every\n// failure — see src/lib/api-errors.ts in the web app — and the SDK turns\n// it into typed exceptions consumers can catch and branch on.\n//\n// Usage in consumer code:\n//\n// try {\n// await tally.agents.upsert({ id: \"research-bot\", display_name: \"...\" });\n// } catch (e) {\n// if (e instanceof AuthenticationError) { /* rotate key */ }\n// if (e instanceof ValidationError) { /* fix input */ }\n// throw e;\n// }\n\nexport type ApiErrorPayload = {\n type: string;\n message: string;\n code?: string;\n details?: unknown;\n};\n\nexport class TallyError extends Error {\n readonly type: string;\n readonly code?: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(opts: {\n type: string;\n message: string;\n status: number;\n code?: string;\n details?: unknown;\n }) {\n super(opts.message);\n this.name = \"TallyError\";\n this.type = opts.type;\n this.code = opts.code;\n this.status = opts.status;\n this.details = opts.details;\n }\n}\n\nexport class AuthenticationError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class NotFoundError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"NotFoundError\";\n }\n}\n\nexport class ValidationError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"ValidationError\";\n }\n}\n\nexport class RateLimitError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"RateLimitError\";\n }\n}\n\nexport class ConflictError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"ConflictError\";\n }\n}\n\n// Maps server error.type → typed exception class.\nexport function makeError(payload: ApiErrorPayload, status: number): TallyError {\n switch (payload.type) {\n case \"unauthenticated\":\n case \"forbidden\":\n return new AuthenticationError(payload, status);\n case \"not_found\":\n return new NotFoundError(payload, status);\n case \"validation_failed\":\n return new ValidationError(payload, status);\n case \"rate_limited\":\n return new RateLimitError(payload, status);\n case \"conflict\":\n return new ConflictError(payload, status);\n default:\n return new TallyError({ ...payload, status });\n }\n}\n","import { makeError, TallyError, type ApiErrorPayload } from \"./errors\";\n\n// One-shot warning when the API key being used is in its rotation\n// grace window. The server sets `Tally-Rotation-Grace-Until` on every\n// /v1/* response when grace is active; we surface it on stderr so the\n// developer notices before the key actually expires. Throttled to\n// once per process per (key + grace_until) tuple so high-volume\n// callers don't get spammed.\nconst warnedRotations = new Set<string>();\nfunction warnRotationOnce(key: string, until: string) {\n const id = `${key.slice(0, 12)}:${until}`;\n if (warnedRotations.has(id)) return;\n warnedRotations.add(id);\n const remaining = (() => {\n const ms = new Date(until).getTime() - Date.now();\n if (ms <= 0) return \"shortly\";\n const h = Math.floor(ms / 3_600_000);\n const m = Math.floor((ms % 3_600_000) / 60_000);\n return h > 0 ? `${h}h ${m}m` : `${m}m`;\n })();\n // Use stderr so it doesn't pollute the program's normal output.\n // eslint-disable-next-line no-console\n console.warn(\n `[tally] API key is in its rotation grace window (ends in ~${remaining}, at ${until}). Switch to the rotated key before then to avoid 401s.`,\n );\n}\n\n// Thin HTTP client used by every resource. Centralizes:\n// - Authorization header\n// - JSON request/response handling\n// - Error mapping (server error.type → typed exception)\n//\n// Uses Node 18+'s native fetch. Works in browsers too if you ever want to\n// hit the API from one — though the more common pattern is server-to-server\n// from the consumer's agent infrastructure.\n\nexport type ClientOptions = {\n /** Tally API key (begins with `tly_`). */\n apiKey: string;\n /** Base URL of the Tally API. Defaults to https://app.tallyforagents.com. Override for local dev or self-hosting. */\n baseUrl?: string;\n /** Custom fetch implementation. Useful for tests or non-Node runtimes. */\n fetch?: typeof fetch;\n};\n\nexport class TallyClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly #fetch: typeof fetch;\n\n constructor(opts: ClientOptions) {\n if (\n !opts.apiKey ||\n (!opts.apiKey.startsWith(\"tly_test_\") && !opts.apiKey.startsWith(\"tly_live_\"))\n ) {\n throw new TallyError({\n type: \"validation_failed\",\n message:\n \"Invalid API key. Tally keys start with 'tly_test_' (test mode) or 'tly_live_' (live mode).\",\n status: 0,\n });\n }\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? \"https://app.tallyforagents.com\").replace(/\\/$/, \"\");\n this.#fetch = opts.fetch ?? globalThis.fetch;\n if (typeof this.#fetch !== \"function\") {\n throw new TallyError({\n type: \"internal\",\n message: \"fetch is not available in this runtime. Provide one via `fetch` option.\",\n status: 0,\n });\n }\n }\n\n async request<T>(\n method: \"GET\" | \"POST\" | \"DELETE\",\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const res = await this.#fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n // Surface rotation-grace warning on every response (success or\n // error) where the server attached the header. Server sets it on\n // post-auth responses; absent header = no rotation in progress.\n const graceUntil = res.headers.get(\"tally-rotation-grace-until\");\n if (graceUntil) warnRotationOnce(this.apiKey, graceUntil);\n\n if (!res.ok) {\n let payload: { error?: ApiErrorPayload } = {};\n try {\n payload = await res.json();\n } catch {\n // Non-JSON error body — fall back to a generic message.\n }\n const err = payload.error ?? {\n type: \"internal\",\n message: `Request failed with status ${res.status}.`,\n };\n throw makeError(err, res.status);\n }\n\n // 204 No Content (e.g., future delete endpoints)\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n}\n","import type { TallyClient } from \"../client\";\n\nexport type AgentWallet = {\n /** Wallet address (lowercased EVM hex) the agent can spend from. */\n address: string;\n /** Friendly name the wallet was given in the dashboard. */\n display_name: string;\n /** Optional role label (e.g., \"main\", \"treasury\"); null if unset. */\n role_label: string | null;\n /** Max per-tx spend in USDC as a decimal string. */\n max_per_tx_usdc: string;\n /** Daily cap in USDC as a decimal string, null if uncapped. */\n daily_cap_usdc: string | null;\n};\n\nexport type Agent = {\n /** Stable user-provided identifier (e.g., \"research-bot\"). */\n id: string;\n /** \"active\" — at least one activated (non-revoked) signer is attached.\n * \"no_permissions\" — no activated signer (may still have pending\n * grants — see pending_signers). */\n status: \"no_permissions\" | \"active\";\n mode: \"test\" | \"live\";\n /** ISO 8601 timestamp. */\n created_at: string;\n /** The account slug this agent belongs to. Surfaced so callers can\n * build dashboard deep-links (e.g., when telling the user to grant\n * a permission). */\n account_slug: string;\n /** Count of non-revoked, activated signers. */\n active_signers: number;\n /** Count of non-revoked, not-yet-activated signers. */\n pending_signers: number;\n /** Wallets this agent has active permission to spend from, with the\n * per-wallet caps from the permission. Empty array if no active\n * grants. Pick one to pass as `wallet` to tally.payments.create(). */\n wallets: AgentWallet[];\n};\n\nexport type AgentUpsertInput = {\n /** User-provided stable string identifier (e.g., \"research-bot\"). */\n id: string;\n};\n\n// agents.upsert / list / get — Phase 2's full surface.\n// Future phases will add: agents.delete, agents.permissions.*, etc.\n\nexport class AgentsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Creates the agent if it doesn't exist; no-op if it does.\n *\n * Scope: the active API key's account + mode. Same id can exist\n * independently in test and live mode for the same account.\n */\n async upsert(input: AgentUpsertInput): Promise<Agent> {\n const { agent } = await this.client.request<{ agent: Agent }>(\n \"POST\",\n \"/v1/agents\",\n input,\n );\n return agent;\n }\n\n async list(): Promise<Agent[]> {\n const { agents } = await this.client.request<{ agents: Agent[] }>(\n \"GET\",\n \"/v1/agents\",\n );\n return agents;\n }\n\n async get(id: string): Promise<Agent> {\n const { agent } = await this.client.request<{ agent: Agent }>(\n \"GET\",\n `/v1/agents/${encodeURIComponent(id)}`,\n );\n return agent;\n }\n\n /**\n * Soft-deletes an agent. The row stays in the DB so historical\n * transactions keep their attribution; future list/get responses\n * hide it.\n *\n * Throws `ConflictError` (HTTP 409, `code: \"has_active_grants\"`) if\n * the agent has any active permissions — revoke them first.\n *\n * Re-creating an agent with the same `id` via `upsert()` is the\n * supported way to bring a deleted agent back.\n */\n async delete(id: string): Promise<void> {\n await this.client.request<{ deleted: boolean; id: string }>(\n \"DELETE\",\n `/v1/agents/${encodeURIComponent(id)}`,\n );\n }\n}\n","// Stripe-style auto-paginating list result. Implements `AsyncIterable`\n// so callers can `for await (const item of list) { ... }` and the SDK\n// transparently fetches subsequent pages as iteration consumes items.\n//\n// Server contract: each fetch returns `{ data: T[], next_cursor: string | null }`.\n// When `next_cursor` is null, the iterator stops.\n//\n// Used by:\n// - `tally.payments.list()`\n// - `tally.permissions.list()` (Phase 2)\n// - any future v1 list endpoint that follows the same shape\n\nexport interface PageFetcher<T> {\n (cursor: string | null): Promise<{ data: T[]; next_cursor: string | null }>;\n}\n\nexport class AsyncResourcePage<T> implements AsyncIterable<T> {\n constructor(private readonly fetchPage: PageFetcher<T>) {}\n\n /**\n * Async-iterates every item across all pages. Stops when the server\n * returns `next_cursor: null`.\n *\n * ```ts\n * for await (const p of tally.payments.list({ status: \"confirmed\" })) {\n * console.log(p.id);\n * }\n * ```\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<T> {\n let cursor: string | null = null;\n while (true) {\n const page = await this.fetchPage(cursor);\n for (const item of page.data) yield item;\n if (!page.next_cursor) break;\n cursor = page.next_cursor;\n }\n }\n\n /**\n * Collects items into an array, optionally bounded. `toArray()` with\n * no argument pulls every page; `toArray(20)` stops after the first\n * 20 items (across whatever page boundaries that crosses).\n *\n * ```ts\n * const recent = await tally.payments.list().toArray(20);\n * ```\n */\n async toArray(maxItems?: number): Promise<T[]> {\n const out: T[] = [];\n for await (const item of this) {\n out.push(item);\n if (maxItems !== undefined && out.length >= maxItems) break;\n }\n return out;\n }\n\n /**\n * Returns just the first page (no auto-pagination). Use when the\n * cursor itself matters — e.g., persisting it across runs to resume\n * iteration later.\n *\n * ```ts\n * const { data, next_cursor } = await tally.payments.list().firstPage();\n * // … store next_cursor; next run: tally.payments.list().pageAfter(saved_cursor)\n * ```\n */\n async firstPage(): Promise<{ data: T[]; next_cursor: string | null }> {\n return this.fetchPage(null);\n }\n\n /**\n * Returns the page that follows the given cursor. Useful for resuming\n * iteration from a previously-persisted cursor, or for manual paging\n * when the auto-iterator's eager fetching isn't a fit.\n */\n async pageAfter(\n cursor: string,\n ): Promise<{ data: T[]; next_cursor: string | null }> {\n return this.fetchPage(cursor);\n }\n}\n","import type { TallyClient } from \"../client\";\nimport { AsyncResourcePage } from \"../pagination\";\n\nexport type Payment = {\n id: string;\n /** \"pending\" until the on-chain tx confirms; updated by a follow-up\n * GET or webhook. */\n status: \"pending\" | \"confirmed\" | \"failed\";\n /** Transaction hash on the underlying chain. Set as soon as Privy\n * accepts the signed RPC; the SDK does not wait for receipt. */\n tx_hash: string | null;\n amount_usdc: string;\n /** Destination address. For outbound, this is the external recipient.\n * For inbound, this is one of your wallets. */\n to: string;\n /** Source address. For outbound, this is your wallet. For inbound,\n * this is the external sender. */\n from: string;\n memo: string | null;\n idempotency_key: string | null;\n created_at: string;\n /** \"inbound\" or \"outbound\". Populated on list responses; may be\n * undefined on responses from `create()` or `get()` (which always\n * describe outbound payments). */\n direction?: \"inbound\" | \"outbound\";\n /** Your wallet's address — always one of YOUR wallets regardless of\n * direction. Same as `from` for outbound, `to` for inbound. */\n wallet_address?: string;\n /** Friendly name of `wallet_address`. */\n wallet_display_name?: string;\n /** The agent's externalId for outbound payments; null for inbound. */\n agent_id?: string | null;\n /** Block timestamp from the chain, set after the tx confirms. */\n block_timestamp?: string | null;\n /** Human-readable reason for `status: \"failed\"` rows. */\n error_reason?: string | null;\n};\n\nexport type PaymentListFilters = {\n /** Filter by payment status. */\n status?: \"pending\" | \"confirmed\" | \"failed\";\n /** Filter by direction. */\n direction?: \"inbound\" | \"outbound\";\n /** Filter by agent (external id, the user-provided string). */\n agent_id?: string;\n /** Filter by wallet address (case-insensitive). */\n wallet?: string;\n /** Free-text search over tx hash, addresses, memo. */\n q?: string;\n /** Max items per page. Default 50, max 100. The auto-iterator uses\n * this for each page fetch. */\n limit?: number;\n};\n\nexport type PaymentCreateInput = {\n /** Agent's user-provided id (the externalId), e.g. \"research-bot\". */\n agent_id: string;\n /** Sender wallet address. Must be in this API key's account+mode and\n * must have an active permission grant for the agent. */\n wallet: string;\n /** Recipient EVM address. */\n to: string;\n /** Decimal USDC amount as a string, e.g. \"10\" or \"2.50\". */\n amount_usdc: string;\n /** Optional memo (max 200 chars), stored alongside the transaction. */\n memo?: string;\n /** Optional idempotency key (max 64 chars). Scoped to (account, mode);\n * retries with the same key return the original Payment without\n * resubmitting on-chain. */\n idempotency_key?: string;\n};\n\n// payments.create — single endpoint for Phase 4.\n// Future phases will add: list, get (with chain status refresh), retry.\n\nexport class PaymentsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Submits a USDC payment from one of the agent's granted wallets.\n *\n * Enforcement happens in two layers:\n * 1. Tally checks policy (per-tx max, recipient/contract allowlist,\n * expiry, daily cap) and rejects with a structured error before\n * any Privy call.\n * 2. Privy's secure enclave independently checks the per-tx max and\n * recipient/contract conditions encoded in the signer's policy.\n *\n * Returns immediately with `status: \"pending\"` and the on-chain\n * `tx_hash` once Privy accepts the signed RPC. The SDK does not wait\n * for block confirmation; the Transaction row is updated by a\n * separate confirmation flow (forthcoming).\n */\n async create(input: PaymentCreateInput): Promise<Payment> {\n const { payment } = await this.client.request<{ payment: Payment }>(\n \"POST\",\n \"/v1/payments\",\n input,\n );\n return payment;\n }\n\n /**\n * Fetches the current state of a payment by id. If still pending on\n * Tally's side, this call lazily refreshes from the chain — so polling\n * this is the canonical way to wait for `confirmed` / `failed`.\n */\n async get(id: string): Promise<Payment> {\n const { payment } = await this.client.request<{ payment: Payment }>(\n \"GET\",\n `/v1/payments/${encodeURIComponent(id)}`,\n );\n return payment;\n }\n\n /**\n * Lists payments in the API key's account + mode, auto-paginated.\n * Returns an `AsyncResourcePage` you can `for await` over to iterate\n * every payment, or call `.toArray(n)` for a bounded read.\n *\n * Pending outbound rows are lazily refreshed from the chain on read,\n * matching the dashboard's behavior — polling `list({ status: \"pending\" })`\n * is a valid way to wait for confirmation.\n *\n * ```ts\n * for await (const p of tally.payments.list({ status: \"confirmed\" })) {\n * console.log(p.id, p.amount_usdc, p.to);\n * }\n *\n * // Or bounded:\n * const last20 = await tally.payments.list({ direction: \"outbound\" }).toArray(20);\n * ```\n */\n list(filters: PaymentListFilters = {}): AsyncResourcePage<Payment> {\n const { limit, status, direction, agent_id, wallet, q } = filters;\n return new AsyncResourcePage<Payment>(async (cursor) => {\n const qs = new URLSearchParams();\n if (cursor) qs.set(\"cursor\", cursor);\n if (limit !== undefined) qs.set(\"limit\", String(limit));\n if (status) qs.set(\"status\", status);\n if (direction) qs.set(\"direction\", direction);\n if (agent_id) qs.set(\"agent_id\", agent_id);\n if (wallet) qs.set(\"wallet\", wallet);\n if (q) qs.set(\"q\", q);\n const query = qs.toString();\n const path = query ? `/v1/payments?${query}` : \"/v1/payments\";\n return this.client.request<{ data: Payment[]; next_cursor: string | null }>(\n \"GET\",\n path,\n );\n });\n }\n}\n","import type { TallyClient } from \"../client\";\nimport { AsyncResourcePage } from \"../pagination\";\n\nexport type Permission = {\n /** Stable cuid for this grant. Use as the reference id if/when revoke\n * or update endpoints land. */\n id: string;\n /** Agent's user-provided externalId (e.g., \"research-bot\"). */\n agent_id: string;\n /** Wallet address this grant is on. */\n wallet_address: string;\n /** Friendly wallet name set at provisioning. */\n wallet_display_name: string;\n /** \"active\" once the wallet owner has authorized the signer on-chain;\n * \"pending\" before that. */\n status: \"active\" | \"pending\";\n max_per_tx_usdc: string;\n daily_cap_usdc: string | null;\n /** USDC already moved by this grant today (UTC). */\n used_today_usdc: string;\n /** daily_cap_usdc minus used_today_usdc, or null if uncapped. Clamped\n * to 0 when over-spent (shouldn't happen — daily-cap enforcement is\n * pre-broadcast — but defensively non-negative). */\n remaining_today_usdc: string | null;\n /** Whitelisted recipient addresses. Empty = unrestricted. */\n recipient_allowlist: string[];\n /** Whitelisted contract addresses. Defaults to the mode's USDC contract. */\n contract_allowlist: string[];\n /** ISO 8601 timestamp; null if no expiry. */\n expires_at: string | null;\n created_at: string;\n activated_at: string | null;\n};\n\nexport type PermissionListFilters = {\n /** Filter by agent (external id). */\n agent_id?: string;\n /** Max items per page. Default 50, max 100. */\n limit?: number;\n};\n\nexport class PermissionsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Lists active grants in the API key's account + mode, auto-paginated.\n * Returns an `AsyncResourcePage` you can `for await` over to iterate\n * every permission, or call `.toArray(n)` for a bounded read.\n *\n * Each row includes the granted caps, today's usage, and the rolling\n * remaining-daily-allowance, so agent code can preflight a payment\n * without round-tripping the policy bounds.\n *\n * ```ts\n * for await (const p of tally.permissions.list({ agent_id: \"research-bot\" })) {\n * console.log(`${p.wallet_display_name}: $${p.remaining_today_usdc} left`);\n * }\n * ```\n */\n list(filters: PermissionListFilters = {}): AsyncResourcePage<Permission> {\n const { limit, agent_id } = filters;\n return new AsyncResourcePage<Permission>(async (cursor) => {\n const qs = new URLSearchParams();\n if (cursor) qs.set(\"cursor\", cursor);\n if (limit !== undefined) qs.set(\"limit\", String(limit));\n if (agent_id) qs.set(\"agent_id\", agent_id);\n const query = qs.toString();\n const path = query ? `/v1/permissions?${query}` : \"/v1/permissions\";\n return this.client.request<{\n data: Permission[];\n next_cursor: string | null;\n }>(\"GET\", path);\n });\n }\n}\n","import type { TallyClient } from \"../client\";\n\nexport type Wallet = {\n /** Stable Tally cuid for this wallet. Pass as `wallet_id` to permission\n * routes (grant / edit / revoke). */\n id: string;\n /** EVM address. Use as `wallet` in `tally.payments.create({ wallet })`. */\n address: string;\n /** Friendly name set when the wallet was created. */\n display_name: string;\n /** Optional role label (e.g., \"treasury\", \"ops\"); null if unset. */\n role_label: string | null;\n mode: \"test\" | \"live\";\n /** ISO 8601 timestamp. */\n created_at: string;\n};\n\nexport type WalletCreateInput = {\n /** Human-friendly name. 1–60 chars after trimming. */\n display_name: string;\n /** Optional role label (1–40 chars after trimming). */\n role_label?: string;\n};\n\nexport class WalletsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Lists every wallet in the API key's account + mode. Wallets are\n * returned oldest-first (so the \"Main Wallet\" auto-provisioned at\n * sign-in shows up first).\n */\n async list(): Promise<Wallet[]> {\n const { wallets } = await this.client.request<{ wallets: Wallet[] }>(\n \"GET\",\n \"/v1/wallets\",\n );\n return wallets;\n }\n\n /**\n * Provisions a new Privy server wallet in the API key's account + mode.\n * The wallet is owned (in Privy) by the oldest `owner`-role member of\n * the account, so SDK-created wallets behave identically to dashboard-\n * created ones: the same passkey approves any future signer changes.\n */\n async create(input: WalletCreateInput): Promise<Wallet> {\n const { wallet } = await this.client.request<{ wallet: Wallet }>(\n \"POST\",\n \"/v1/wallets\",\n input,\n );\n return wallet;\n }\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type { TallyClient } from \"../client\";\n\n// Webhook helpers for SDK consumers. Tally signs every delivery with\n// HMAC-SHA256 over `${timestamp}.${body}` and sends the signature in\n// the `tally-signature` header:\n//\n// tally-signature: t=<unix_seconds>,v1=<hex_lowercase>\n//\n// Receivers MUST verify before trusting any payload — otherwise a\n// known URL is forge-able. Pass the RAW request body (not the\n// JSON-parsed object) so the bytes match what Tally signed.\n//\n// The verifier is intentionally duplicated from the server-side\n// implementation (src/lib/webhook-sign.ts) to keep the SDK\n// self-contained — external consumers shouldn't depend on Tally's\n// server code.\n\nconst SIGNATURE_VERSION = \"v1\";\nexport const DEFAULT_TOLERANCE_SECONDS = 300; // 5 minutes\n\nexport type WebhookVerifyResult =\n | { ok: true }\n | {\n ok: false;\n reason:\n | \"missing_header\"\n | \"malformed_header\"\n | \"no_v1_signature\"\n | \"timestamp_out_of_tolerance\"\n | \"signature_mismatch\";\n };\n\nexport type WebhookVerifyInput = {\n /** Raw request body the receiver got — string, exactly the bytes\n * Tally signed. Don't pass a JSON-parsed object. */\n body: string;\n /** Value of the `tally-signature` header on the incoming request. */\n header: string | null | undefined;\n /** The plaintext signing secret you saved at webhook creation\n * (`whsec_<mode>_<base64url>`). */\n secret: string;\n /** Unix seconds. Defaults to now. Pin for tests. */\n now?: number;\n /** How far the header timestamp may drift from `now`. Defaults to\n * 300s (5 min). Tighten or loosen to taste; the wider the window,\n * the longer a stolen signature stays replayable. */\n toleranceSeconds?: number;\n};\n\nexport type WebhookEventType =\n | \"payment.created\"\n | \"payment.confirmed\"\n | \"payment.failed\"\n | \"inbound.received\"\n | \"permission.granted\"\n | \"permission.updated\"\n | \"permission.revoked\";\n\nexport type Webhook = {\n /** Stable cuid for this endpoint. Pass to `revoke()`. */\n id: string;\n mode: \"test\" | \"live\";\n /** Receiver URL Tally POSTs to. */\n url: string;\n /** Event types this endpoint subscribes to. */\n events: string[];\n /** First few characters of the secret (e.g., `whsec_test_abc…`).\n * Useful for identifying the secret in dashboards without\n * exposing the plaintext. */\n secret_prefix: string;\n created_at: string;\n revoked_at: string | null;\n /** ISO timestamp of the last delivery attempt. null until first send. */\n last_delivery_at: string | null;\n};\n\nexport type CreatedWebhook = Webhook & {\n /** Plaintext signing secret. Returned exactly once at creation.\n * Store it now — it's NOT recoverable later. */\n secret: string;\n};\n\nexport type WebhookCreateInput = {\n /** Receiver URL. Must be https:// (or http://localhost for dev). */\n url: string;\n /** Event types to subscribe to. */\n events: WebhookEventType[];\n};\n\nexport class WebhooksResource {\n /**\n * The TallyClient is optional so legacy zero-arg construction\n * (`new WebhooksResource()`) still works for non-class consumers who\n * only use `verifySignature`. List / create / revoke require a\n * client; calling them on a clientless instance throws.\n */\n constructor(private readonly client?: TallyClient) {}\n\n /**\n * Verify a `tally-signature` header against the request body. Use in\n * your webhook handler before processing the payload.\n *\n * @example\n * ```ts\n * const body = await req.text(); // raw, not JSON-parsed\n * const result = tally.webhooks.verifySignature({\n * body,\n * header: req.headers.get(\"tally-signature\"),\n * secret: process.env.TALLY_WEBHOOK_SECRET!,\n * });\n * if (!result.ok) return new Response(\"invalid signature\", { status: 400 });\n * const event = JSON.parse(body);\n * // ... handle event\n * ```\n */\n verifySignature(input: WebhookVerifyInput): WebhookVerifyResult {\n return verifySignature(input);\n }\n\n private requireClient(): TallyClient {\n if (!this.client) {\n throw new Error(\n \"WebhooksResource was constructed without a TallyClient. Use `new Tally({ apiKey })` to enable list/create/revoke.\",\n );\n }\n return this.client;\n }\n\n /**\n * Lists every webhook endpoint in the API key's account + mode.\n * Revoked endpoints are included (with `revoked_at` set) for audit.\n *\n * ```ts\n * for (const w of await tally.webhooks.list()) {\n * console.log(`${w.url} → ${w.events.join(\",\")}`);\n * }\n * ```\n */\n async list(): Promise<Webhook[]> {\n const { webhooks } = await this.requireClient().request<{\n webhooks: Webhook[];\n }>(\"GET\", \"/v1/webhooks\");\n return webhooks;\n }\n\n /**\n * Creates a new webhook endpoint. The signing secret is returned in\n * `webhook.secret` **exactly once** — store it now; it's not\n * recoverable later. If you lose it, revoke and recreate.\n *\n * ```ts\n * const webhook = await tally.webhooks.create({\n * url: \"https://example.com/tally/events\",\n * events: [\"payment.confirmed\", \"payment.failed\"],\n * });\n * console.log(webhook.secret); // shown once\n * ```\n */\n async create(input: WebhookCreateInput): Promise<CreatedWebhook> {\n const { webhook } = await this.requireClient().request<{\n webhook: CreatedWebhook;\n }>(\"POST\", \"/v1/webhooks\", input);\n return webhook;\n }\n\n /**\n * Revokes a webhook endpoint by id. No further deliveries will be\n * enqueued; deliveries already in-flight are not cancelled.\n * Idempotent: revoking an already-revoked endpoint is a no-op.\n *\n * ```ts\n * await tally.webhooks.revoke(\"whk_01HXYZ...\");\n * ```\n */\n async revoke(id: string): Promise<Webhook> {\n const { webhook } = await this.requireClient().request<{\n webhook: Webhook;\n }>(\"POST\", `/v1/webhooks/${encodeURIComponent(id)}/revoke`);\n return webhook;\n }\n}\n\nexport function verifySignature(input: WebhookVerifyInput): WebhookVerifyResult {\n const { body, header, secret } = input;\n const now = input.now ?? Math.floor(Date.now() / 1000);\n const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n\n if (!header) return { ok: false, reason: \"missing_header\" };\n\n let timestamp: number | null = null;\n let v1: string | null = null;\n for (const part of header.split(\",\")) {\n const [k, v] = part.split(\"=\", 2);\n if (k === \"t\") {\n const n = Number(v);\n if (Number.isFinite(n)) timestamp = n;\n } else if (k === SIGNATURE_VERSION) {\n v1 = v;\n }\n }\n\n if (timestamp === null) return { ok: false, reason: \"malformed_header\" };\n if (!v1) return { ok: false, reason: \"no_v1_signature\" };\n\n if (Math.abs(now - timestamp) > tolerance) {\n return { ok: false, reason: \"timestamp_out_of_tolerance\" };\n }\n\n const expected = createHmac(\"sha256\", secret)\n .update(`${timestamp}.${body}`)\n .digest(\"hex\");\n\n if (expected.length !== v1.length) {\n return { ok: false, reason: \"signature_mismatch\" };\n }\n const a = Buffer.from(expected, \"utf8\");\n const b = Buffer.from(v1, \"utf8\");\n if (!timingSafeEqual(a, b)) {\n return { ok: false, reason: \"signature_mismatch\" };\n }\n return { ok: true };\n}\n","// @tallyforagents/sdk — the financial OS for AI agents.\n//\n// Usage:\n// import { Tally } from \"@tallyforagents/sdk\";\n//\n// const tally = new Tally({ apiKey: process.env.TALLY_API_KEY! });\n// await tally.agents.upsert({ id: \"research-bot\", display_name: \"Research Bot\" });\n\nimport { TallyClient, type ClientOptions } from \"./client\";\nimport { AgentsResource } from \"./resources/agents\";\nimport { PaymentsResource } from \"./resources/payments\";\nimport { PermissionsResource } from \"./resources/permissions\";\nimport { WalletsResource } from \"./resources/wallets\";\nimport { WebhooksResource } from \"./resources/webhooks\";\n\nexport class Tally {\n readonly agents: AgentsResource;\n readonly payments: PaymentsResource;\n readonly permissions: PermissionsResource;\n readonly wallets: WalletsResource;\n readonly webhooks: WebhooksResource;\n\n // Expose the underlying client for advanced use (custom retries, etc.).\n // Internal callers go through the resource classes instead.\n private readonly client: TallyClient;\n\n constructor(opts: ClientOptions) {\n this.client = new TallyClient(opts);\n this.agents = new AgentsResource(this.client);\n this.payments = new PaymentsResource(this.client);\n this.permissions = new PermissionsResource(this.client);\n this.wallets = new WalletsResource(this.client);\n this.webhooks = new WebhooksResource(this.client);\n }\n}\n\nexport type { ClientOptions } from \"./client\";\nexport type { Agent, AgentUpsertInput, AgentWallet } from \"./resources/agents\";\nexport type {\n Payment,\n PaymentCreateInput,\n PaymentListFilters,\n} from \"./resources/payments\";\nexport { AsyncResourcePage } from \"./pagination\";\nexport type { Wallet, WalletCreateInput } from \"./resources/wallets\";\nexport type {\n Permission,\n PermissionListFilters,\n} from \"./resources/permissions\";\nexport type {\n Webhook,\n CreatedWebhook,\n WebhookCreateInput,\n WebhookEventType,\n WebhookVerifyInput,\n WebhookVerifyResult,\n} from \"./resources/webhooks\";\nexport { verifySignature } from \"./resources/webhooks\";\nexport {\n TallyError,\n AuthenticationError,\n NotFoundError,\n ValidationError,\n RateLimitError,\n ConflictError,\n} from \"./errors\";\n"]}
|