@clawcard/cli 2.1.8 → 3.0.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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @clawcard/cli
2
2
 
3
- On-demand credit cards, email, and phone for your AI agents.
3
+ The complete identity for autonomous AI agents.
4
4
 
5
- ClawCard gives your agent a full identity dedicated email inbox, SMS-enabled US phone number, virtual Mastercards with spend limits, and an encrypted credential vault. Your agent interacts with ClawCard through CLI commands the API key never enters the agent's context.
5
+ One command gives your agent email, phone, virtual cards, a USDC crypto wallet, on-chain identity, and access to every paid API on the internet. Works with Claude Code, Cursor, Gemini CLI, and any MCP-compatible agent.
6
6
 
7
7
  ## Install
8
8
 
@@ -26,28 +26,81 @@ clawcard keys create
26
26
  clawcard setup
27
27
  ```
28
28
 
29
- `clawcard setup` uses [skills.sh](https://skills.sh) to install the ClawCard skill for your agent. Works with Claude Code, Cursor, Codex, Cline, OpenClaw, and 40+ other agents.
29
+ `clawcard setup` uses [skills.sh](https://skills.sh) to install the ClawCard skill for your agent.
30
+
31
+ ## What Your Agent Gets
32
+
33
+ | Capability | Description |
34
+ |---|---|
35
+ | Email | Dedicated inbox at `@mail.clawcard.sh` |
36
+ | Phone | SMS-enabled US phone number |
37
+ | Virtual Cards | Instant Visa cards with per-card spend limits |
38
+ | Crypto Wallet | USDC on Base — gasless transfers, x402 + MPP payments |
39
+ | Service Discovery | Find and pay for any API in the x402 ecosystem |
40
+ | On-Chain Identity | ERC-8004 NFT — verifiable, portable agent identity |
41
+ | Credentials | AES-256 encrypted vault for API keys and secrets |
42
+ | Spending Guardrails | Per-agent, per-transaction, per-day limits |
30
43
 
31
44
  ## Agent Commands
32
45
 
33
46
  Your agent runs these commands directly. All support `--json` for machine-parseable output.
34
47
 
48
+ ### Identity & Info
49
+
50
+ | Command | Description |
51
+ |---|---|
52
+ | `clawcard agent info --json` | Agent identity (email, phone, wallet, budget) |
53
+ | `clawcard agent budget --json` | Check remaining FIAT budget |
54
+ | `clawcard agent activity --json` | View activity log |
55
+
56
+ ### Communication
57
+
35
58
  | Command | Description |
36
59
  |---|---|
37
- | `clawcard agent info --json` | Agent identity (email, phone, budget) |
38
60
  | `clawcard agent emails --json` | List inbox |
39
61
  | `clawcard agent emails send --to --subject --body --json` | Send email |
40
62
  | `clawcard agent sms --json` | List SMS messages |
41
63
  | `clawcard agent sms send --to --body --json` | Send SMS |
64
+
65
+ ### Virtual Cards (FIAT)
66
+
67
+ | Command | Description |
68
+ |---|---|
42
69
  | `clawcard agent cards --json` | List virtual cards |
43
70
  | `clawcard agent cards create --amount --type --memo --json` | Create card |
44
71
  | `clawcard agent cards details <id> --json` | Get PAN, CVV, expiry |
45
72
  | `clawcard agent cards close <id> --json` | Close card |
73
+
74
+ ### Crypto Wallet (USDC on Base)
75
+
76
+ | Command | Description |
77
+ |---|---|
78
+ | `clawcard agent wallet --json` | View wallet (or create one) |
79
+ | `clawcard agent wallet balance --json` | Balance + spending power |
80
+ | `clawcard agent wallet fund --amount <dollars> --json` | Fund wallet from FIAT balance |
81
+ | `clawcard agent wallet send --to <0x> --amount <usdc> --json` | Send USDC |
82
+ | `clawcard agent wallet send --url <url> --json` | Pay an x402 API |
83
+ | `clawcard agent wallet send --url <url> --protocol mpp --json` | Pay an MPP API |
84
+ | `clawcard agent wallet transactions --json` | Transaction history |
85
+ | `clawcard agent wallet freeze --json` | Freeze wallet |
86
+ | `clawcard agent wallet unfreeze --json` | Unfreeze wallet |
87
+ | `clawcard agent wallet close --json` | Close wallet permanently |
88
+
89
+ ### Discovery & Identity
90
+
91
+ | Command | Description |
92
+ |---|---|
93
+ | `clawcard agent discover --query "web search" --json` | Find x402 services |
94
+ | `clawcard agent identity --json` | Register/view ERC-8004 on-chain identity |
95
+ | `clawcard agent card --json` | View A2A agent card (discovery document) |
96
+
97
+ ### Credentials
98
+
99
+ | Command | Description |
100
+ |---|---|
46
101
  | `clawcard agent creds --json` | List stored credentials |
47
102
  | `clawcard agent creds set --service --key --value --json` | Store credential |
48
103
  | `clawcard agent creds get --service --key --json` | Retrieve credential |
49
- | `clawcard agent budget --json` | Check remaining budget |
50
- | `clawcard agent activity --json` | View activity log |
51
104
 
52
105
  ## User Commands
53
106
 
@@ -60,33 +113,45 @@ Your agent runs these commands directly. All support `--json` for machine-parsea
60
113
  | `clawcard agent` | Show agent identity |
61
114
  | `clawcard agent fund` | Add budget to your agent |
62
115
  | `clawcard keys create` | Create agent key |
63
- | `clawcard keys revoke` | Revoke key (exports credentials first) |
116
+ | `clawcard keys revoke` | Revoke key |
64
117
  | `clawcard setup` | Install ClawCard skill |
65
118
  | `clawcard billing` | Billing dashboard |
66
119
  | `clawcard billing topup` | Top up balance |
67
120
  | `clawcard billing balance` | Quick balance check |
68
121
  | `clawcard referral` | Show referral code |
122
+ | `clawcard settings` | Manage billing address |
69
123
  | `clawcard help` | Show all commands |
70
124
 
71
125
  ## How It Works
72
126
 
73
- 1. You create an agent key via the CLI — it gets an email, phone number, and budget
74
- 2. `clawcard setup` installs a skill file that teaches your agent the available commands
75
- 3. Your agent runs `clawcard agent` commands to send emails, create cards, store credentials, etc.
76
- 4. The API key stays in `~/.clawcard/.env` the agent never sees it
127
+ 1. You create an agent key — it gets email, phone, and budget
128
+ 2. `clawcard setup` installs the skill that teaches your agent the commands
129
+ 3. Agent creates a wallet, discovers services, pays for APIs, signs up for things
130
+ 4. Everything is logged full audit trail in the dashboard
131
+ 5. You control the budget and can freeze/close anything instantly
132
+
133
+ ## Payment Protocols
134
+
135
+ ClawCard is the first platform to unify both major machine payment protocols:
136
+
137
+ - **x402** (Coinbase) — Pay-per-request API payments with USDC. 50M+ transactions.
138
+ - **MPP** (Stripe/Tempo) — Session-based machine payments. Cards, stablecoins, Lightning.
139
+
140
+ Your agent pays for any service in either ecosystem. You never think about protocols.
77
141
 
78
142
  ## Pricing
79
143
 
80
144
  Pay as you go. No subscriptions, no monthly fees.
81
145
 
82
146
  - **Minimum top-up** — $5
83
- - **Processing fee** — 10%
147
+ - **Processing fee** — 10% on FIAT top-ups
148
+ - **Wallet funding fee** — 2% on FIAT-to-USDC conversion
84
149
  - **Cards** — Unlimited
85
-
86
- Top up your balance and your agent spends it on virtual cards.
150
+ - **x402/MPP payments** — Pay source price (no markup)
87
151
 
88
152
  ## Links
89
153
 
90
154
  - [Website](https://clawcard.sh)
91
155
  - [Docs](https://clawcard.sh/docs)
92
156
  - [Skill](https://github.com/latchagent/skill)
157
+ - [Dashboard](https://clawcard.sh/dashboard)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawcard/cli",
3
- "version": "2.1.8",
3
+ "version": "3.0.1",
4
4
  "description": "The ClawCard CLI — manage your agent keys, billing, and setup from the terminal",
5
5
  "bin": {
6
6
  "clawcard": "./bin/clawcard.mjs"
package/skill/SKILL.md CHANGED
@@ -1,17 +1,25 @@
1
1
  ---
2
2
  name: clawcard
3
- description: Email, SMS, virtual cards, and credential vault for autonomous agents. Run clawcard agent commands to interact with your resources.
3
+ description: Email, SMS, virtual cards, crypto wallet, and credential vault for autonomous agents. Run clawcard agent commands to interact with your resources.
4
4
  ---
5
5
 
6
6
  You have access to ClawCard via the `clawcard` CLI. All commands support `--json` for machine-readable output. Always pass `--json` when calling these commands.
7
7
 
8
- ## Step 1: Check your identity
8
+ ## CRITICAL: Know Your Resources Before Acting
9
+
10
+ **BEFORE creating cards, sending USDC, or spending money, ALWAYS check what you already have:**
9
11
 
10
12
  ```
