@clawcard/cli 2.1.7 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawcard/cli",
3
- "version": "2.1.7",
3
+ "version": "3.0.0",
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
+ }
@@ -72,28 +72,41 @@ export async function signupCommand() {
72
72
  message: "Payment complete?",
73
73
  });
74
74
 
75
- if (p.isCancel(ready) || !ready) return;
75
+ if (!p.isCancel(ready) && ready) {
76
+ // Step 2: Create key
77
+ const createKey = await p.confirm({
78
+ message: "Create your agent key now?",
79
+ });
76
80
 
77
- // Step 2: Create key
78
- const createKey = await p.confirm({
79
- message: "Create your agent key now?",
80
- });
81
+ if (!p.isCancel(createKey) && createKey) {
82
+ const { keysCreateCommand } = await import("./keys.js");
83
+ await keysCreateCommand();
81
84
 
82
- if (!p.isCancel(createKey) && createKey) {
83
- const { keysCreateCommand } = await import("./keys.js");
84
- await keysCreateCommand();
85
+ // Step 3: Setup
86
+ const runSetup = await p.confirm({
87
+ message: "Set up your agent now?",
88
+ });
85
89
 
86
- // Step 3: Setup
87
- const runSetup = await p.confirm({
88
- message: "Set up your agent now?",
89
- });
90
-
91
- if (!p.isCancel(runSetup) && runSetup) {
92
- const { setupCommand } = await import("./setup.js");
93
- await setupCommand();
90
+ if (!p.isCancel(runSetup) && runSetup) {
91
+ const { setupCommand } = await import("./setup.js");
92
+ await setupCommand();
93
+ }
94
94
  }
95
95
  }
96
96
  }
97
+
98
+ // Always show next steps so the user knows where to go
99
+ p.note(
100
+ [
101
+ `${orange.bold("claw topup")} ${chalk.dim("Top up your balance")}`,
102
+ `${orange.bold("claw keys create")} ${chalk.dim("Create an agent key")}`,
103
+ `${orange.bold("claw setup")} ${chalk.dim("Set up your agent")}`,
104
+ `${orange.bold("claw dashboard")} ${chalk.dim("Open the portal")}`,
105
+ "",
106
+ chalk.dim(`Or visit: ${BASE_URL}/dashboard`),
107
+ ].join("\n"),
108
+ "You can pick up where you left off anytime"
109
+ );
97
110
  } catch (err) {
98
111
  s.stop("Signup failed");
99
112
  p.log.error(err.message);
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")