@wafermapxyz/cli 0.4.7 → 0.4.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.9 (7 Nov 2026)
4
+
5
+ **`wafermap call <llm> --stream`: watch the answer arrive.**
6
+
7
+ - With `--stream`, an LLM resource answers as server-sent events and the CLI prints each piece as it comes. The payment is the same signature as before; Wafermap settles it only after the last piece, so a model that stops halfway charges nothing (the CLI then exits with `stream_broken`).
8
+ - Free calls ($WAFERMAP Pro) and `--pay wafermap` still answer in one JSON body; `--stream` prints that the usual way.
9
+ - `Wafermap.call()` takes `onDelta(text)` for the same thing from code.
10
+
11
+ ## 0.4.8 (5 Nov 2026)
12
+
13
+ **Two more payment networks: Arc and Soneium.**
14
+
15
+ - `--network arc` pays in USDC on Arc (eip155:5042, the ERC-20 face of Arc's native USDC at `0x3600…0000`, 6 decimals) and `--network soneium` in bridged USDC.e on Soneium (eip155:1868). Both are EIP-3009 transfers, gasless for you; Wafermap settles them through the Elsa facilitator. The EIP-712 domains (`"USDC", "2"` and `"Bridged USDC (Soneium)", "2"`) were checked against each token's `DOMAIN_SEPARATOR()`.
16
+ - `balance`, `withdraw` and `history --verify` cover both. RPCs: `WAFERMAP_RPC_ARC`, `WAFERMAP_RPC_SONEIUM` (public ones by default). On Arc the gas token is USDC itself; on Soneium it is ETH.
17
+ - The MCP server's `network` option takes `arc` and `soneium`.
18
+ - LLM resources can stream: pass `accept: text/event-stream` to `x402fetch` and read `delta`, `receipt` and `done` events (https://wafermap.xyz/docs#streaming). Payment works as before and settles once the model finished.
19
+
3
20
  ## 0.4.7 (4 Nov 2026)
4
21
 
5
22
  **`history --verify` checks Solana payments.** Until now they showed `unknown`.
package/README.md CHANGED
@@ -6,7 +6,7 @@ The CLI, SDK and MCP server behind [Wafermap](https://wafermap.xyz): an x402 wal
6
6
  - **discover** third-party x402 services in the open x402 index and **pay** them
7
7
  - **swap** tokens through the KyberSwap aggregator
8
8
 
9
- Every payment is checked against a per-payment cap and a daily cap before anything is signed. Swaps have their own per-swap cap. Payments settle in USDG on Robinhood Chain or USDC on Solana, Base, Arbitrum or Polygon and are gasless; swaps need a little ETH for gas.
9
+ Every payment is checked against a per-payment cap and a daily cap before anything is signed. Swaps have their own per-swap cap. Payments settle in USDG on Robinhood Chain or USDC on Solana, Base, Arbitrum, Polygon, Arc or Soneium and are gasless; swaps need a little ETH for gas.
10
10
 
11
11
  ## Install
12
12
 
@@ -35,7 +35,7 @@ wafermap swap 0.01 ETH USDG
35
35
  wafermap history
36
36
  ```
37
37
 
38
- Add `--yes` to skip the confirmation prompt (caps still apply), `--network base`, `arbitrum`, `polygon` or `solana` to pay in USDC on that network (swaps run on `robinhood` or `base`). Set `WAFERMAP_PASSWORD` to avoid the password prompt in scripts.
38
+ Add `--yes` to skip the confirmation prompt (caps still apply), `--network base`, `arbitrum`, `polygon`, `arc`, `soneium` or `solana` to pay in USDC on that network (swaps run on `robinhood` or `base`). Set `WAFERMAP_PASSWORD` to avoid the password prompt in scripts.
39
39
 
40
40
  ## MCP server
41
41
 
@@ -82,7 +82,9 @@ Pass `approve: (a) => boolean` to confirm each payment or swap yourself, and `le
82
82
 
83
83
  ## $WAFERMAP holder discount
84
84
 
85
- A wallet that holds at least 100,000 $WAFERMAP on Robinhood Chain pays 50% less for Wafermap's own resources when it pays on an EVM network (Robinhood Chain, Base, Arbitrum, Polygon). Nothing to set up: `wafermap call` names the wallet to Wafermap and the quote comes back discounted; the server re-checks the balance of the wallet that signs. Marketplace listings and Solana payments keep the regular price. Check a wallet: https://wafermap.xyz/api/holder?address=0x…, or run `wafermap holder`.
85
+ A wallet that holds at least 100,000 $WAFERMAP on Robinhood Chain pays 50% less for Wafermap's own resources when it pays on an EVM network (Robinhood Chain, Base, Arbitrum, Polygon, Arc, Soneium). Nothing to set up: `wafermap call` names the wallet to Wafermap and the quote comes back discounted; the server re-checks the balance of the wallet that signs. Marketplace listings and Solana payments keep the regular price. Check a wallet: https://wafermap.xyz/api/holder?address=0x…, or run `wafermap holder`.
86
+
87
+ LLM resources can **stream**: `wafermap call gemma-4 prompt="hi" --stream` prints the answer as it arrives, and the payment settles only once the model finishes.
86
88
 
87
89
  Wafermap's own resources can also be **paid in $WAFERMAP at 50% off, and the tokens are burned**: `wafermap approve-wafermap` once (a Permit2 approval, one Robinhood Chain transaction), then `wafermap call <id> --pay wafermap`. Each payment is a Permit2 signature for one exact amount, usable for two minutes only by Wafermap's relayer (fixed in the client), checked against a market price the client reads itself.
88
90
 
package/dist/cli.js CHANGED
@@ -447,7 +447,7 @@ function choose(q, prefer = CHAIN_KEYS) {
447
447
  const r = q.accepts.find((a) => a.chain.key === k);
448
448
  if (r) return r;
449
449
  }
450
- throw new X402Error("no_supported_payment", q.offered.length ? `This service only accepts ${[...new Set(q.offered)].join(", ")}. Wafermap pays USDG on Robinhood Chain or USDC on Solana, Base, Arbitrum or Polygon.` : "The service offered no payment option.");
450
+ throw new X402Error("no_supported_payment", q.offered.length ? `This service only accepts ${[...new Set(q.offered)].join(", ")}. Wafermap pays USDG on Robinhood Chain or USDC on Solana, Base, Arbitrum, Polygon, Arc or Soneium.` : "The service offered no payment option.");
451
451
  }
452
452
  async function signPayment(signer, req, resource, solana) {
453
453
  if (req.chain.vm === "svm") {
@@ -535,7 +535,10 @@ var init_x402client = __esm({
535
535
  { key: "solana", vm: "svm", id: 0, caip: SOLANA_CAIP, v1: "solana", stable: SOLANA_USDC, symbol: "USDC", name: "Solana" },
536
536
  { key: "base", vm: "evm", id: 8453, caip: "eip155:8453", v1: "base", stable: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", symbol: "USDC", name: "Base", domain: USDC_DOMAIN },
537
537
  { key: "arbitrum", vm: "evm", id: 42161, caip: "eip155:42161", v1: "arbitrum", stable: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", symbol: "USDC", name: "Arbitrum", domain: USDC_DOMAIN },
538
- { key: "polygon", vm: "evm", id: 137, caip: "eip155:137", v1: "polygon", stable: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", symbol: "USDC", name: "Polygon", domain: USDC_DOMAIN }
538
+ { key: "polygon", vm: "evm", id: 137, caip: "eip155:137", v1: "polygon", stable: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", symbol: "USDC", name: "Polygon", domain: USDC_DOMAIN },
539
+ // EIP-712 domains read from the token contracts (name(), version()) and checked against DOMAIN_SEPARATOR().
540
+ { key: "arc", vm: "evm", id: 5042, caip: "eip155:5042", v1: "arc", stable: "0x3600000000000000000000000000000000000000", symbol: "USDC", name: "Arc", domain: { name: "USDC", version: "2" } },
541
+ { key: "soneium", vm: "evm", id: 1868, caip: "eip155:1868", v1: "soneium", stable: "0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369", symbol: "USDC", name: "Soneium", domain: { name: "Bridged USDC (Soneium)", version: "2" } }
539
542
  ];
540
543
  EVM_CHAINS = PAY_CHAINS.filter((c) => c.vm === "evm");
541
544
  CHAIN_KEYS = PAY_CHAINS.map((c) => c.key);
@@ -643,7 +646,7 @@ __export(core_exports, {
643
646
  });
644
647
  import { createPublicClient, createWalletClient, erc20Abi as erc20Abi2, formatUnits as formatUnits2, http, pad, parseAbiItem, toEventSelector } from "viem";
645
648
  import { privateKeyToAccount } from "viem/accounts";
646
- import { arbitrum, base, polygon, robinhood } from "viem/chains";
649
+ import { arbitrum, arc, base, polygon, robinhood, soneium } from "viem/chains";
647
650
  function mergeLog(entries) {
648
651
  const outcomes = new Map(entries.filter((e) => e.of).map((e) => [e.of, e]));
649
652
  return entries.filter((e) => !e.of).map((e) => {
@@ -748,7 +751,7 @@ var init_core = __esm({
748
751
  }
749
752
  code;
750
753
  };
751
- CHAINS = { robinhood, base, arbitrum, polygon };
754
+ CHAINS = { robinhood, base, arbitrum, polygon, arc, soneium };
752
755
  AUTH_STATE_ABI = [{ type: "function", name: "authorizationState", stateMutability: "view", inputs: [{ name: "authorizer", type: "address" }, { name: "nonce", type: "bytes32" }], outputs: [{ type: "bool" }] }];
753
756
  AUTH_USED = parseAbiItem("event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce)");
754
757
  DEFAULT_LIMITS = { perPayment: 0.1, perDay: 1, perSwap: 25 };
@@ -929,19 +932,59 @@ var init_core = __esm({
929
932
  throw new WafermapError("approve_failed", e?.shortMessage ?? e?.message ?? "The approval failed.");
930
933
  }
931
934
  }
935
+ /** Reads /v1's server-sent events for an LLM call: delta pieces as they arrive, then receipt (or error), then done. */
936
+ async readStream(res, onDelta) {
937
+ const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
938
+ let buf = "";
939
+ let receipt = null;
940
+ let done = null;
941
+ let error = null;
942
+ for (; ; ) {
943
+ const { value, done: end } = await reader.read();
944
+ if (end) break;
945
+ buf += value;
946
+ let i;
947
+ while ((i = buf.indexOf("\n\n")) >= 0) {
948
+ const block = buf.slice(0, i);
949
+ buf = buf.slice(i + 2);
950
+ const ev = /^event: (.+)$/m.exec(block)?.[1];
951
+ let data = null;
952
+ try {
953
+ data = JSON.parse(/^data: (.*)$/m.exec(block)?.[1] ?? "null");
954
+ } catch {
955
+ }
956
+ if (ev === "delta" && typeof data?.text === "string") onDelta(data.text);
957
+ else if (ev === "receipt") receipt = data;
958
+ else if (ev === "done") done = data;
959
+ else if (ev === "error") error = data;
960
+ }
961
+ }
962
+ return { receipt, done, error };
963
+ }
932
964
  async call(id, input = {}, o = {}) {
933
965
  const inToken = o.payIn === "wafermap";
966
+ const stream = !!o.onDelta && !inToken;
934
967
  if (inToken && id.startsWith("m/")) throw new WafermapError("token_not_offered", "Marketplace listings are paid in USDG or USDC to their seller, not in $WAFERMAP.");
935
968
  if (inToken && !await this.wafermapApproved().catch(() => true)) throw new WafermapError("permit2_not_approved", "Paying in $WAFERMAP needs a one-time approval of Permit2. Run `wafermap approve-wafermap` (one transaction on Robinhood Chain, paid in ETH), then call again.");
936
969
  const pass = id.startsWith("m/") ? null : await this.holderPass();
937
- const go = () => this.paidFetch("resource", id, `${this.baseUrl}/v1/${id}`, { method: "POST", headers: { "content-type": "application/json", "x-wafermap-payer": this.address, ...pass ? { [PASS_HEADER]: pass } : {} }, body: JSON.stringify(input) }, o.network, inToken ? "wafermap" : void 0);
970
+ const go = () => this.paidFetch("resource", id, `${this.baseUrl}/v1/${id}`, { method: "POST", headers: { "content-type": "application/json", "x-wafermap-payer": this.address, ...stream ? { accept: "text/event-stream" } : {}, ...pass ? { [PASS_HEADER]: pass } : {} }, body: JSON.stringify(input) }, o.network, inToken ? "wafermap" : void 0);
938
971
  const settle = (r2, out2) => {
939
972
  if (!r2.paid || !r2.id) return;
940
- const status = r2.res.status === 402 ? "refused" : r2.res.ok ? "ok" : "failed";
973
+ const status = r2.res.status === 402 ? "refused" : r2.res.ok && !out2?.streamError ? "ok" : "failed";
941
974
  this.outcome(r2.id, "resource", id, r2.paid, status, out2?.receipt?.tx ?? r2.paid.tx, typeof out2?.receipt?.amountUsd === "number" ? out2.receipt.amountUsd : null);
942
975
  };
943
976
  const host = new URL(this.baseUrl).host;
944
- const read = (r2) => this.body(r2.res, () => r2.res.json(), {}, host, r2.paid?.auth ?? (r2.paid ? {} : null));
977
+ const read = (r2) => this.body(
978
+ r2.res,
979
+ async () => {
980
+ if (!(stream && r2.res.ok && (r2.res.headers.get("content-type") ?? "").includes("text/event-stream"))) return r2.res.json();
981
+ const s = await this.readStream(r2.res, o.onDelta);
982
+ return { resource: id, result: { text: s.done?.text ?? "", model: s.done?.model, usage: s.done?.usage }, receipt: s.receipt, streamError: s.error };
983
+ },
984
+ {},
985
+ host,
986
+ r2.paid?.auth ?? (r2.paid ? {} : null)
987
+ );
945
988
  let r = await go();
946
989
  let out = await read(r);
947
990
  settle(r, out);
@@ -956,6 +999,7 @@ var init_core = __esm({
956
999
  const { res, paid } = r;
957
1000
  const refused = this.rejected(res, out, paid);
958
1001
  if (refused) throw refused;
1002
+ if (out?.streamError) throw new WafermapError(out.streamError.code ?? "stream_failed", out.streamError.message ?? "The answer did not finish. You were not charged.");
959
1003
  if (!res.ok) throw new WafermapError(out?.error?.code ?? "http_error", out?.error?.message ?? (typeof out?.error === "string" ? out.error : `HTTP ${res.status}`));
960
1004
  return out;
961
1005
  }
@@ -1226,7 +1270,7 @@ function openWallet(password2, approve) {
1226
1270
  limits: c.limits,
1227
1271
  ledger: { entries: spendLog, record: appendSpend },
1228
1272
  approve,
1229
- rpc: { robinhood: process.env.WAFERMAP_RPC_ROBINHOOD, base: process.env.WAFERMAP_RPC_BASE, arbitrum: process.env.WAFERMAP_RPC_ARBITRUM, polygon: process.env.WAFERMAP_RPC_POLYGON, solana: process.env.WAFERMAP_RPC_SOLANA }
1273
+ rpc: { robinhood: process.env.WAFERMAP_RPC_ROBINHOOD, base: process.env.WAFERMAP_RPC_BASE, arbitrum: process.env.WAFERMAP_RPC_ARBITRUM, polygon: process.env.WAFERMAP_RPC_POLYGON, arc: process.env.WAFERMAP_RPC_ARC, soneium: process.env.WAFERMAP_RPC_SONEIUM, solana: process.env.WAFERMAP_RPC_SOLANA }
1230
1274
  });
1231
1275
  if (wm.solanaAddress) rememberSolana(wm.solanaAddress);
1232
1276
  return wm;
@@ -1244,7 +1288,7 @@ var VERSION;
1244
1288
  var init_version = __esm({
1245
1289
  "src/version.ts"() {
1246
1290
  "use strict";
1247
- VERSION = "0.4.7";
1291
+ VERSION = "0.4.9";
1248
1292
  }
1249
1293
  });
1250
1294
 
@@ -1363,7 +1407,7 @@ async function runMcp() {
1363
1407
  "wafermap_discover",
1364
1408
  {
1365
1409
  title: "Discover x402 services",
1366
- description: "Search the open x402 index for third-party paid APIs (weather, search, enrichment, data\u2026). Services marked payable accept USDG on Robinhood Chain or USDC on Solana, Base, Arbitrum or Polygon and can be called with wafermap_pay.",
1410
+ description: "Search the open x402 index for third-party paid APIs (weather, search, enrichment, data\u2026). Services marked payable accept USDG on Robinhood Chain or USDC on Solana, Base, Arbitrum, Polygon, Arc or Soneium and can be called with wafermap_pay.",
1367
1411
  inputSchema: { query: z.string().min(2).describe("What you need, in a few words."), limit: z.number().int().min(1).max(20).optional() }
1368
1412
  },
1369
1413
  async ({ query, limit }) => {
@@ -1441,7 +1485,7 @@ var init_mcp = __esm({
1441
1485
  init_local();
1442
1486
  init_store();
1443
1487
  init_version();
1444
- NET = z.enum(["robinhood", "solana", "base", "arbitrum", "polygon"]).optional().describe("Payment network: robinhood (USDG), or solana, base, arbitrum or polygon (USDC). Defaults to your configured network.");
1488
+ NET = z.enum(["robinhood", "solana", "base", "arbitrum", "polygon", "arc", "soneium"]).optional().describe("Payment network: robinhood (USDG), or solana, base, arbitrum, polygon, arc or soneium (USDC). Defaults to your configured network.");
1445
1489
  SWAP_NET = z.enum(["robinhood", "base"]).optional().describe("Swap network: robinhood or base. Defaults to your configured network when it is one of these, else robinhood.");
1446
1490
  }
1447
1491
  });
@@ -1775,7 +1819,9 @@ var PAY_NETWORKS = {
1775
1819
  solana: { id: 0, caip: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", name: "Solana", asset: "USDC", explorer: "https://solscan.io", vm: "svm" },
1776
1820
  base: { ...NETWORKS.base, vm: "evm" },
1777
1821
  arbitrum: { id: 42161, caip: "eip155:42161", name: "Arbitrum", asset: "USDC", explorer: "https://arbiscan.io", vm: "evm" },
1778
- polygon: { id: 137, caip: "eip155:137", name: "Polygon", asset: "USDC", explorer: "https://polygonscan.com", vm: "evm" }
1822
+ polygon: { id: 137, caip: "eip155:137", name: "Polygon", asset: "USDC", explorer: "https://polygonscan.com", vm: "evm" },
1823
+ arc: { id: 5042, caip: "eip155:5042", name: "Arc", asset: "USDC", explorer: "https://explorer.arc.io", vm: "evm" },
1824
+ soneium: { id: 1868, caip: "eip155:1868", name: "Soneium", asset: "USDC", explorer: "https://soneium.blockscout.com", vm: "evm" }
1779
1825
  };
1780
1826
  function payNetworkOf(v) {
1781
1827
  const s = String(v ?? "").toLowerCase();
@@ -1790,7 +1836,7 @@ init_local();
1790
1836
  init_solana();
1791
1837
  init_store();
1792
1838
  init_version();
1793
- var HELP = `wafermap ${VERSION}: an x402 wallet for agents (Robinhood Chain, Solana, Base, Arbitrum, Polygon)
1839
+ var HELP = `wafermap ${VERSION}: an x402 wallet for agents (Robinhood Chain, Solana, Base, Arbitrum, Polygon, Arc, Soneium)
1794
1840
 
1795
1841
  Wallet
1796
1842
  wafermap init [--import] create (or import) the agent wallet, encrypted with a password
@@ -1805,6 +1851,7 @@ Pay
1805
1851
  wafermap resources [words] Wafermap's pay-per-call catalog
1806
1852
  wafermap call <id> [field=value ...] run a resource and pay its price (--json '{...}' for complex input)
1807
1853
  wafermap call <id> ... --pay wafermap pay a Wafermap resource in $WAFERMAP at 50% off (the tokens are burned)
1854
+ wafermap call <llm> ... --stream print the answer as it arrives; you pay only once the model finishes
1808
1855
  wafermap approve-wafermap one-time approval so the wallet can pay in $WAFERMAP (Robinhood Chain, ETH gas)
1809
1856
  wafermap holder what this wallet's $WAFERMAP unlocks: holder price, Pro free calls left today
1810
1857
  wafermap pass print a holder pass (X-Wafermap-Pass) for scripts and x402 Tracker Pro
@@ -1817,7 +1864,7 @@ Trade
1817
1864
  Agents
1818
1865
  wafermap mcp run as an MCP server (stdio); password from \`wafermap keychain on\` or WAFERMAP_PASSWORD
1819
1866
 
1820
- Common flags: --network robinhood|solana|base|arbitrum|polygon --yes (skip the confirmation)
1867
+ Common flags: --network robinhood|solana|base|arbitrum|polygon|arc|soneium --yes (skip the confirmation)
1821
1868
  Files live in ${HOME}. Docs: https://wafermap.xyz/docs#cli`;
1822
1869
  function parse(argv) {
1823
1870
  const out = { _: [], flags: {} };
@@ -1826,7 +1873,7 @@ function parse(argv) {
1826
1873
  if (a.startsWith("--")) {
1827
1874
  const [k, v] = a.slice(2).split("=", 2);
1828
1875
  if (v !== void 0) out.flags[k] = v;
1829
- else if (argv[i + 1] && !argv[i + 1].startsWith("--") && !["yes", "post", "import"].includes(k)) out.flags[k] = argv[++i];
1876
+ else if (argv[i + 1] && !argv[i + 1].startsWith("--") && !["yes", "post", "import", "stream"].includes(k)) out.flags[k] = argv[++i];
1830
1877
  else out.flags[k] = true;
1831
1878
  } else out._.push(a);
1832
1879
  }
@@ -1919,7 +1966,7 @@ async function main() {
1919
1966
  console.log(`\u2713 Agent wallet ${acct.address}
1920
1967
  Solana address ${sol} (derived from the same key)
1921
1968
  Key encrypted in ${HOME}/keystore.json. Forget the password and the key is gone: run \`wafermap export-key\` to back it up.
1922
- Fund it with a few dollars of USDC on Solana (Solana address), or USDG on Robinhood Chain or USDC on Base, Arbitrum or Polygon (EVM address). Payments are gasless; swaps need a little ETH.
1969
+ Fund it with a few dollars of USDC on Solana (Solana address), or USDG on Robinhood Chain or USDC on Base, Arbitrum, Polygon, Arc or Soneium (EVM address). Payments are gasless; swaps need a little ETH.
1923
1970
  Caps: $0.10 per payment, $1 per day, $25 per swap. Change with \`wafermap limits\`.`);
1924
1971
  return;
1925
1972
  }
@@ -1994,7 +2041,7 @@ async function main() {
1994
2041
  const rows = mergeLog(spendLog()).filter((e) => e.at >= since).reverse();
1995
2042
  if (!rows.length) return console.log(`Nothing in the last ${days} days.`);
1996
2043
  const verify = a.flags.verify !== void 0;
1997
- const RPC = { robinhood: process.env.WAFERMAP_RPC_ROBINHOOD, base: process.env.WAFERMAP_RPC_BASE, arbitrum: process.env.WAFERMAP_RPC_ARBITRUM, polygon: process.env.WAFERMAP_RPC_POLYGON, solana: process.env.WAFERMAP_RPC_SOLANA };
2044
+ const RPC = { robinhood: process.env.WAFERMAP_RPC_ROBINHOOD, base: process.env.WAFERMAP_RPC_BASE, arbitrum: process.env.WAFERMAP_RPC_ARBITRUM, polygon: process.env.WAFERMAP_RPC_POLYGON, arc: process.env.WAFERMAP_RPC_ARC, soneium: process.env.WAFERMAP_RPC_SONEIUM, solana: process.env.WAFERMAP_RPC_SOLANA };
1998
2045
  const chain = [];
1999
2046
  if (verify) {
2000
2047
  const txCount = /* @__PURE__ */ new Map();
@@ -2039,18 +2086,21 @@ Total $${total.toFixed(4)} in ${pays.length} payments. From 0.3.3 every payment
2039
2086
  ${s.description.slice(0, 140)}${s.input ? `
2040
2087
  input: ${JSON.stringify(s.input).slice(0, 140)}` : ""}`);
2041
2088
  return console.log(`
2042
- \u25CF payable with USDG (Robinhood Chain) or USDC (Solana, Base, Arbitrum, Polygon) \u25CB other networks only`);
2089
+ \u25CF payable with USDG (Robinhood Chain) or USDC (Solana, Base, Arbitrum, Polygon, Arc, Soneium) \u25CB other networks only`);
2043
2090
  }
2044
2091
  case "call": {
2045
2092
  const id = a._[0] ?? die("Which resource? e.g. wafermap call gas-now network=base");
2046
2093
  const wm = openWallet(await password(), approver(yes));
2047
2094
  const payIn = a.flags.pay === "wafermap" ? "wafermap" : a.flags.pay === void 0 || a.flags.pay === "usd" ? void 0 : die("--pay must be wafermap (or leave it out to pay in USDG/USDC)");
2048
- const out = await wm.call(id, inputOf(a, 1), { network: net(a.flags.network), payIn });
2095
+ let streamed = false;
2096
+ const onDelta = a.flags.stream === true ? (t) => (streamed = true, process.stdout.write(t)) : void 0;
2097
+ const out = await wm.call(id, inputOf(a, 1), { network: net(a.flags.network), payIn, onDelta });
2098
+ if (streamed) process.stdout.write("\n");
2049
2099
  if (out.receipt?.asset === "WAFERMAP") console.error(`\u2713 Paid ${out.receipt.amountTokens} $WAFERMAP (\u2248 $${out.receipt.amountUsd}), burned \xB7 ${EXPLORER(out.receipt.network)}/tx/${out.receipt.tx}`);
2050
2100
  else if (out.receipt) console.error(`\u2713 Paid $${out.receipt.amountUsd} ${out.receipt.asset} \xB7 ${EXPLORER(out.receipt.network)}/tx/${out.receipt.tx}`);
2051
2101
  else if (out.free) console.error(`\u2713 Free call ($WAFERMAP Pro) \xB7 ${out.free.left} of ${out.free.perDay} left today. Nothing was paid.`);
2052
2102
  if (typeof a.flags.out === "string") await saveMedia(out.result, a.flags.out);
2053
- return printResult(out.result);
2103
+ return streamed ? void 0 : printResult(out.result);
2054
2104
  }
2055
2105
  case "pay": {
2056
2106
  const url = a._[0] ?? die("Which URL? e.g. wafermap pay https://example.com/api city=Paris");
@@ -2063,7 +2113,7 @@ Total $${total.toFixed(4)} in ${pays.length} payments. From 0.3.3 every payment
2063
2113
  case "balance": {
2064
2114
  const addr = keystoreAddress() ?? die("No wallet yet. Run `wafermap init`.");
2065
2115
  const { Wafermap: Wafermap2 } = await Promise.resolve().then(() => (init_core(), core_exports));
2066
- const wm = new Wafermap2({ account: { ...privateKeyToAccount2(generatePrivateKey()), address: addr }, solanaAddress: keystoreSolana() ?? void 0, baseUrl: process.env.WAFERMAP_URL, rpc: { robinhood: process.env.WAFERMAP_RPC_ROBINHOOD, base: process.env.WAFERMAP_RPC_BASE, arbitrum: process.env.WAFERMAP_RPC_ARBITRUM, polygon: process.env.WAFERMAP_RPC_POLYGON, solana: process.env.WAFERMAP_RPC_SOLANA } });
2116
+ const wm = new Wafermap2({ account: { ...privateKeyToAccount2(generatePrivateKey()), address: addr }, solanaAddress: keystoreSolana() ?? void 0, baseUrl: process.env.WAFERMAP_URL, rpc: { robinhood: process.env.WAFERMAP_RPC_ROBINHOOD, base: process.env.WAFERMAP_RPC_BASE, arbitrum: process.env.WAFERMAP_RPC_ARBITRUM, polygon: process.env.WAFERMAP_RPC_POLYGON, arc: process.env.WAFERMAP_RPC_ARC, soneium: process.env.WAFERMAP_RPC_SONEIUM, solana: process.env.WAFERMAP_RPC_SOLANA } });
2067
2117
  const b = await wm.balances();
2068
2118
  console.log(addr);
2069
2119
  for (const n of NETS) console.log(` ${n.padEnd(10)} ${b[n] ? `${Number(b[n].stable).toFixed(4)} ${b[n].symbol} ${Number(b[n].eth).toPrecision(4)} ${b[n].gas}` : n === "solana" && !wm.solanaAddress ? "run `wafermap address` once with WAFERMAP_PASSWORD set" : "unreadable (RPC did not answer)"}`);
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/core.ts
2
2
  import { createPublicClient, createWalletClient, erc20Abi as erc20Abi2, formatUnits as formatUnits2, http, pad, parseAbiItem, toEventSelector } from "viem";
3
3
  import { privateKeyToAccount } from "viem/accounts";
4
- import { arbitrum, base, polygon, robinhood } from "viem/chains";
4
+ import { arbitrum, arc, base, polygon, robinhood, soneium } from "viem/chains";
5
5
 
6
6
  // shared/swap.ts
7
7
  import { decodeFunctionData, erc20Abi, formatUnits, getAddress, isAddress, parseAbi, parseUnits } from "viem";
@@ -357,7 +357,10 @@ var PAY_CHAINS = [
357
357
  { key: "solana", vm: "svm", id: 0, caip: SOLANA_CAIP, v1: "solana", stable: SOLANA_USDC, symbol: "USDC", name: "Solana" },
358
358
  { key: "base", vm: "evm", id: 8453, caip: "eip155:8453", v1: "base", stable: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", symbol: "USDC", name: "Base", domain: USDC_DOMAIN },
359
359
  { key: "arbitrum", vm: "evm", id: 42161, caip: "eip155:42161", v1: "arbitrum", stable: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", symbol: "USDC", name: "Arbitrum", domain: USDC_DOMAIN },
360
- { key: "polygon", vm: "evm", id: 137, caip: "eip155:137", v1: "polygon", stable: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", symbol: "USDC", name: "Polygon", domain: USDC_DOMAIN }
360
+ { key: "polygon", vm: "evm", id: 137, caip: "eip155:137", v1: "polygon", stable: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", symbol: "USDC", name: "Polygon", domain: USDC_DOMAIN },
361
+ // EIP-712 domains read from the token contracts (name(), version()) and checked against DOMAIN_SEPARATOR().
362
+ { key: "arc", vm: "evm", id: 5042, caip: "eip155:5042", v1: "arc", stable: "0x3600000000000000000000000000000000000000", symbol: "USDC", name: "Arc", domain: { name: "USDC", version: "2" } },
363
+ { key: "soneium", vm: "evm", id: 1868, caip: "eip155:1868", v1: "soneium", stable: "0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369", symbol: "USDC", name: "Soneium", domain: { name: "Bridged USDC (Soneium)", version: "2" } }
361
364
  ];
362
365
  var EVM_CHAINS = PAY_CHAINS.filter((c) => c.vm === "evm");
363
366
  var CHAIN_KEYS = PAY_CHAINS.map((c) => c.key);
@@ -466,7 +469,7 @@ function choose(q, prefer = CHAIN_KEYS) {
466
469
  const r = q.accepts.find((a) => a.chain.key === k);
467
470
  if (r) return r;
468
471
  }
469
- throw new X402Error("no_supported_payment", q.offered.length ? `This service only accepts ${[...new Set(q.offered)].join(", ")}. Wafermap pays USDG on Robinhood Chain or USDC on Solana, Base, Arbitrum or Polygon.` : "The service offered no payment option.");
472
+ throw new X402Error("no_supported_payment", q.offered.length ? `This service only accepts ${[...new Set(q.offered)].join(", ")}. Wafermap pays USDG on Robinhood Chain or USDC on Solana, Base, Arbitrum, Polygon, Arc or Soneium.` : "The service offered no payment option.");
470
473
  }
471
474
  var TYPES = {
472
475
  TransferWithAuthorization: [
@@ -601,7 +604,7 @@ var WafermapError = class extends Error {
601
604
  }
602
605
  code;
603
606
  };
604
- var CHAINS = { robinhood, base, arbitrum, polygon };
607
+ var CHAINS = { robinhood, base, arbitrum, polygon, arc, soneium };
605
608
  var AUTH_STATE_ABI = [{ type: "function", name: "authorizationState", stateMutability: "view", inputs: [{ name: "authorizer", type: "address" }, { name: "nonce", type: "bytes32" }], outputs: [{ type: "bool" }] }];
606
609
  function mergeLog(entries) {
607
610
  const outcomes = new Map(entries.filter((e) => e.of).map((e) => [e.of, e]));
@@ -868,19 +871,59 @@ var Wafermap = class {
868
871
  throw new WafermapError("approve_failed", e?.shortMessage ?? e?.message ?? "The approval failed.");
869
872
  }
870
873
  }
874
+ /** Reads /v1's server-sent events for an LLM call: delta pieces as they arrive, then receipt (or error), then done. */
875
+ async readStream(res, onDelta) {
876
+ const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
877
+ let buf = "";
878
+ let receipt = null;
879
+ let done = null;
880
+ let error = null;
881
+ for (; ; ) {
882
+ const { value, done: end } = await reader.read();
883
+ if (end) break;
884
+ buf += value;
885
+ let i;
886
+ while ((i = buf.indexOf("\n\n")) >= 0) {
887
+ const block = buf.slice(0, i);
888
+ buf = buf.slice(i + 2);
889
+ const ev = /^event: (.+)$/m.exec(block)?.[1];
890
+ let data = null;
891
+ try {
892
+ data = JSON.parse(/^data: (.*)$/m.exec(block)?.[1] ?? "null");
893
+ } catch {
894
+ }
895
+ if (ev === "delta" && typeof data?.text === "string") onDelta(data.text);
896
+ else if (ev === "receipt") receipt = data;
897
+ else if (ev === "done") done = data;
898
+ else if (ev === "error") error = data;
899
+ }
900
+ }
901
+ return { receipt, done, error };
902
+ }
871
903
  async call(id, input = {}, o = {}) {
872
904
  const inToken = o.payIn === "wafermap";
905
+ const stream = !!o.onDelta && !inToken;
873
906
  if (inToken && id.startsWith("m/")) throw new WafermapError("token_not_offered", "Marketplace listings are paid in USDG or USDC to their seller, not in $WAFERMAP.");
874
907
  if (inToken && !await this.wafermapApproved().catch(() => true)) throw new WafermapError("permit2_not_approved", "Paying in $WAFERMAP needs a one-time approval of Permit2. Run `wafermap approve-wafermap` (one transaction on Robinhood Chain, paid in ETH), then call again.");
875
908
  const pass = id.startsWith("m/") ? null : await this.holderPass();
876
- const go = () => this.paidFetch("resource", id, `${this.baseUrl}/v1/${id}`, { method: "POST", headers: { "content-type": "application/json", "x-wafermap-payer": this.address, ...pass ? { [PASS_HEADER]: pass } : {} }, body: JSON.stringify(input) }, o.network, inToken ? "wafermap" : void 0);
909
+ const go = () => this.paidFetch("resource", id, `${this.baseUrl}/v1/${id}`, { method: "POST", headers: { "content-type": "application/json", "x-wafermap-payer": this.address, ...stream ? { accept: "text/event-stream" } : {}, ...pass ? { [PASS_HEADER]: pass } : {} }, body: JSON.stringify(input) }, o.network, inToken ? "wafermap" : void 0);
877
910
  const settle = (r2, out2) => {
878
911
  if (!r2.paid || !r2.id) return;
879
- const status = r2.res.status === 402 ? "refused" : r2.res.ok ? "ok" : "failed";
912
+ const status = r2.res.status === 402 ? "refused" : r2.res.ok && !out2?.streamError ? "ok" : "failed";
880
913
  this.outcome(r2.id, "resource", id, r2.paid, status, out2?.receipt?.tx ?? r2.paid.tx, typeof out2?.receipt?.amountUsd === "number" ? out2.receipt.amountUsd : null);
881
914
  };
882
915
  const host = new URL(this.baseUrl).host;
883
- const read = (r2) => this.body(r2.res, () => r2.res.json(), {}, host, r2.paid?.auth ?? (r2.paid ? {} : null));
916
+ const read = (r2) => this.body(
917
+ r2.res,
918
+ async () => {
919
+ if (!(stream && r2.res.ok && (r2.res.headers.get("content-type") ?? "").includes("text/event-stream"))) return r2.res.json();
920
+ const s = await this.readStream(r2.res, o.onDelta);
921
+ return { resource: id, result: { text: s.done?.text ?? "", model: s.done?.model, usage: s.done?.usage }, receipt: s.receipt, streamError: s.error };
922
+ },
923
+ {},
924
+ host,
925
+ r2.paid?.auth ?? (r2.paid ? {} : null)
926
+ );
884
927
  let r = await go();
885
928
  let out = await read(r);
886
929
  settle(r, out);
@@ -895,6 +938,7 @@ var Wafermap = class {
895
938
  const { res, paid } = r;
896
939
  const refused = this.rejected(res, out, paid);
897
940
  if (refused) throw refused;
941
+ if (out?.streamError) throw new WafermapError(out.streamError.code ?? "stream_failed", out.streamError.message ?? "The answer did not finish. You were not charged.");
898
942
  if (!res.ok) throw new WafermapError(out?.error?.code ?? "http_error", out?.error?.message ?? (typeof out?.error === "string" ? out.error : `HTTP ${res.status}`));
899
943
  return out;
900
944
  }
@@ -10,7 +10,7 @@ export type Signer = {
10
10
  message: any;
11
11
  }) => Promise<Hex>;
12
12
  };
13
- export type ChainKey = "robinhood" | "base" | "arbitrum" | "polygon" | "solana";
13
+ export type ChainKey = "robinhood" | "base" | "arbitrum" | "polygon" | "arc" | "soneium" | "solana";
14
14
  export type Chain = {
15
15
  key: ChainKey;
16
16
  vm: "evm" | "svm";
@@ -61,7 +61,7 @@ export type WafermapOptions = {
61
61
  solanaAddress?: string;
62
62
  /** Default https://wafermap.xyz */
63
63
  baseUrl?: string;
64
- /** Preferred payment network. Default robinhood (USDG); base, arbitrum, polygon and solana pay in USDC. */
64
+ /** Preferred payment network. Default robinhood (USDG); base, arbitrum, polygon, arc, soneium and solana pay in USDC. */
65
65
  network?: Net;
66
66
  limits?: Partial<Limits>;
67
67
  /** Where spending is remembered for the daily cap. Default: in memory. */
@@ -155,9 +155,12 @@ export declare class Wafermap {
155
155
  * transaction on Robinhood Chain, paid in ETH. Each payment after that is a signature for one exact amount,
156
156
  * spendable only by Wafermap's relayer for two minutes. */
157
157
  approveWafermap(): Promise<string>;
158
+ /** Reads /v1's server-sent events for an LLM call: delta pieces as they arrive, then receipt (or error), then done. */
159
+ private readStream;
158
160
  call(id: string, input?: Record<string, unknown>, o?: {
159
161
  network?: Net;
160
162
  payIn?: "stablecoin" | "wafermap";
163
+ onDelta?: (text: string) => void;
161
164
  }): Promise<{
162
165
  resource: string;
163
166
  result: any;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wafermapxyz/cli",
3
- "version": "0.4.7",
4
- "description": "Wafermap for agents: an encrypted local wallet that pays x402 resources, discovers and pays third-party x402 services (USDG on Robinhood Chain; USDC on Solana, Base, Arbitrum, Polygon), and swaps on Robinhood Chain and Base. CLI, SDK and MCP server.",
3
+ "version": "0.4.9",
4
+ "description": "Wafermap for agents: an encrypted local wallet that pays x402 resources, discovers and pays third-party x402 services (USDG on Robinhood Chain; USDC on Solana, Base, Arbitrum, Polygon, Arc, Soneium), and swaps on Robinhood Chain and Base. CLI, SDK and MCP server.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "wafermap": "dist/cli.js"
@@ -36,6 +36,8 @@
36
36
  "base",
37
37
  "arbitrum",
38
38
  "polygon",
39
+ "arc",
40
+ "soneium",
39
41
  "solana",
40
42
  "usdg",
41
43
  "usdc"