11
- clawcard agent info --json
13
+ clawcard agent info --json # Your identity, email, phone, wallet, budget
14
+ clawcard agent cards --json # Your existing cards (check BEFORE creating new ones!)
15
+ clawcard agent wallet --json # Your wallet (check BEFORE creating a new one!)
16
+ clawcard agent budget --json # Your remaining FIAT budget
17
+ clawcard agent creds --json # Your stored credentials
12
18
  ```
13
19
 
14
- Returns your agent name, email address, phone number, and remaining budget.
20
+ **You MUST list existing cards before every purchase.** If you already have an open `merchant_locked` card for the same merchant, REUSE IT — do not create a new one. Cards are limited and creating duplicates wastes budget.
21
+
22
+ ---
15
23
 
16
24
  ## Email
17
25
 
@@ -44,22 +52,38 @@ clawcard agent sms send --to "+15551234567" --body "Message" --json
44
52
 
45
53
  ## Virtual Cards
46
54
 
47
- IMPORTANT: Always list cards first. Reuse open merchant_locked cards for the same merchant.
55
+ ### MANDATORY: Check existing cards FIRST
56
+
57
+ **Before EVERY payment, run this:**
58
+ ```
59
+ clawcard agent cards --json
60
+ ```
61
+
62
+ Look at the response. For each card, check:
63
+ - `status`: Is it `open`? (If `paused` or `closed`, skip it)
64
+ - `type`: Is it `merchant_locked`? (If so, it can be reused at the same merchant)
65
+ - `memo`: Does the description match the merchant/service you're paying?
66
+ - `spendLimitCents`: Does it have enough budget remaining?
67
+
68
+ **If you find an open merchant_locked card for the same merchant — USE IT. Do NOT create a new card.**
69
+
70
+ ### Card types
48
71
 
49
- ⚠️ SUBSCRIPTION WARNING: ClawCard cards cannot be topped up after creation. If you are about to pay for a recurring subscription (monthly SaaS, hosting, etc.), STOP and tell the user:
50
- "This looks like a subscription. ClawCard cards have a fixed budget and can't be topped up — the renewal will fail when the budget runs out. Consider using a personal card for subscriptions, or create a merchant_locked card with enough budget to cover several months."
51
- Only proceed if the user explicitly confirms.
72
+ - `single_use`: Auto-closes after one charge. Use for one-time purchases (domains, invoices, one-off payments). **This is the default.**
73
+ - `merchant_locked`: Locks to first merchant, allows repeat charges. Use ONLY when the user explicitly needs multiple purchases at the same merchant.
52
74
 
53
- Card types (REQUIRED — you must specify one):
54
- - single_use: auto-closes after one charge. Use for one-time purchases (domains, invoices, one-off payments). THIS IS THE DEFAULT — use this unless the user specifically asks for a recurring card.
55
- - merchant_locked: locks to first merchant, allows repeat charges. Use ONLY when the user explicitly needs to make multiple purchases at the same merchant. NOT recommended for subscriptions since the card budget cannot be refilled.
75
+ ### Subscription warning
76
+
77
+ Cards CANNOT be topped up. If you are about to pay for a recurring subscription (monthly SaaS, hosting, etc.), STOP and warn the user: "This card has a fixed budget and can't be refilled the renewal will fail when the budget runs out. Consider using a personal card for subscriptions."
78
+
79
+ ### Commands
56
80
 
57
81
  List cards:
58
82
  ```
