x402-safe-fetch 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cli.js +12 -6
- package/index.js +1 -1
- package/package.json +1 -1
- package/wallet.js +7 -0
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ npx x402-safe-fetch pay <url> --max 0.05 --daily 1
|
|
|
30
30
|
npx x402-safe-fetch spent # what was paid today, and what is left
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
**No key to copy.** `connect` makes a wallet for this computer (its key stays in `~/.x402-safe-fetch/wallet.json`, readable by you only) and shows a link and a code: open it on your phone, sign in to [wallet.fizzl.eu](https://wallet.fizzl.eu) and approve. From then on every payment is first reserved on your Fizzl wallet: your limits there apply, and above them you get Approve / Deny on your phone (Telegram or the dashboard) before anything is signed; the key never leaves this computer.
|
|
33
|
+
**No key to copy.** `connect` makes a wallet for this computer (its key stays in `~/.x402-safe-fetch/wallet.json`, readable by you only) and shows a link and a code: open it on your phone, sign in to [wallet.fizzl.eu](https://wallet.fizzl.eu) and approve. From then on every payment is first reserved on your Fizzl wallet: your limits there apply, and above them you get Approve / Deny on your phone (Telegram or the dashboard) before anything is signed; the key never leaves this computer. Then add a few dollars of USDC on Base to the address it shows (`wallet` shows it again, with the balance): no crypto yet? It also prints a link to buy $5 of USDC with a card at Coinbase, sent straight to that address (the link starts a Coinbase Onramp session through x402 Doctor; you pay Coinbase, Fizzl never holds the money). Or send USDC on Base from Coinbase, a wallet app or an exchange. `init` makes the wallet without linking it, and `EVM_PRIVATE_KEY` still overrides both.
|
|
34
34
|
|
|
35
35
|
`pay` pays USDC on Base, only when x402 Doctor's preflight says go: the cheapest option within `--max`, all calls of the UTC day together within `--daily` (preflights included), and the paid answer checked for instructions aimed at your agent (`--no-answer-check` turns that off). An endpoint that took a payment without delivering isn't paid again for 30 minutes, also by the next command and whatever is added to its URL. `--method POST --data '{"q":1}'` sends a body, `--dry-run` is `check`, and `--json` prints one line of JSON (with the answer, the transaction hash and the day's spend). Exit code 0 is done, 1 is not paid (a check said no, or a limit), 2 is a usage error. The day's spend is kept in `~/.x402-safe-fetch/state.json` (`X402_SAFE_FETCH_HOME` moves it). Use a dedicated wallet with a few dollars.
|
|
36
36
|
|
package/cli.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import fs from "node:fs";
|
|
19
19
|
import os from "node:os";
|
|
20
20
|
import path from "node:path";
|
|
21
|
-
import { ensureWallet, readWallet, writeWallet, pair, serverSigner, usdcBalance, WALLET_URL } from "./wallet.js";
|
|
21
|
+
import { ensureWallet, readWallet, writeWallet, pair, serverSigner, usdcBalance, buyUsdcUrl, WALLET_URL } from "./wallet.js";
|
|
22
22
|
import {
|
|
23
23
|
createSafeFetch, SafePayError, receiptOf, readOffer, usdOfOption, answerCheckOf, purchaseKey, paidTwiceMessage,
|
|
24
24
|
PAID_ERROR_MS, DOCTOR_URL, VERSION,
|
|
@@ -31,7 +31,7 @@ const HELP = `x402-safe-fetch ${VERSION}: pay for an API call from the command l
|
|
|
31
31
|
|
|
32
32
|
connect Start here. Makes a wallet for this computer and links it to your Fizzl wallet
|
|
33
33
|
(wallet.fizzl.eu): approve the code on your phone. Your limits and approvals then apply.
|
|
34
|
-
init Only the wallet for this computer, not linked: shows
|
|
34
|
+
init Only the wallet for this computer, not linked: shows its address and a link to buy USDC with a card.
|
|
35
35
|
wallet This computer's wallet: address, USDC balance, and whether it is linked.
|
|
36
36
|
find <need> Free. Who sells it (Fizzl Buy's pick): URL, price, and how often it delivered.
|
|
37
37
|
check <url> Free, pays nothing. The price per network and x402 Doctor's free check of the endpoint.
|
|
@@ -238,7 +238,7 @@ async function pay(args, { fetch, env, store, createPayingFetch, safeFetchOption
|
|
|
238
238
|
async function init(args, { store, fetch }) {
|
|
239
239
|
const { wallet, created } = await ensureWallet(store.home);
|
|
240
240
|
const usdc = await usdcBalance(wallet.address, { fetch });
|
|
241
|
-
return { ok: true, created, address: wallet.address, usdc_base: usdc, linked: wallet.server ? { url: wallet.server.url, agent: wallet.server.agent } : null, file: path.join(store.home, "wallet.json") };
|
|
241
|
+
return { ok: true, created, address: wallet.address, usdc_base: usdc, buy_usdc_url: buyUsdcUrl(wallet.address), linked: wallet.server ? { url: wallet.server.url, agent: wallet.server.agent } : null, file: path.join(store.home, "wallet.json") };
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
async function connect(args, { store, fetch, say, sleep }) {
|
|
@@ -251,14 +251,14 @@ async function connect(args, { store, fetch, say, sleep }) {
|
|
|
251
251
|
});
|
|
252
252
|
writeWallet(store.home, { ...wallet, server: linked });
|
|
253
253
|
const usdc = await usdcBalance(wallet.address, { fetch });
|
|
254
|
-
return { ok: true, address: wallet.address, linked: { url: linked.url, agent: linked.agent }, usdc_base: usdc };
|
|
254
|
+
return { ok: true, address: wallet.address, linked: { url: linked.url, agent: linked.agent }, usdc_base: usdc, buy_usdc_url: buyUsdcUrl(wallet.address) };
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
async function walletInfo(args, { store, fetch }) {
|
|
258
258
|
const wallet = readWallet(store.home);
|
|
259
259
|
if (!wallet) return { ok: false, code: "no_wallet", message: "no wallet yet: run npx x402-safe-fetch connect (or init)" };
|
|
260
260
|
const usdc = await usdcBalance(wallet.address, { fetch });
|
|
261
|
-
return { ok: true, address: wallet.address, usdc_base: usdc, linked: wallet.server ? { url: wallet.server.url, agent: wallet.server.agent } : null };
|
|
261
|
+
return { ok: true, address: wallet.address, usdc_base: usdc, buy_usdc_url: buyUsdcUrl(wallet.address), linked: wallet.server ? { url: wallet.server.url, agent: wallet.server.agent } : null };
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
function spent(args, { store }) {
|
|
@@ -302,7 +302,13 @@ function text(cmd, r) {
|
|
|
302
302
|
if (cmd === "connect") lines.push(`Linked to your Fizzl wallet${r.linked?.agent ? ` as "${r.linked.agent}"` : ""}: your limits and approvals now apply.`);
|
|
303
303
|
lines.push(`Address: ${r.address}`, `USDC on Base: ${r.usdc_base === null ? "?" : `$${r.usdc_base}`}`);
|
|
304
304
|
if (cmd !== "connect") lines.push(r.linked ? `Linked to ${r.linked.url}${r.linked.agent ? ` as "${r.linked.agent}"` : ""}` : "Not linked to a Fizzl wallet (run connect for limits and phone approvals).");
|
|
305
|
-
if (!r.usdc_base)
|
|
305
|
+
if (!r.usdc_base) {
|
|
306
|
+
lines.push(
|
|
307
|
+
"To pay, add a few dollars of USDC on Base to the address above:",
|
|
308
|
+
` with a card (Coinbase, $5; open it in your browser): ${r.buy_usdc_url}`,
|
|
309
|
+
" or send USDC from Coinbase, a wallet app or an exchange (choose the Base network).",
|
|
310
|
+
);
|
|
311
|
+
}
|
|
306
312
|
lines.push('Then: npx x402-safe-fetch find "what you need"');
|
|
307
313
|
} else if (cmd === "spent") {
|
|
308
314
|
lines.push(`${r.day}: $${r.spent_today_usd} paid in ${r.calls} call(s), $${r.left_usd} left of $${r.daily_limit_usd}`);
|
package/index.js
CHANGED
|
@@ -51,7 +51,7 @@ export const PRESIGN_GUARD_URL = "https://presign-guard.fizzl.eu";
|
|
|
51
51
|
export const INJECTION_CAP = "$0.02"; // presign-guard's injection check costs $0.01; never more than twice that
|
|
52
52
|
// The header a checked answer carries: green, orange, red (only with onInjection "flag" or a function) or unchecked.
|
|
53
53
|
export const ANSWER_CHECK_HEADER = "x-answer-check";
|
|
54
|
-
export const VERSION = "0.
|
|
54
|
+
export const VERSION = "0.19.0";
|
|
55
55
|
// Doctor sees which calls come from this package (in its usage counts), nothing more.
|
|
56
56
|
const DOCTOR_HEADERS = { accept: "application/json", "user-agent": `x402-safe-fetch/${VERSION}` };
|
|
57
57
|
export const BASE = "eip155:8453";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x402-safe-fetch",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "A fetch for AI agents that checks an unknown x402 endpoint ($0.001 x402 Doctor preflight) before paying it, and never pays above your budget.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/wallet.js
CHANGED
|
@@ -13,6 +13,13 @@ import { SafePayError } from "./index.js";
|
|
|
13
13
|
export const WALLET_URL = "https://wallet.fizzl.eu";
|
|
14
14
|
export const BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
15
15
|
const BASE_RPC = "https://mainnet.base.org";
|
|
16
|
+
export const ONRAMP_URL = "https://x402-doctor.fizzl.eu/api/onramp";
|
|
17
|
+
|
|
18
|
+
// A link to buy USDC on Base with a card at Coinbase, delivered to `address`. Each visit starts a new Coinbase session
|
|
19
|
+
// (x402 Doctor asks Coinbase Onramp for it); the buyer pays Coinbase and Fizzl never holds the money.
|
|
20
|
+
export function buyUsdcUrl(address, { amount = 5 } = {}) {
|
|
21
|
+
return `${ONRAMP_URL}?${new URLSearchParams({ address, amount: String(amount) })}`;
|
|
22
|
+
}
|
|
16
23
|
|
|
17
24
|
export function walletFile(home) {
|
|
18
25
|
return path.join(home, "wallet.json");
|