@wafermapxyz/cli 0.0.0-stage → 0.4.1
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 +42 -0
- package/LICENSE +21 -0
- package/README.md +114 -3
- package/dist/cli.js +1771 -0
- package/dist/index.js +719 -0
- package/dist/types/shared/solana.d.ts +36 -0
- package/dist/types/shared/swap.d.ts +78 -0
- package/dist/types/shared/x402client.d.ts +116 -0
- package/dist/types/src/core.d.ts +184 -0
- package/dist/types/src/index.d.ts +5 -0
- package/package.json +68 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.1 (2 Nov 2026)
|
|
4
|
+
|
|
5
|
+
On npm as **`@wafermapxyz/cli`**: `npm i -g @wafermapxyz/cli` (SDK: `import { Wafermap } from "@wafermapxyz/cli"`). The command stays `wafermap`, the wallet stays in `~/.wafermap`. The registry refuses the bare name `wafermap` for everyone, so the package lives under our own scope. If you installed from the tarball, run `npm rm -g wafermap` before installing the scoped package. No code changes since 0.4.0.
|
|
6
|
+
|
|
7
|
+
## 0.4.0 (2 Nov 2026)
|
|
8
|
+
|
|
9
|
+
From the second public review by @whoiskn0why, thank you.
|
|
10
|
+
|
|
11
|
+
- **Shorter payment authorizations.** EVM payments are signed for at most 90 seconds (was up to 300), or less when the server asks for less. That is the window in which "a failed call costs $0" rests on the server's word; `wafermap history --verify` still checks every one on-chain afterwards.
|
|
12
|
+
- **Time limits.** Requests to paid resources and services, Wafermap's API, the swap API and Solana RPC give up after 60 seconds (`timeoutMs` in the SDK, `DEFAULT_TIMEOUT_MS`); EVM RPC keeps viem's time-outs. Before, a server that never answered held a call forever. A paid call that times out after signing says the payment stays counted (and, for EVM payments, until when it can be executed); one that times out before signing says nothing was paid.
|
|
13
|
+
- **Swaps are checked before anything is signed.** The build now comes first and the approval after it. The quote and the build must name KyberSwap's router (`KYBER_ROUTER`, `0x6131B5fae19EA4f9D964eAc0408E4408b66337b5`). The swap calldata is decoded (`decodeSwap`) and must trade the quoted tokens, spend exactly the quoted amount, pay the output to this wallet, take no fee, and set an on-chain minimum output no lower than the built output less your slippage; the built output must be within your slippage of the quote, and ETH is sent only when selling ETH. Otherwise nothing is approved or sent (`unexpected_router`, `unexpected_calldata`, `build_mismatch`, `price_moved`). `amountOutMin` is now the minimum encoded in the transaction.
|
|
14
|
+
- **The MCP password can leave the config file.** `wafermap keychain on` stores it with Windows DPAPI, the macOS Keychain or the Linux Secret Service; `wafermap mcp` reads it from there, and warns when `WAFERMAP_PASSWORD` is still set in plain text.
|
|
15
|
+
- An empty `WAFERMAP_PASSWORD` counts as not set. `wafermap keychain off` checks that the password is really gone.
|
|
16
|
+
- Tests for all of the above (`test/hardening.test.mjs`: real DPAPI on Windows; the macOS and Linux stores are exercised against a stub). CI also runs on Windows and macOS.
|
|
17
|
+
- The SDK exports `quoteSwap`, `executeSwap`, `decodeSwap`, `KYBER_ROUTER`, `MAX_VALIDITY_SECONDS` and `DEFAULT_TIMEOUT_MS`.
|
|
18
|
+
|
|
19
|
+
npm: the name `wafermap` is refused by the registry for everyone (too similar to an existing package). From 0.4.1 the package is `@wafermapxyz/cli`.
|
|
20
|
+
|
|
21
|
+
## 0.3.3 (1 Nov 2026)
|
|
22
|
+
|
|
23
|
+
**Fix: the daily cap no longer trusts the server.** Reported publicly in a code review by @whoiskn0why, thank you.
|
|
24
|
+
|
|
25
|
+
Before: the spend record was written after the call, from the server's receipt (`call()`), or as $0 when the server answered with an error (`pay()`). A server that reported $0, sent no receipt, answered with an error or dropped the connection could take signed payments that never counted towards the daily cap. With a $0.10 cap, $0.15 in signed payments could go through.
|
|
26
|
+
|
|
27
|
+
Now:
|
|
28
|
+
- Every payment is recorded at its full signed amount the moment it is signed, before it is sent, and stays counted whatever the server answers. The outcome (ok / failed / refused) is written as a second line that adds nothing to the total.
|
|
29
|
+
- Check, sign and record run one payment at a time within a process, so concurrent calls cannot both pass the cap on the same remaining budget (found while writing the tests). Across processes sharing one wallet (two MCP clients, CLI + MCP), each signed payment is checked again against the shared log before it is sent and dropped, never sent, if another process used the budget meanwhile (found in our own adversarial review).
|
|
30
|
+
- `wafermap history --verify` (SDK: `checkPayment`) reads `authorizationState` on the token contract to show which signed EVM payments were executed and which expired unused, without trusting any server.
|
|
31
|
+
- `wafermap history` shows each payment's status and notes that log lines from before 0.3.3 hold what the server reported.
|
|
32
|
+
- Tests (`npm test`): the caps against a local mock server that lies, errors, refuses or hangs up; CI on Node 20 and 22.
|
|
33
|
+
|
|
34
|
+
Older `spend.jsonl` lines are still read; `mergeLog` joins the new two-line records.
|
|
35
|
+
|
|
36
|
+
## 0.3.2
|
|
37
|
+
|
|
38
|
+
Opt-in per-payment confirmation for the MCP server (`wafermap limits --mcp-confirm on`).
|
|
39
|
+
|
|
40
|
+
## 0.3.1
|
|
41
|
+
|
|
42
|
+
First public source release: CLI, SDK and MCP server.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wafermap
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,114 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# wafermap
|
|
2
|
+
|
|
3
|
+
The CLI, SDK and MCP server behind [Wafermap](https://wafermap.xyz): an x402 wallet for AI agents. One local, encrypted key that can:
|
|
4
|
+
|
|
5
|
+
- **call** Wafermap's pay-per-call resources (LLMs, image and audio models, web reading, on-chain data, tokenized stocks, prediction markets)
|
|
6
|
+
- **discover** third-party x402 services in the open x402 index and **pay** them
|
|
7
|
+
- **swap** tokens through the KyberSwap aggregator
|
|
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.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i -g @wafermapxyz/cli
|
|
15
|
+
wafermap init
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The command is `wafermap`. The package is **`@wafermapxyz/cli`** on npm (the `wafermapxyz` scope, after the wafermap.xyz domain), the same build as https://wafermap.xyz/cli/wafermap.tgz and this repository. The bare name `wafermap` on npm is **not** this package: the registry refuses it for everyone. Coming from the tarball install? Run `npm rm -g wafermap` first; your wallet in `~/.wafermap` stays.
|
|
19
|
+
|
|
20
|
+
`init` creates a key and encrypts it with your password (scrypt + AES-256-GCM) in `~/.wafermap/keystore.json`. Back it up with `wafermap export-key`. Then send a few dollars of USDC on Solana to the Solana address it prints, or USDG (Robinhood Chain) or USDC (Base, Arbitrum, Polygon) to its EVM address.
|
|
21
|
+
|
|
22
|
+
The Solana address comes from the same key (derived with HMAC-SHA256), so one backup covers both. `wafermap export-key --solana` prints the Solana secret key in the form Phantom and Solflare import. `wafermap address` shows both addresses.
|
|
23
|
+
|
|
24
|
+
## CLI
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
wafermap balance
|
|
28
|
+
wafermap limits --per-payment 0.1 --per-day 1 --per-swap 25 --network robinhood
|
|
29
|
+
wafermap resources stock
|
|
30
|
+
wafermap call prediction-markets query="fed rates"
|
|
31
|
+
wafermap call flux-2-dev prompt="a glossy silicon wafer" --out wafer.png
|
|
32
|
+
wafermap discover weather
|
|
33
|
+
wafermap pay https://x402.agentindex.world/weather city=Jakarta
|
|
34
|
+
wafermap swap 0.01 ETH USDG
|
|
35
|
+
wafermap history
|
|
36
|
+
```
|
|
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.
|
|
39
|
+
|
|
40
|
+
## MCP server
|
|
41
|
+
|
|
42
|
+
Give Claude Desktop, Cursor or any MCP client the same wallet. First put the wallet password in your operating system's keychain, once, in a terminal:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
wafermap keychain on
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Then the client config holds no secret:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"wafermap": {
|
|
54
|
+
"command": "wafermap",
|
|
55
|
+
"args": ["mcp"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`wafermap keychain on` stores the password with Windows DPAPI (a file only your Windows user can decrypt), in the macOS Keychain, or in the Secret Service on Linux (GNOME Keyring, KWallet, through `secret-tool`). `wafermap keychain` shows whether it is set and still unlocks the wallet; `wafermap keychain off` removes it. This keeps the password out of config files, backups and screenshots. It does not stop a program already running as you, which could also ask the OS for it. Setting `WAFERMAP_PASSWORD` in the client's `env` still works, but the password then sits in plain text in that config file, and the server says so when it starts.
|
|
62
|
+
|
|
63
|
+
Tools: `wafermap_wallet`, `wafermap_resources`, `wafermap_call`, `wafermap_discover`, `wafermap_pay`, `wafermap_swap_quote`, `wafermap_swap`, `wafermap_history`. Swaps from MCP are off until you run `wafermap limits --mcp-swaps on`.
|
|
64
|
+
|
|
65
|
+
To approve every payment yourself, run `wafermap limits --mcp-confirm on` and restart the MCP server. Each payment and swap then waits for you in the client, showing the amount, asset, recipient, network and resource, before the wallet signs. It uses MCP elicitation; a client that cannot show the prompt gets a refusal and nothing is paid. The caps still apply first.
|
|
66
|
+
|
|
67
|
+
## SDK
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { Wafermap } from "@wafermapxyz/cli";
|
|
71
|
+
|
|
72
|
+
const wm = new Wafermap({ privateKey: process.env.AGENT_KEY as `0x${string}`, limits: { perPayment: 0.05, perDay: 2 } });
|
|
73
|
+
|
|
74
|
+
const { result, receipt } = await wm.call("stock-signal", { symbol: "TSLA" });
|
|
75
|
+
const services = await wm.discover("token price");
|
|
76
|
+
const paid = await wm.pay(services[0].url, { method: "POST", input: { symbol: "ETH" }, network: "base" });
|
|
77
|
+
const quote = await wm.swapQuote("ETH", "USDG", "0.01");
|
|
78
|
+
await wm.swap(quote);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Pass `approve: (a) => boolean` to confirm each payment or swap yourself, and `ledger` to persist spending for the daily cap. Lower level: `x402fetch(url, init, { signer })` pays any x402 v1 or v2 endpoint.
|
|
82
|
+
|
|
83
|
+
## Where the limits live
|
|
84
|
+
|
|
85
|
+
The caps sit with the key, on your machine. Before the wallet signs anything it reads the 402 quote and refuses when the price is above the per-payment cap or would take today's spend past the daily cap, so nothing is signed and nothing can be charged. With `--mcp-confirm on`, you also approve each MCP payment yourself before it is signed. A budget written in a prompt is an instruction, not a limit: set it with `wafermap limits`. More: https://wafermap.xyz/docs#limits
|
|
86
|
+
|
|
87
|
+
**The daily cap counts what the wallet signed, not what a server says it charged.** Every payment is written to `spend.jsonl` at its full signed amount the moment it is signed, before it is sent, and stays counted whatever the server answers: an error, a receipt saying $0, no receipt, or no answer at all. Check, sign and record run one payment at a time within a process, so calls started together cannot share the same remaining budget. Across processes on the same wallet (two MCP clients, or the CLI next to the MCP server), the wallet re-reads the shared log right after recording a signed payment and, if the cap is now passed, drops that payment before it is sent. (Before 0.3.3 the cap trusted the server's receipt; see [CHANGELOG.md](CHANGELOG.md).)
|
|
88
|
+
|
|
89
|
+
**What the wallet cannot enforce.** An x402 `exact` payment is a signed authorization (EIP-3009 on EVM chains, a partially signed transaction on Solana). Whoever receives it can execute it once until it expires. The wallet signs EVM authorizations for at most 90 seconds, or less when the server asks for less (300 before 0.4.0). "A failed call costs $0" is therefore a promise of the server you pay: Wafermap's server settles only after the resource worked and otherwise lets the authorization expire unused. You do not have to take that on trust: `wafermap history --verify` asks each token contract (`authorizationState`) whether every signed payment was executed, and shows the ones that expired unused (`error` means the RPC did not answer). Solana payments are not checked yet. The caps bound the worst case either way.
|
|
90
|
+
|
|
91
|
+
**Time limits.** Requests to paid resources and services, to Wafermap's API, to the swap API and to Solana RPC give up after 60 seconds (`timeoutMs` in the SDK); EVM RPC calls keep viem's own time-outs. A paid call that times out after the payment was signed says so: the payment stays counted, and for EVM payments the message says until when it can be executed.
|
|
92
|
+
|
|
93
|
+
**Swaps.** The wallet builds the swap, checks it, and only then approves and sends. The swap must go to KyberSwap's router (`0x6131B5fae19EA4f9D964eAc0408E4408b66337b5` on Robinhood Chain and Base). The calldata itself is decoded and must trade the quoted tokens, spend exactly the quoted amount, send the output to this wallet, take no fee, and carry an on-chain minimum output of the built output less your slippage, where the built output must be within your slippage of the quote. ETH is sent only when selling ETH, and an ERC-20 approval is for exactly the amount, to that router, and only when the allowance is lower. The router and the route it executes are KyberSwap's: the wallet checks what the call asks for, not what the router contract does.
|
|
94
|
+
|
|
95
|
+
The checks are in [`src/core.ts`](src/core.ts) (`gate`, `paidFetch`, `checkPayment`), the keystore in [`src/store.ts`](src/store.ts), payment signing in [`shared/x402client.ts`](shared/x402client.ts) and [`shared/solana.ts`](shared/solana.ts). The tests in [`test/caps.test.mjs`](test/caps.test.mjs) run the caps against a local server that lies; [`test/hardening.test.mjs`](test/hardening.test.mjs) covers expiry, time limits, swap checks and the keychain (real DPAPI on Windows; on macOS and Linux the `security` and `secret-tool` calls run against a stub, not a real keychain). CI runs them on Linux, Windows and macOS.
|
|
96
|
+
|
|
97
|
+
## Build from source
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git clone https://github.com/wafermap/wafermap-cli.git && cd wafermap-cli
|
|
101
|
+
npm ci
|
|
102
|
+
npm run build # dist/cli.js, dist/index.js and type declarations (strict tsc)
|
|
103
|
+
npm test # the caps against a mock server that misbehaves
|
|
104
|
+
node dist/cli.js --help
|
|
105
|
+
npm i -g . # optional: install this build as `wafermap`
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Layout: `src/` is the CLI, SDK and MCP server; `shared/` is the x402 client, the Solana payment builder, the swap helpers and the resource catalog, the same files the Wafermap site uses.
|
|
109
|
+
|
|
110
|
+
## Files
|
|
111
|
+
|
|
112
|
+
`~/.wafermap/keystore.json` (encrypted key), `config.json` (caps, network), `spend.jsonl` (history). Override the folder with `WAFERMAP_HOME`, the RPCs with `WAFERMAP_RPC_ROBINHOOD` / `WAFERMAP_RPC_BASE`.
|
|
113
|
+
|
|
114
|
+
Docs: https://wafermap.xyz/docs#cli · Security: [SECURITY.md](SECURITY.md) · License: [MIT](LICENSE)
|