59
83
  clawcard agent cards --json
60
84
  ```
61
85
 
62
- Create card:
86
+ Create card (ONLY after confirming no existing card works):
63
87
  ```
64
88
  clawcard agent cards create --amount <cents> --type <single_use|merchant_locked> --memo "description" --json
65
89
  ```
@@ -74,6 +98,93 @@ Close card:
74
98
  clawcard agent cards close <card-id> --json
75
99
  ```
76
100
 
101
+ ## Stablecoin Wallet (USDC on Base)
102
+
103
+ Your agent can have a USDC wallet for crypto payments, agent-to-agent transfers, and x402 API access.
104
+
105
+ **Use wallet for:** x402 APIs (HTTP 402 responses), crypto transfers, micropayments
106
+ **Use cards for:** traditional merchant checkouts, websites with payment forms
107
+
108
+ ### Setup
109
+
110
+ Check if you have a wallet:
111
+ ```
112
+ clawcard agent wallet --json
113
+ ```
114
+
115
+ If you get `"No wallet found"`, create one via the API or run `clawcard agent wallet` interactively.
116
+
117
+ ### Commands
118
+
119
+ Check balance (shows USDC + FIAT spending power):
120
+ ```
121
+ clawcard agent wallet balance --json
122
+ ```
123
+
124
+ Send USDC to an address:
125
+ ```
126
+ clawcard agent wallet send --to 0xADDRESS --amount 5.00 --json
127
+ ```
128
+
129
+ Pay a URL via x402 (default — when a service returns HTTP 402):
130
+ ```
131
+ clawcard agent wallet send --url https://api.example.com/data --json
132
+ ```
133
+
134
+ Pay a URL via MPP (Machine Payments Protocol — Stripe/Tempo):
135
+ ```
136
+ clawcard agent wallet send --url https://api.example.com/data --protocol mpp --json
137
+ ```
138
+
139
+ View transaction history:
140
+ ```
141
+ clawcard agent wallet transactions --json
142
+ ```
143
+
144
+ Fund your wallet from your FIAT balance (2% conversion fee):
145
+ ```
146
+ clawcard agent wallet fund --amount 10.00 --json
147
+ ```
148
+
149
+ Or send USDC directly to your wallet address on Base. Check your address with `clawcard agent wallet --json`.
150
+
151
+ ## Discover Services
152
+
153
+ **When you need a capability you don't have** (web search, data enrichment, image generation, etc.), search the x402 ecosystem:
154
+
155
+ ```
156
+ clawcard agent discover --query "web search" --json
157
+ ```
158
+
159
+ This returns paid API services you can access with your wallet. To use one:
160
+
161
+ ```
162
+ clawcard agent wallet send --url https://discovered-service.com/endpoint --json
163
+ ```
164
+
165
+ The wallet handles payment automatically via the x402 protocol. No API keys or subscriptions needed — just USDC micropayments.
166
+
167
+ Browse all available services:
168
+ ```
169
+ clawcard agent discover --json
170
+ ```
171
+
172
+ ## On-Chain Identity (ERC-8004)
173
+
174
+ Your agent can have a verifiable on-chain identity — an NFT on Base that proves who the agent is, what it can do, and links to its wallet address.
175
+
176
+ Register or view identity:
177
+ ```
178
+ clawcard agent identity --json
179
+ ```
180
+
181
+ ## A2A Agent Card
182
+
183
+ View your agent's discovery card (used by other agents to find and interact with you):
184
+ ```
185
+ clawcard agent card --json
186
+ ```
187
+
77
188
  ## Credentials
78
189
 
79
190
  Store and retrieve secrets. Use consistent lowercase naming.
