@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 +1 -1
- package/skill/SKILL.md +165 -32
- package/src/agent-api.js +58 -0
- package/src/commands/agent.js +383 -0
- package/src/commands/signup.js +29 -16
- package/src/index.js +104 -0
package/package.json
CHANGED
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
248
|
+
|
|
249
|
+
3. **Create a card ONLY if no existing card works:**
|
|
132
250
|
```
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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
|
-
-
|
|
148
|
-
- Check budget before
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
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`);
|
package/src/commands/agent.js
CHANGED
|
@@ -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/commands/signup.js
CHANGED
|
@@ -72,28 +72,41 @@ export async function signupCommand() {
|
|
|
72
72
|
message: "Payment complete?",
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
if (p.isCancel(ready)
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
});
|
|
81
|
+
if (!p.isCancel(createKey) && createKey) {
|
|
82
|
+
const { keysCreateCommand } = await import("./keys.js");
|
|
83
|
+
await keysCreateCommand();
|
|
81
84
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
// Step 3: Setup
|
|
86
|
+
const runSetup = await p.confirm({
|
|
87
|
+
message: "Set up your agent now?",
|
|
88
|
+
});
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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")
|