agent402-client 0.6.11 → 0.7.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.
Files changed (3) hide show
  1. package/README.md +38 -18
  2. package/index.js +24 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  A tiny buyer-side client for [Agent402](https://agent402.tools) (and any Agent402
4
4
  instance) - the buy side of [Agentic Finance](https://agent402.tools/agentic-finance),
5
- agents paying per request over x402 or MPP. **Resolve a task to a tool, then call it — with payment handled for
5
+ agents paying per request over x402 or MPP. **Resolve a task to a tool, then call it - with payment handled for
6
6
  you.** Free pure-CPU tools settle with a built-in proof-of-work (no wallet, zero
7
- dependencies); wallet-only tools settle via an x402-wrapped fetch you provide.
8
- Results are cached, and retries reuse an `Idempotency-Key` so a lost response
9
- never double-charges.
7
+ dependencies); wallet-only tools settle via an x402- or MPP-wrapped fetch you
8
+ provide, or by card through a prepaid credits key. Results are cached, and
9
+ retries reuse an `Idempotency-Key` so a lost response never double-charges.
10
10
 
11
11
  ```bash
12
12
  npm install agent402-client
13
13
  ```
14
14
 
15
- Runnable copy of the free-tier quickstart below: [`examples/hello-agent402.js`](https://github.com/MikeyPetrillo/Agent402/blob/main/examples/hello-agent402.js) — discover a tool and call it in ~15 lines, no wallet.
15
+ Runnable copy of the free-tier quickstart below: [`examples/hello-agent402.js`](https://github.com/MikeyPetrillo/Agent402/blob/main/examples/hello-agent402.js) - discover a tool and call it in ~15 lines, no wallet.
16
16
 
17
17
  ## Free tier (proof-of-work, no wallet)
18
18
 
@@ -25,7 +25,7 @@ const a = new Agent402(); // → https://agent402.tools
25
25
  const matches = await a.find("extract the article from a url");
26
26
  // → [{ slug: "extract", route, price, inputSchema, example, … }]
27
27
 
28
- // Call it — proof-of-work is solved automatically for free tools.
28
+ // Call it - proof-of-work is solved automatically for free tools.
29
29
  const out = await a.call("hash", { text: "hello world", algo: "sha256" });
30
30
  console.log(out.hex);
31
31
  ```
@@ -69,6 +69,26 @@ const a = new Agent402({ fetch: payFetch });
69
69
  const article = await a.call("extract", { url: "https://example.com/article" });
70
70
  ```
71
71
 
72
+ ## Pay by card instead of a wallet (prepaid credits)
73
+
74
+ Buy a credits pack ($20 / $50 / $100) at https://agent402.tools/credits, claim the
75
+ `a402_...` key once, and pass it as `creditsKey`. The SDK then sends
76
+ `Authorization: Bearer a402_...` on wallet-only calls; the server authorizes
77
+ against the key's balance before the handler runs and debits the list price only
78
+ on a successful (200) response (the `X-Credits-Balance` header carries what is
79
+ left; credits never expire). A refused call throws with the balance and a top-up
80
+ link; nothing is debited.
81
+
82
+ ```js
83
+ const client = new Agent402({ creditsKey: "a402_..." });
84
+ await client.call("whois", { domain: "example.com" }); // debited per successful call
85
+ ```
86
+
87
+ The same `maxPerCallUsd` / `dailyLimitUsd` / `maxPerHostUsd` caps apply (a
88
+ credits call reserves its price like a wallet call). A payment `fetch` still wins
89
+ when both are given, and free pure-CPU tools keep settling with proof-of-work.
90
+ The same Bearer header also reads the balance directly, e.g. `curl -H "Authorization: Bearer a402_..." https://agent402.tools/api/credits/balance`.
91
+
72
92
  ## Retries never double-charge
73
93
 
74
94
  Every paid call the SDK makes carries an `Idempotency-Key`, so a retry of a
@@ -80,8 +100,8 @@ same id is a new payment).
80
100
 
81
101
  ## Workflows (skill packs)
82
102
 
83
- For jobs that no single tool covers — e.g. "audit a domain", "build a stock
84
- brief" — Agent402 ships curated multi-tool **skill packs**: 5–7 catalog tools
103
+ For jobs that no single tool covers - e.g. "audit a domain", "build a stock
104
+ brief" - Agent402 ships curated multi-tool **skill packs**: 5–7 catalog tools
85
105
  composed into a Claude-ready task template. Discover them the same way you'd
86
106
  discover a tool:
87
107
 
@@ -96,7 +116,7 @@ const { messages } = await a.getWorkflowPrompt("security-audit", { domain: "stri
96
116
 
97
117
  ## Discover the live x402 economy
98
118
 
99
- Want to see who's actually getting paid on x402 right now — not just what tools
119
+ Want to see who's actually getting paid on x402 right now - not just what tools
100
120
  this service exposes? `topSellers()` returns the live leaderboard of sellers
101
121
  settling USDC (primarily on Base) in the last ~24h, derived from on-chain transfers. Free
102
122
  to call (no payment, no proof-of-work):
@@ -113,12 +133,12 @@ await a.topSellers({ sort: "calls", include: "all" });
113
133
 
114
134
  | Method | What |
115
135
  |---|---|
116
- | `new Agent402({ baseUrl?, fetch?, cache?, fetchImpl?, maxPerCallUsd?, dailyLimitUsd?, maxPerHostUsd? })` | `fetch` is your x402-wrapped fetch for paid tools (optional); `cache` (default `true`) memoizes deterministic results; the three USD caps set optional spending limits (see below) |
136
+ | `new Agent402({ baseUrl?, fetch?, creditsKey?, cache?, fetchImpl?, maxPerCallUsd?, dailyLimitUsd?, maxPerHostUsd? })` | `fetch` is your x402- or MPP-wrapped fetch for paid tools (optional); `creditsKey` is a prepaid card-credits key (`a402_...`) used for paid tools when no `fetch` is given; `cache` (default `true`) memoizes deterministic results; the three USD caps set optional spending limits (see below) |
117
137
  | `await a.find(task, { k = 5 })` | Resolve a plain-language task to the best-matching tools (route, price, schema, example) |
118
138
  | `await a.findWorkflows(task, { k = 2 })` | Resolve a task to matching multi-tool workflow templates (skill packs) |
119
139
  | `await a.getWorkflowPrompt(slug, args)` | Fetch the rendered prompt messages for a skill pack with arguments substituted in |
120
140
  | `await a.topSellers({ limit?, sort?, include? })` | Live x402 leaderboard: which sellers are settling the most USDC (primarily on Base) in the last ~24h (free, no payment) |
121
- | `await a.call(slug, params, { idempotencyKey?, cache? })` | Call a tool; auto-pays (PoW for free tools, x402 for wallet-only); returns the JSON result |
141
+ | `await a.call(slug, params, { idempotencyKey?, cache? })` | Call a tool; auto-pays (PoW for free tools; your payment `fetch` or the credits key for wallet-only); returns the JSON result |
122
142
  | `Agent402.solvePow(pow)` | Solve a proof-of-work challenge object → an `X-Pow-Solution` value |
123
143
  | `a.spendingSummary()` | Rolling-24h paid spend so far: `{ dailyUsd, calls, byHost, limits }` |
124
144
  | `a.clearCache()` | Drop the in-memory result cache |
@@ -127,7 +147,7 @@ await a.topSellers({ sort: "calls", include: "all" });
127
147
 
128
148
  By default the client pays whatever a tool costs. Set optional hard ceilings and a
129
149
  call that would exceed one is **refused before any payment is signed** (it throws
130
- `SpendingLimitError` — no funds move):
150
+ `SpendingLimitError` - no funds move):
131
151
 
132
152
  ```js
133
153
  import { Agent402, SpendingLimitError } from "agent402-client";
@@ -146,29 +166,29 @@ try {
146
166
  }
147
167
  ```
148
168
 
149
- Only **settled** paid calls count against the rolling window — a blocked or failed
169
+ Only **settled** paid calls count against the rolling window - a blocked or failed
150
170
  call never consumes budget. Free proof-of-work calls are never counted. Omit a cap
151
171
  (or leave it `null`) for no limit; with none set, behavior is unchanged.
152
172
 
153
173
  **What the caps check.** When a cap is set, the client preflights the `402` and
154
174
  checks the ceiling against the **larger** of the advertised price (from the
155
- seller's `/api/pricing`) and the amount the `402` challenge actually quotes — so a
175
+ seller's `/api/pricing`) and the amount the `402` challenge actually quotes - so a
156
176
  server that under-advertises and then quotes more in the `402` is refused *before*
157
177
  your wallet fetch signs anything. If the `402` can't be read (FREE_MODE, or an
158
178
  unrecognized challenge shape) it falls back to the advertised price rather than
159
179
  block a legitimate payment. Caps hold under **concurrency** too: each call reserves
160
180
  its amount synchronously, so N simultaneous calls can't each pass against the same
161
- pre-commit total. (The `402` amount is derived assuming stablecoin settlement —
162
- `atomic / 10^decimals ≈ USD` — which matches x402's USDC/USDG rails.)
181
+ pre-commit total. (The `402` amount is derived assuming stablecoin settlement -
182
+ `atomic / 10^decimals ≈ USD` - which matches x402's USDC/USDG rails.)
163
183
 
164
184
  - **Zero dependencies** for the free/proof-of-work path (uses `node:crypto`).
165
- - **Non-custodial:** paid settlement is your `@x402/fetch` + wallet; this client never sees your key.
185
+ - **Non-custodial:** paid settlement is your `@x402/fetch` / `mppx` fetch + wallet (or a prepaid credits key you bought); this client never sees a private key.
166
186
  - MIT licensed. Part of [Agent402](https://github.com/MikeyPetrillo/Agent402).
167
187
 
168
188
  ## Pick the settlement chain (`withNetworkPreference`)
169
189
 
170
190
  Multi-chain sellers list Base first, so an unmodified x402 client effectively
171
- always settles there. To pin a chain — e.g. **USDG on Robinhood Chain** —
191
+ always settles there. To pin a chain - e.g. **USDG on Robinhood Chain** -
172
192
  wrap your client before building the fetch:
173
193
 
174
194
  ```js
package/index.js CHANGED
@@ -18,7 +18,7 @@ import { createHash } from "node:crypto";
18
18
  // `User-Agent: agent402-client/<version>` — a standard header, no extra
19
19
  // network calls — so a seller can attribute traffic (and settled payments)
20
20
  // to this SDK. Product token only; nothing about the caller rides along.
21
- const VERSION = "0.6.1";
21
+ const VERSION = "0.7.0";
22
22
  const USER_AGENT = `agent402-client/${VERSION}`;
23
23
 
24
24
  const leadingZeroBits = (buf) => { let n = 0; for (const b of buf) { if (b === 0) { n += 8; continue; } n += Math.clz32(b) - 24; break; } return n; };
@@ -33,9 +33,12 @@ export class Agent402 {
33
33
  * @param {number} [opts.maxPerCallUsd] hard ceiling on a single paid call (USD); over → SpendingLimitError before paying
34
34
  * @param {number} [opts.dailyLimitUsd] hard ceiling on rolling-24h paid spend (USD)
35
35
  * @param {number} [opts.maxPerHostUsd] hard ceiling on rolling-24h paid spend to one seller host (USD)
36
+ * @param {string} [opts.creditsKey] a prepaid card-credits key (a402_...) from agent402.tools/credits -
37
+ * pays wallet-only tools by card when no payFetch is given
36
38
  */
37
39
  constructor({ baseUrl = "https://agent402.tools", fetch: payFetch, cache = true, fetchImpl = globalThis.fetch,
38
- maxPerCallUsd = null, dailyLimitUsd = null, maxPerHostUsd = null } = {}) {
40
+ maxPerCallUsd = null, dailyLimitUsd = null, maxPerHostUsd = null, creditsKey = null } = {}) {
41
+ this.creditsKey = typeof creditsKey === "string" && /^a402_[A-Za-z0-9_-]{32,64}$/.test(creditsKey) ? creditsKey : null;
39
42
  if (typeof fetchImpl !== "function") throw new Error("No fetch available — pass { fetchImpl } on Node < 18");
40
43
  this.baseUrl = String(baseUrl).replace(/\/$/, "");
41
44
  this.payFetch = payFetch || null;
@@ -227,9 +230,27 @@ export class Agent402 {
227
230
  throw e;
228
231
  }
229
232
  }
233
+ if (this.creditsKey) {
234
+ // Prepaid card credits: the server authorizes against the key's balance
235
+ // before the handler and debits only on a 200 (X-Credits-Balance tells
236
+ // what is left). The same spend caps apply as on the wallet path.
237
+ const host = hostOf(this.baseUrl);
238
+ const usd = parseUsd(tool.price);
239
+ const reservation = this._spendReserve(host, usd, slug);
240
+ try {
241
+ const r = await send({ Authorization: `Bearer ${this.creditsKey}` });
242
+ if (r.status === 402) {
243
+ const body = await r.json().catch(() => ({}));
244
+ throw new Error(`call "${slug}" refused by credits: ${body.error || "payment required"}${body.balanceUsd != null ? ` (balance $${body.balanceUsd})` : ""} - top up at ${body.topup || `${this.baseUrl}/credits`}`);
245
+ }
246
+ if (!r.ok) throw new Error(`call "${slug}" failed: HTTP ${r.status}`);
247
+ this._spendSettle(reservation);
248
+ return this._store(cacheKey, await r.json(), cache);
249
+ } catch (e) { this._spendRelease(reservation); throw e; }
250
+ }
230
251
  const r = await send(); // no wallet — succeeds only on a FREE_MODE instance
231
252
  if (r.ok) return this._store(cacheKey, await r.json(), cache);
232
- throw new Error(`call "${slug}" failed: HTTP ${r.status} — wallet-only tool; construct with { fetch: payFetch } (an @x402/fetch-wrapped fetch)`);
253
+ throw new Error(`call "${slug}" failed: HTTP ${r.status} — wallet-only tool; construct with { fetch: payFetch } (an @x402/fetch-wrapped fetch) or { creditsKey } (prepaid card credits from ${this.baseUrl}/credits)`);
233
254
  }
234
255
 
235
256
  // Free (compute-payable) tool: succeeds plainly on a FREE_MODE instance,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent402-client",
3
- "version": "0.6.11",
3
+ "version": "0.7.0",
4
4
  "description": "Agent402 buyer SDK: resolve a task to a tool across 500+ pay-per-call endpoints and call it with payment handled - free via proof-of-work, or over x402 or MPP (a stock mppx fetch works) with a wallet you keep; the buy side of Agentic Finance. Caching, idempotent retries (Idempotency-Key), non-custodial, zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "index.js",