@@ -119,34 +230,56 @@ clawcard agent billing-address --json
119
230
  ```
120
231
 
121
232
  Returns: name, line1, line2, city, state, zip, country.
122
- Use these values to fill billing/address fields on checkout pages.
123
233
 
124
234
  ## Paying on Checkout Pages
125
235
 
126
- When you reach a payment form:
127
- 1. Get billing info: `clawcard agent billing-address --json`
128
- 2. Fill all non-payment fields yourself (name, email, address, zip) using the billing address
129
- 3. Create a card if needed:
236
+ **FOLLOW THESE STEPS IN ORDER:**
237
+
238
+ 1. **Check existing cards FIRST:**
239
+ ```
240
+ clawcard agent cards --json
241
+ ```
242
+ Look for an open card that matches this merchant. If found, skip to step 4.
243
+
244
+ 2. **Check budget:**
245
+ ```
246
+ clawcard agent budget --json
130
247
  ```
131
- clawcard agent cards create --amount <cents> --type single_use --memo "desc" --json
248
+
249
+ 3. **Create a card ONLY if no existing card works:**
132
250
  ```
133
- 3. Try filling card fields yourself first (card number, expiry, CVC)
134
- 4. If you can't access the card fields (iframe/cross-origin), run:
251
+ clawcard agent cards create --amount <cents> --type single_use --memo "description of purchase" --json
252
+ ```
253
+
254
+ 4. **Get billing address:**
255
+ ```
256
+ clawcard agent billing-address --json
257
+ ```
258
+
259
+ 5. **Fill non-payment fields** (name, email, address, zip) using the billing address.
260
+
261
+ 6. **Get card details:**
262
+ ```
263
+ clawcard agent cards details <card-id> --json
264
+ ```
265
+
266
+ 7. **Fill card fields** (card number, expiry, CVC) yourself if possible.
267
+
268
+ 8. **If card fields are in iframes you can't reach**, use the browser extension:
135
269
  ```
136
270
  clawcard agent pay --card-id <card-id> --json
137
271
  ```
138
- This sends the card details to the ClawCard browser extension which
139
- fills payment form fields that are inside iframes you can't reach.
140
- 5. If the extension is not installed, this will return an error —
141
- fall back to telling the user they need the extension installed.
142
- 6. After running pay, wait a moment then verify the fields are filled
143
- before clicking the submit/pay button.
272
+
273
+ 9. **Verify fields are filled**, then submit the payment.
144
274
 
145
275
  ## Tips
146
276
 
147
- - Always run `clawcard agent info --json` first to verify your identity.
148
- - Check budget before creating cards: `clawcard agent budget --json`
149
- - Default to single_use cards for everything. They auto-close after one charge.
150
- - Only use merchant_locked if the user explicitly needs repeat charges at the same merchant.
151
- - NEVER silently pay for a subscription. Always warn the user that card budgets are fixed and renewals will fail.
152
- - Store credentials in the vault with consistent naming so you can find them later.
277
+ - **ALWAYS check existing resources before creating new ones** cards, wallet, credentials.
278
+ - Check budget before spending: `clawcard agent budget --json`
279
+ - For API payments that return HTTP 402, use the wallet: `clawcard agent wallet send --url <url> --json`
280
+ - For traditional checkouts, use cards. For crypto/x402, use the wallet.
281
+ - Default to `single_use` cards. Only use `merchant_locked` for repeat purchases at the same merchant.
282
+ - NEVER silently pay for a subscription. Always warn the user first.
283
+ - Store credentials with consistent naming so you can find them later.
284
+ - If you need a capability (search, enrichment, scraping, etc.), discover it: `clawcard agent discover --query "..." --json`
285
+ - If a service returns HTTP 402, your wallet can pay for it automatically.
package/src/agent-api.js CHANGED
@@ -113,3 +113,61 @@ export const listActivity = (agentId, params = {}) => {
113
113
  const query = qs.toString();
114
114
  return agentRequest(`/api/agents/${agentId}/activity${query ? "?" + query : ""}`);
115
115
  };
116
+
117
+ // Wallet
118
+ export const getWallet = (agentId) =>
119
+ agentRequest(`/api/agents/${agentId}/wallet`);
120
+
121
+ export const createWallet = (agentId) =>
122
+ agentRequest(`/api/agents/${agentId}/wallet`, {
123
+ method: "POST",
124
+ body: JSON.stringify({}),
125
+ });
126
+
127
+ export const walletAction = (agentId, action) =>
128
+ agentRequest(`/api/agents/${agentId}/wallet`, {
129
+ method: "PATCH",
130
+ body: JSON.stringify({ action }),
131
+ });
132
+
133
+ export const walletFund = (agentId, amount) =>
134
+ agentRequest(`/api/agents/${agentId}/wallet/fund`, {
135
+ method: "POST",
136
+ body: JSON.stringify({ amount }),
137
+ });
138
+
139
+ export const walletSend = (agentId, data) =>
140
+ agentRequest(`/api/agents/${agentId}/wallet/send`, {
141
+ method: "POST",
142
+ body: JSON.stringify(data),
143
+ });
144
+
145
+ export const walletTransactions = (agentId, params = {}) => {
146
+ const qs = new URLSearchParams();
147
+ if (params.limit) qs.set("limit", params.limit);
148
+ const query = qs.toString();
149
+ return agentRequest(`/api/agents/${agentId}/wallet/transactions${query ? "?" + query : ""}`);
150
+ };
151
+
152
+ // Discovery
153
+ export const discoverServices = (params = {}) => {
154
+ const qs = new URLSearchParams();
155
+ if (params.query) qs.set("q", params.query);
156
+ if (params.limit) qs.set("limit", params.limit);
157
+ const query = qs.toString();
158
+ return agentRequest(`/api/discover${query ? "?" + query : ""}`);
159
+ };
160
+
161
+ // On-Chain Identity (ERC-8004)
162
+ export const getOnChainIdentity = (agentId) =>
163
+ agentRequest(`/api/agents/${agentId}/identity`);
164
+
165
+ export const registerOnChainIdentity = (agentId, data = {}) =>
166
+ agentRequest(`/api/agents/${agentId}/identity`, {
167
+ method: "POST",
168
+ body: JSON.stringify(data),
169
+ });
170
+
171
+ // A2A Agent Card
172
+ export const getAgentCard = (agentId) =>
173
+ agentRequest(`/api/agents/${agentId}/agent-card`);
@@ -15,6 +15,16 @@ import {
15
15
  getCredential,
16
16
  getBudget,
17
17
  listActivity,
18
+ getWallet,
19
+ createWallet as createWalletApi,
20
+ walletAction,
21
+ walletFund,
22
+ walletSend,
23
+ walletTransactions as walletTransactionsApi,
24
+ discoverServices,
25
+ getOnChainIdentity,
26
+ registerOnChainIdentity,
27
+ getAgentCard,
18
28
  } from "../agent-api.js";
19
29
 
20
30
  const orange = chalk.hex("#FF6B35");
@@ -38,10 +48,24 @@ export async function agentInfoCmd(options) {
38
48
  if (options.json) return output(me, true);
39
49
 
40
50
  const budget = await getBudget(me.keyId);
51
+
52
+ let wallet = null;
53
+ try {
54
+ wallet = await getWallet(me.keyId);
55
+ } catch {
56
+ // No wallet — that's fine
57
+ }
58
+
41
59
  console.log();
42
60
  console.log(` Name: ${orange.bold(me.name || "unnamed")}`);
43
61
  console.log(` Email: ${me.email || "-"}`);
44
62
  console.log(` Phone: ${me.phone || "-"}`);
63
+ if (wallet) {
64
+ console.log(` Wallet: ${orange(wallet.address)} ${chalk.dim("(Base)")}`);
65
+ console.log(` USDC: ${chalk.green(wallet.balanceUsdc + " USDC")}`);
66
+ } else {
67
+ console.log(` Wallet: ${chalk.dim("none — run: clawcard agent wallet")}`);
68
+ }
45
69
  console.log(` Key ID: ${chalk.dim(me.keyId)}`);
46
70
  console.log(` Budget: ${chalk.green("$" + ((budget.budgetCents || 0) / 100).toFixed(2))}`);
47
71
  console.log();
@@ -376,3 +400,362 @@ export async function agentPayCmd(options) {
376
400
  console.log(` Error: ${result.error}`);
377
401
  }
378
402
  }
403
+
404
+ // ── Wallet ──
405
+
406
+ export async function agentWalletCmd(options) {
407
+ const agentId = await getAgentId();
408
+
409
+ // Try to get existing wallet
410
+ try {
411
+ const wallet = await getWallet(agentId);
412
+ if (options.json) return output(wallet, true);
413
+
414
+ console.log();
415
+ console.log(` Address: ${orange(wallet.address)}`);
416
+ console.log(` Network: Base`);
417
+ console.log(` Balance: ${chalk.green(wallet.balanceUsdc + " USDC")}`);
418
+ console.log(` Status: ${wallet.status}`);
419
+ console.log();
420
+ return;
421
+ } catch (err) {
422
+ // No wallet — prompt to create
423
+ if (!err.message.includes("No wallet found") && !err.message.includes("404")) {
424
+ if (options.json) return output({ error: err.message }, true);
425
+ console.log(` Error: ${err.message}`);
426
+ return;
427
+ }
428
+ }
429
+
430
+ if (options.json) {
431
+ return output({ error: "No wallet found" }, true);
432
+ }
433
+
434
+ // Interactive prompt to create
435
+ const prompts = await import("@clack/prompts");
436
+ const shouldCreate = await prompts.confirm({
437
+ message: "No wallet detected. Create a USDC wallet on Base?",
438
+ });
439
+
440
+ if (prompts.isCancel(shouldCreate) || !shouldCreate) {
441
+ console.log(" Wallet creation cancelled.");
442
+ return;
443
+ }
444
+
445
+ try {
446
+ const wallet = await createWalletApi(agentId);
447
+ console.log();
448
+ console.log(` ${chalk.green("Wallet created!")}`);
449
+ console.log();
450
+ console.log(` Address: ${orange(wallet.address)}`);
451
+ console.log(` Network: Base`);
452
+ console.log(` Balance: ${wallet.balanceUsdc} USDC`);
453
+ console.log(` Status: ${wallet.status}`);
454
+ console.log();
455
+ console.log(chalk.dim(" Fund via FIAT balance or send USDC directly to the address above."));
456
+ console.log();
457
+ } catch (err) {
458
+ console.log(` Error creating wallet: ${err.message}`);
459
+ }
460
+ }
461
+
462
+ export async function agentWalletBalanceCmd(options) {
463
+ const agentId = await getAgentId();
464
+
465
+ try {
466
+ const wallet = await getWallet(agentId);
467
+ const budget = await getBudget(agentId);
468
+ const fiatBudget = (budget.budgetCents || 0) / 100;
469
+ const usdcBalance = parseFloat(wallet.balanceUsdc);
470
+ const effective = usdcBalance + fiatBudget;
471
+
472
+ if (options.json) return output({ ...wallet, fiatBudgetCents: budget.budgetCents, effectiveUsdc: effective.toFixed(2) }, true);
473
+
474
+ console.log();
475
+ console.log(` USDC Balance: ${chalk.green(wallet.balanceUsdc + " USDC")}`);
476
+ console.log(` FIAT Budget: ${chalk.green("$" + fiatBudget.toFixed(2))} ${chalk.dim("(available for auto-conversion)")}`);
477
+ console.log(` Effective: ${chalk.green("~$" + effective.toFixed(2))} ${chalk.dim("total spending power")}`);
478
+ console.log();
479
+ } catch (err) {
480
+ if (options.json) return output({ error: err.message }, true);
481
+ console.log(` Error: ${err.message}`);
482
+ }
483
+ }
484
+
485
+ export async function agentWalletSendCmd(options) {
486
+ // Validate input before making any API calls
487
+ if (options.to && options.url) {
488
+ const err = "Cannot use both --to and --url. Use --to for direct sends, --url for x402.";
489
+ if (options.json) return output({ success: false, error: err }, true);
490
+ console.log(` Error: ${err}`);
491
+ return;
492
+ }
493
+ if (options.to && !options.amount) {
494
+ const err = "--to requires --amount";
495
+ if (options.json) return output({ success: false, error: err }, true);
496
+ console.log(` Error: ${err}`);
497
+ return;
498
+ }
499
+ if (options.amount && !options.to) {
500
+ const err = "--amount requires --to";
501
+ if (options.json) return output({ success: false, error: err }, true);
502
+ console.log(` Error: ${err}`);
503
+ return;
504
+ }
505
+ if (!options.to && !options.url) {
506
+ const err = "Provide --to <address> --amount <usdc> or --url <x402-url>";
507
+ if (options.json) return output({ success: false, error: err }, true);
508
+ console.log(` Error: ${err}`);
509
+ return;
510
+ }
511
+
512
+ const agentId = await getAgentId();
513
+
514
+ const data = {};
515
+ if (options.to) {
516
+ data.to = options.to;
517
+ data.amountUsdc = options.amount;
518
+ } else if (options.url) {
519
+ data.url = options.url;
520
+ if (options.protocol) data.protocol = options.protocol;
521
+ }
522
+
523
+ if (options.memo) data.memo = options.memo;
524
+
525
+ try {
526
+ const result = await walletSend(agentId, data);
527
+ if (options.json) return output(result, true);
528
+
529
+ if (result.success) {
530
+ console.log();
531
+ console.log(` ${chalk.green("Sent!")} ${result.amountUsdc} USDC → ${result.to}`);
532
+ console.log(` TX: ${chalk.dim(result.txHash)}`);
533
+ console.log(` Protocol: ${result.protocol}`);
534
+ if (result.fiatConverted) {
535
+ console.log(` Converted: ${result.fiatConverted} from FIAT budget`);
536
+ }
537
+ console.log();
538
+ } else {
539
+ console.log(` Error: ${result.error}`);
540
+ }
541
+ } catch (err) {
542
+ if (options.json) return output({ error: err.message }, true);
543
+ console.log(` Error: ${err.message}`);
544
+ }
545
+ }
546
+
547
+ export async function agentWalletTransactionsCmd(options) {
548
+ const agentId = await getAgentId();
549
+
550
+ try {
551
+ const result = await walletTransactionsApi(agentId, { limit: options.limit });
552
+ if (options.json) return output(result, true);
553
+
554
+ const txns = result.transactions || [];
555
+ if (!txns.length) {
556
+ console.log(" No wallet transactions");
557
+ return;
558
+ }
559
+
560
+ console.log();
561
+ for (const t of txns) {
562
+ const dir = t.type === "receive" ? chalk.green("←") : t.type === "send" ? chalk.blue("→") : chalk.yellow("⟳");
563
+ const date = new Date(t.createdAt).toLocaleString();
564
+ console.log(` ${dir} ${t.amountUsdc} USDC ${chalk.dim(t.counterparty || "")} ${chalk.dim(t.protocol)} ${chalk.dim(date)}`);
565
+ }
566
+ console.log();
567
+ } catch (err) {
568
+ if (options.json) return output({ error: err.message }, true);
569
+ console.log(` Error: ${err.message}`);
570
+ }
571
+ }
572
+
573
+ export async function agentWalletFundCmd(options) {
574
+ if (!options.amount) {
575
+ const err = "--amount is required (in dollars, e.g., 10.00)";
576
+ if (options.json) return output({ success: false, error: err }, true);
577
+ console.log(` Error: ${err}`);
578
+ return;
579
+ }
580
+
581
+ const agentId = await getAgentId();
582
+ try {
583
+ const result = await walletFund(agentId, options.amount);
584
+ if (options.json) return output(result, true);
585
+
586
+ if (result.success) {
587
+ console.log();
588
+ console.log(` ${chalk.green("Funded!")} ${result.funded} USDC added to wallet`);
589
+ console.log(` Cost: $${(result.costCents / 100).toFixed(2)} (includes $${(result.feeCents / 100).toFixed(2)} fee)`);
590
+ console.log(` TX: ${chalk.dim(result.txHash)}`);
591
+ console.log(` Budget: $${(result.newFiatBudgetCents / 100).toFixed(2)} remaining`);
592
+ console.log();
593
+ } else {
594
+ console.log(` Error: ${result.error}`);
595
+ }
596
+ } catch (err) {
597
+ if (options.json) return output({ success: false, error: err.message }, true);
598
+ console.log(` Error: ${err.message}`);
599
+ }
600
+ }
601
+
602
+ export async function agentWalletActionCmd(action, options) {
603
+ const agentId = await getAgentId();
604
+ try {
605
+ const result = await walletAction(agentId, action);
606
+ if (options.json) return output(result, true);
607
+ console.log(` Wallet ${action}: ${result.status}`);
608
+ } catch (err) {
609
+ if (options.json) return output({ error: err.message }, true);
610
+ console.log(` Error: ${err.message}`);
611
+ }
612
+ }
613
+
614
+ // ── On-Chain Identity (ERC-8004) ──
615
+
616
+ export async function agentIdentityCmd(options) {
617
+ const agentId = await getAgentId();
618
+
619
+ // Try to get existing on-chain identity
620
+ try {
621
+ const identity = await getOnChainIdentity(agentId);
622
+ if (options.json) return output(identity, true);
623
+
624
+ console.log();
625
+ console.log(` ${orange.bold("On-Chain Identity (ERC-8004)")}`);
626
+ console.log();
627
+ if (identity.metadata) {
628
+ console.log(` Name: ${identity.metadata.name}`);
629
+ console.log(` Address: ${orange(identity.walletAddress)}`);
630
+ console.log(` Network: ${identity.network}`);
631
+ console.log(` Token ID: ${chalk.dim(identity.tokenId)}`);
632
+ if (identity.metadata.properties) {
633
+ const caps = identity.metadata.properties.capabilities || [];
634
+ console.log(` Capabilities: ${caps.join(", ") || "none"}`);
635
+ }
636
+ } else {
637
+ console.log(` Wallet: ${orange(identity.walletAddress)}`);
638
+ console.log(` Token ID: ${identity.tokenId}`);
639
+ console.log(` URI: ${chalk.dim(identity.metadataURI)}`);
640
+ }
641
+ console.log();
642
+ return;
643
+ } catch (err) {
644
+ // No identity found — offer to register
645
+ if (!err.message.includes("No on-chain identity") && !err.message.includes("No wallet") && !err.message.includes("404")) {
646
+ if (options.json) return output({ error: err.message }, true);
647
+ console.log(` Error: ${err.message}`);
648
+ return;
649
+ }
650
+ }
651
+
652
+ // No identity found
653
+ if (options.json) {
654
+ // In JSON mode, try to register automatically
655
+ try {
656
+ const result = await registerOnChainIdentity(agentId, {});
657
+ return output(result, true);
658
+ } catch (err) {
659
+ return output({ error: err.message }, true);
660
+ }
661
+ }
662
+
663
+ // Interactive prompt to register
664
+ const prompts = await import("@clack/prompts");
665
+ const shouldRegister = await prompts.confirm({
666
+ message: "No on-chain identity found. Register one now?",
667
+ });
668
+
669
+ if (prompts.isCancel(shouldRegister) || !shouldRegister) {
670
+ console.log(" Registration cancelled.");
671
+ return;
672
+ }
673
+
674
+ try {
675
+ const result = await registerOnChainIdentity(agentId, {});
676
+ console.log();
677
+ console.log(` ${chalk.green("Identity prepared!")}`);
678
+ console.log();
679
+ console.log(` Wallet: ${orange(result.walletAddress)}`);
680
+ console.log(` Network: ${result.network}`);
681
+ console.log(` Standard: ERC-8004`);
682
+ if (result.metadata?.properties?.capabilities) {
683
+ console.log(` Capabilities: ${result.metadata.properties.capabilities.join(", ")}`);
684
+ }
685
+ console.log();
686
+ if (result.note) {
687
+ console.log(chalk.dim(` ${result.note}`));
688
+ console.log();
689
+ }
690
+ } catch (err) {
691
+ console.log(` Error registering identity: ${err.message}`);
692
+ }
693
+ }
694
+
695
+ // ── A2A Agent Card ──
696
+
697
+ export async function agentCardCmd(options) {
698
+ const agentId = await getAgentId();
699
+
700
+ try {
701
+ const card = await getAgentCard(agentId);
702
+ if (options.json) return output(card, true);
703
+
704
+ console.log();
705
+ console.log(` ${orange.bold("A2A Agent Card")}`);
706
+ console.log();
707
+ console.log(` Name: ${card.name}`);
708
+ console.log(` Description: ${card.description}`);
709
+ console.log(` URL: ${chalk.dim(card.url)}`);
710
+ console.log(` Version: ${card.version}`);
711
+ console.log(` Provider: ${card.provider.organization}`);
712
+ console.log();
713
+ console.log(` ${orange("Skills:")}`);
714
+ for (const skill of card.skills || []) {
715
+ console.log(` - ${chalk.bold(skill.name)} ${chalk.dim(`[${skill.tags.join(", ")}]`)}`);
716
+ console.log(` ${skill.description}`);
717
+ }
718
+ console.log();
719
+ console.log(` Auth: ${(card.authentication?.schemes || []).join(", ")}`);
720
+ console.log(` Input: ${(card.defaultInputModes || []).join(", ")}`);
721
+ console.log(` Output: ${(card.defaultOutputModes || []).join(", ")}`);
722
+ console.log();
723
+ } catch (err) {
724
+ if (options.json) return output({ error: err.message }, true);
725
+ console.log(` Error: ${err.message}`);
726
+ }
727
+ }
728
+
729
+ // ── Discovery ──
730
+
731
+ export async function agentDiscoverCmd(options) {
732
+ try {
733
+ const result = await discoverServices({
734
+ query: options.query,
735
+ limit: options.limit,
736
+ });
737
+ if (options.json) return output(result, true);
738
+
739
+ const services = result.services || [];
740
+ if (!services.length) {
741
+ console.log(` No services found${options.query ? ` for "${options.query}"` : ""}`);
742
+ console.log(chalk.dim(" The x402 ecosystem is growing — try a broader search or check back later."));
743
+ return;
744
+ }
745
+
746
+ console.log();
747
+ console.log(` ${chalk.green(services.length)} services found${options.query ? ` for "${options.query}"` : ""}`);
748
+ console.log();
749
+ for (const s of services) {
750
+ console.log(` ${orange(s.url)}`);
751
+ console.log(` ${s.description}`);
752
+ console.log(` Price: ${chalk.green(s.priceUsdc + " USDC")} Network: ${chalk.dim(s.network)}`);
753
+ console.log();
754
+ }
755
+ console.log(chalk.dim(" Pay for any service: clawcard agent wallet send --url <url> --json"));
756
+ console.log();
757
+ } catch (err) {
758
+ if (options.json) return output({ error: err.message }, true);
759
+ console.log(` Error: ${err.message}`);
760
+ }
761
+ }
package/src/index.js CHANGED
@@ -208,6 +208,78 @@ agentCards
208
208
  await agentCardsActionCmd(cardId, "resume", options);
209
209
  });
210
210
 
211
+ // Agent wallet
212
+ const agentWallet = agent.command("wallet").description("Stablecoin wallet (USDC on Base)");
213
+ agentWallet
214
+ .option("--json", "Output as JSON")
215
+ .action(async (options) => {
216
+ const { agentWalletCmd } = await import("./commands/agent.js");
217
+ await agentWalletCmd(options);
218
+ });
219
+ agentWallet
220
+ .command("balance")
221
+ .description("Check wallet balance + effective spending power")
222
+ .option("--json", "Output as JSON")
223
+ .action(async (options) => {
224
+ const { agentWalletBalanceCmd } = await import("./commands/agent.js");
225
+ await agentWalletBalanceCmd(options);
226
+ });
227
+ agentWallet
228
+ .command("send")
229
+ .description("Send USDC to an address or pay an x402/MPP URL")
230
+ .option("--to <address>", "Recipient 0x address")
231
+ .option("--amount <usdc>", "Amount in USDC (e.g., 5.00)")
232
+ .option("--url <url>", "URL to pay for access (x402 or MPP)")
233
+ .option("--protocol <protocol>", "Payment protocol: x402 (default) or mpp")
234
+ .option("--memo <memo>", "Optional memo")
235
+ .option("--json", "Output as JSON")
236
+ .action(async (options) => {
237
+ const { agentWalletSendCmd } = await import("./commands/agent.js");
238
+ await agentWalletSendCmd(options);
239
+ });
240
+ agentWallet
241
+ .command("transactions")
242
+ .description("View wallet transaction history")
243
+ .option("--limit <n>", "Max results", "50")
244
+ .option("--json", "Output as JSON")
245
+ .action(async (options) => {
246
+ const { agentWalletTransactionsCmd } = await import("./commands/agent.js");
247
+ await agentWalletTransactionsCmd(options);
248
+ });
249
+ agentWallet
250
+ .command("fund")
251
+ .description("Fund wallet with USDC from FIAT balance")
252
+ .requiredOption("--amount <dollars>", "Amount in dollars to convert (e.g., 10.00)")
253
+ .option("--json", "Output as JSON")
254
+ .action(async (options) => {
255
+ const { agentWalletFundCmd } = await import("./commands/agent.js");
256
+ await agentWalletFundCmd(options);
257
+ });
258
+ agentWallet
259
+ .command("freeze")
260
+ .description("Freeze wallet (block all sends)")
261
+ .option("--json", "Output as JSON")
262
+ .action(async (options) => {
263
+ const { agentWalletActionCmd } = await import("./commands/agent.js");
264
+ await agentWalletActionCmd("freeze", options);
265
+ });
266
+ agentWallet
267
+ .command("unfreeze")
268
+ .description("Unfreeze wallet")
269
+ .option("--json", "Output as JSON")
270
+ .action(async (options) => {
271
+ const { agentWalletActionCmd } = await import("./commands/agent.js");
272
+ await agentWalletActionCmd("unfreeze", options);
273
+ });
274
+ agentWallet
275
+ .command("close")
276
+ .description("Permanently close wallet")
277
+ .option("--json", "Output as JSON")
278
+ .action(async (options) => {
279
+ const { agentWalletActionCmd } = await import("./commands/agent.js");
280
+ await agentWalletActionCmd("close", options);
281
+ });
282
+
211
283
  // Agent credentials
212
284
  const agentCreds = agent.command("creds").description("Credential vault");
213
285
  agentCreds
@@ -278,6 +350,38 @@ agent
278
350
  await agentBillingAddressCmd(options);
279
351
  });
280
352
 
353
+ // Agent on-chain identity (ERC-8004)
354
+ agent
355
+ .command("identity")
356
+ .description("Register or view ERC-8004 on-chain identity")
357
+ .option("--json", "Output as JSON")
358
+ .action(async (options) => {
359
+ const { agentIdentityCmd } = await import("./commands/agent.js");
360
+ await agentIdentityCmd(options);
361
+ });
362
+
363
+ // Agent A2A card
364
+ agent
365
+ .command("card")
366
+ .description("View A2A agent card (discovery document)")
367
+ .option("--json", "Output as JSON")
368
+ .action(async (options) => {
369
+ const { agentCardCmd } = await import("./commands/agent.js");
370
+ await agentCardCmd(options);
371
+ });
372
+
373
+ // Agent discover (x402 service discovery)
374
+ agent
375
+ .command("discover")
376
+ .description("Discover paid services in the x402 ecosystem")
377
+ .option("--query <query>", "Search term (e.g., 'web search', 'enrichment')")
378
+ .option("--limit <n>", "Max results", "20")
379
+ .option("--json", "Output as JSON")
380
+ .action(async (options) => {
381
+ const { agentDiscoverCmd } = await import("./commands/agent.js");
382
+ await agentDiscoverCmd(options);
383
+ });
384
+
281
385
  // Settings
282
386
  program
283
387
  .command("settings")