@web42/w42 0.1.16 → 0.1.18
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 +103 -0
- package/dist/commands/pay.d.ts +2 -0
- package/dist/commands/pay.js +255 -0
- package/dist/commands/send.js +32 -1
- package/dist/index.js +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,7 @@ web42 send http://localhost:3001 "Hello"
|
|
|
47
47
|
| `--new` | Start a new conversation (clears saved context) |
|
|
48
48
|
| `--context <id>` | Use a specific context ID |
|
|
49
49
|
| `--task-id <id>` | Reply to a specific task (e.g. one in `input-required` state) |
|
|
50
|
+
| `--pay <token>` | Attach AP2 payment token as PaymentMandate data part |
|
|
50
51
|
|
|
51
52
|
---
|
|
52
53
|
|
|
@@ -92,6 +93,108 @@ web42 register https://my-agent.example.com --visibility private --tags "nlp,sum
|
|
|
92
93
|
|
|
93
94
|
---
|
|
94
95
|
|
|
96
|
+
### `web42 pay`
|
|
97
|
+
|
|
98
|
+
AP2 payment commands — wallet management, payment intents, checkout, and human signing.
|
|
99
|
+
|
|
100
|
+
#### `web42 pay wallet`
|
|
101
|
+
|
|
102
|
+
View your wallet balance.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
web42 pay wallet
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### `web42 pay wallet topup <amount>`
|
|
109
|
+
|
|
110
|
+
Add funds to your wallet. Amount is in dollars.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
web42 pay wallet topup 50.00
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### `web42 pay intent propose`
|
|
117
|
+
|
|
118
|
+
Generate an intent creation URL for the user to authorize in the browser.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
web42 pay intent propose --nick starbucks-daily --agents @x~starbucks --max-amount 5.00 --prompt-playback "Spend up to $5/day at Starbucks" --recurring daily
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
| Option | Description |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `--nick <nick>` | Short identifier for the intent (required) |
|
|
127
|
+
| `--agents <slugs>` | Comma-separated merchant agent slugs (required) |
|
|
128
|
+
| `--max-amount <dollars>` | Max amount per transaction (required) |
|
|
129
|
+
| `--prompt-playback <text>` | Human-readable description (required) |
|
|
130
|
+
| `--currency <code>` | Currency code (default: `USD`) |
|
|
131
|
+
| `--recurring <type>` | `once`, `daily`, `weekly`, or `monthly` (default: `once`) |
|
|
132
|
+
| `--budget <dollars>` | Lifetime budget |
|
|
133
|
+
| `--expires <date>` | Expiry date (ISO 8601) |
|
|
134
|
+
|
|
135
|
+
#### `web42 pay intent get --nick <nick>`
|
|
136
|
+
|
|
137
|
+
Fetch an intent by nick. Caches locally if active.
|
|
138
|
+
|
|
139
|
+
#### `web42 pay intent list`
|
|
140
|
+
|
|
141
|
+
List all your payment intents.
|
|
142
|
+
|
|
143
|
+
#### `web42 pay intent revoke --nick <nick>`
|
|
144
|
+
|
|
145
|
+
Revoke an active intent.
|
|
146
|
+
|
|
147
|
+
#### `web42 pay checkout`
|
|
148
|
+
|
|
149
|
+
Execute a payment against a matching intent (no human approval needed).
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
web42 pay checkout --cart '<json>' --agent @x~starbucks --intent starbucks-daily
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
| Option | Description |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `--cart <json>` | CartMandate JSON (required) |
|
|
158
|
+
| `--agent <slug>` | Merchant agent slug (required) |
|
|
159
|
+
| `--intent <nick>` | Intent nick to use (required) |
|
|
160
|
+
|
|
161
|
+
#### `web42 pay sign create`
|
|
162
|
+
|
|
163
|
+
Create a payment session for human approval (when no matching intent exists).
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
web42 pay sign create --cart '<json>' --agent @x~bookstore
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
| Option | Description |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `--cart <json>` | CartMandate JSON (required) |
|
|
172
|
+
| `--agent <slug>` | Merchant agent slug (required) |
|
|
173
|
+
|
|
174
|
+
Returns `{ code, signing_url }`. Present the URL to the user.
|
|
175
|
+
|
|
176
|
+
#### `web42 pay sign get <code>`
|
|
177
|
+
|
|
178
|
+
Check the status of a payment session.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
web42 pay sign get a1b2c3d4e5f6g7h8
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### `web42 telemetry`
|
|
187
|
+
|
|
188
|
+
Control usage telemetry.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
web42 telemetry # Show current state
|
|
192
|
+
web42 telemetry on # Enable
|
|
193
|
+
web42 telemetry off # Disable
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
95
198
|
## Environment variables
|
|
96
199
|
|
|
97
200
|
```bash
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { apiGet, apiPost } from "../utils/api.js";
|
|
5
|
+
import { requireAuth, setConfigValue, } from "../utils/config.js";
|
|
6
|
+
// ─── Wallet ───────────────────────────────────────────────
|
|
7
|
+
const walletCommand = new Command("wallet")
|
|
8
|
+
.description("View or top up your wallet balance")
|
|
9
|
+
.action(async () => {
|
|
10
|
+
requireAuth();
|
|
11
|
+
const spinner = ora("Fetching wallet...").start();
|
|
12
|
+
try {
|
|
13
|
+
const res = await apiGet("/api/pay/wallet");
|
|
14
|
+
spinner.stop();
|
|
15
|
+
console.log(JSON.stringify(res, null, 2));
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
spinner.fail("Failed to fetch wallet");
|
|
19
|
+
console.error(chalk.red(String(err)));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
walletCommand
|
|
24
|
+
.command("topup")
|
|
25
|
+
.description("Add funds to your wallet")
|
|
26
|
+
.argument("<amount>", "Amount in dollars (e.g. 50.00)")
|
|
27
|
+
.action(async (amountStr) => {
|
|
28
|
+
requireAuth();
|
|
29
|
+
const amountCents = Math.round(parseFloat(amountStr) * 100);
|
|
30
|
+
if (isNaN(amountCents) || amountCents <= 0) {
|
|
31
|
+
console.error(chalk.red("Amount must be a positive number"));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const spinner = ora("Topping up wallet...").start();
|
|
35
|
+
try {
|
|
36
|
+
const res = await apiPost("/api/pay/wallet/topup", { amount_cents: amountCents });
|
|
37
|
+
spinner.stop();
|
|
38
|
+
console.log(JSON.stringify(res, null, 2));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
spinner.fail("Failed to top up wallet");
|
|
42
|
+
console.error(chalk.red(String(err)));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
// ─── Intent ───────────────────────────────────────────────
|
|
47
|
+
const intentCommand = new Command("intent").description("Manage payment intents");
|
|
48
|
+
intentCommand
|
|
49
|
+
.command("get")
|
|
50
|
+
.description("Fetch an intent by nick")
|
|
51
|
+
.requiredOption("--nick <nick>", "Intent nick")
|
|
52
|
+
.action(async (opts) => {
|
|
53
|
+
requireAuth();
|
|
54
|
+
const spinner = ora(`Fetching intent ${opts.nick}...`).start();
|
|
55
|
+
try {
|
|
56
|
+
const res = await apiGet(`/api/pay/intent/${encodeURIComponent(opts.nick)}`);
|
|
57
|
+
spinner.stop();
|
|
58
|
+
// Cache if active
|
|
59
|
+
if (res.status === "active") {
|
|
60
|
+
setConfigValue(`intents.${opts.nick}`, JSON.stringify(res));
|
|
61
|
+
}
|
|
62
|
+
console.log(JSON.stringify(res, null, 2));
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
spinner.fail("Failed to fetch intent");
|
|
66
|
+
console.error(chalk.red(String(err)));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
intentCommand
|
|
71
|
+
.command("list")
|
|
72
|
+
.description("List all your intents")
|
|
73
|
+
.action(async () => {
|
|
74
|
+
requireAuth();
|
|
75
|
+
const spinner = ora("Fetching intents...").start();
|
|
76
|
+
try {
|
|
77
|
+
const intents = await apiGet("/api/pay/intent");
|
|
78
|
+
spinner.stop();
|
|
79
|
+
// Sync active intents to cache, remove stale ones
|
|
80
|
+
const activeNicks = new Set();
|
|
81
|
+
for (const intent of intents) {
|
|
82
|
+
if (intent.status === "active" && typeof intent.nick === "string") {
|
|
83
|
+
activeNicks.add(intent.nick);
|
|
84
|
+
setConfigValue(`intents.${intent.nick}`, JSON.stringify(intent));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
console.log(JSON.stringify(intents, null, 2));
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
spinner.fail("Failed to list intents");
|
|
91
|
+
console.error(chalk.red(String(err)));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
intentCommand
|
|
96
|
+
.command("revoke")
|
|
97
|
+
.description("Revoke an active intent")
|
|
98
|
+
.requiredOption("--nick <nick>", "Intent nick")
|
|
99
|
+
.action(async (opts) => {
|
|
100
|
+
requireAuth();
|
|
101
|
+
const spinner = ora(`Revoking intent ${opts.nick}...`).start();
|
|
102
|
+
try {
|
|
103
|
+
const res = await apiPost(`/api/pay/intent/${encodeURIComponent(opts.nick)}/revoke`, {});
|
|
104
|
+
spinner.stop();
|
|
105
|
+
// Remove from cache
|
|
106
|
+
setConfigValue(`intents.${opts.nick}`, "");
|
|
107
|
+
console.log(JSON.stringify(res, null, 2));
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
spinner.fail("Failed to revoke intent");
|
|
111
|
+
console.error(chalk.red(String(err)));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
intentCommand
|
|
116
|
+
.command("propose")
|
|
117
|
+
.description("Generate an intent creation URL for the user to authorize in the browser")
|
|
118
|
+
.requiredOption("--nick <nick>", "Short identifier for the intent")
|
|
119
|
+
.requiredOption("--agents <slugs>", "Comma-separated merchant agent slugs (e.g. @x~starbucks)")
|
|
120
|
+
.requiredOption("--max-amount <dollars>", "Max amount per transaction in dollars")
|
|
121
|
+
.requiredOption("--prompt-playback <text>", "Human-readable description of the intent")
|
|
122
|
+
.option("--currency <code>", "Currency code", "USD")
|
|
123
|
+
.option("--recurring <type>", "Recurring type: once, daily, weekly, monthly", "once")
|
|
124
|
+
.option("--budget <dollars>", "Lifetime budget in dollars")
|
|
125
|
+
.option("--expires <date>", "Expiry date (ISO 8601)")
|
|
126
|
+
.action(async (opts) => {
|
|
127
|
+
const cfg = requireAuth();
|
|
128
|
+
const username = cfg.username;
|
|
129
|
+
if (!username) {
|
|
130
|
+
console.error(chalk.red("No username found. Please log in again."));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const baseUrl = cfg.apiUrl ?? "https://web42-network.vercel.app";
|
|
134
|
+
const params = new URLSearchParams({
|
|
135
|
+
nick: opts.nick,
|
|
136
|
+
agents: opts.agents,
|
|
137
|
+
max_amount: opts.maxAmount,
|
|
138
|
+
currency: opts.currency,
|
|
139
|
+
recurring: opts.recurring,
|
|
140
|
+
prompt_playback: opts.promptPlayback,
|
|
141
|
+
});
|
|
142
|
+
if (opts.budget)
|
|
143
|
+
params.set("budget", opts.budget);
|
|
144
|
+
if (opts.expires)
|
|
145
|
+
params.set("expires_at", opts.expires);
|
|
146
|
+
const url = `${baseUrl}/@${username}/intents/create?${params.toString()}`;
|
|
147
|
+
console.log(JSON.stringify({ url }, null, 2));
|
|
148
|
+
});
|
|
149
|
+
// ─── Checkout ─────────────────────────────────────────────
|
|
150
|
+
const checkoutCommand = new Command("checkout")
|
|
151
|
+
.description("Execute a payment against a matching intent (no human needed)")
|
|
152
|
+
.requiredOption("--cart <json>", "CartMandate JSON")
|
|
153
|
+
.requiredOption("--agent <slug>", "Merchant agent slug")
|
|
154
|
+
.requiredOption("--intent <nick>", "Intent nick to use")
|
|
155
|
+
.action(async (opts) => {
|
|
156
|
+
requireAuth();
|
|
157
|
+
let cart;
|
|
158
|
+
try {
|
|
159
|
+
cart = JSON.parse(opts.cart);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
console.error(chalk.red("Invalid cart JSON"));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
const spinner = ora("Processing checkout...").start();
|
|
166
|
+
try {
|
|
167
|
+
const res = await apiPost("/api/pay/checkout", {
|
|
168
|
+
cart,
|
|
169
|
+
agent_slug: opts.agent,
|
|
170
|
+
intent_nick: opts.intent,
|
|
171
|
+
});
|
|
172
|
+
spinner.stop();
|
|
173
|
+
console.log(JSON.stringify(res, null, 2));
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
spinner.fail("Checkout failed");
|
|
177
|
+
console.error(chalk.red(String(err)));
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
// ─── Sign (payment session for human approval) ───────────
|
|
182
|
+
const signCommand = new Command("sign").description("Create a payment session for human approval");
|
|
183
|
+
signCommand
|
|
184
|
+
.command("create")
|
|
185
|
+
.description("Create a new payment session")
|
|
186
|
+
.requiredOption("--cart <json>", "CartMandate JSON")
|
|
187
|
+
.requiredOption("--agent <slug>", "Merchant agent slug")
|
|
188
|
+
.action(async (opts) => {
|
|
189
|
+
requireAuth();
|
|
190
|
+
let cart;
|
|
191
|
+
try {
|
|
192
|
+
cart = JSON.parse(opts.cart);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
console.error(chalk.red("Invalid cart JSON"));
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
// Extract total from cart
|
|
199
|
+
let totalCents = 0;
|
|
200
|
+
let currency = "usd";
|
|
201
|
+
try {
|
|
202
|
+
const c = cart;
|
|
203
|
+
const contents = c.contents;
|
|
204
|
+
const pr = contents?.payment_request;
|
|
205
|
+
const details = pr?.details;
|
|
206
|
+
const total = details?.total;
|
|
207
|
+
totalCents = Math.round(total.amount.value * 100);
|
|
208
|
+
currency = total.amount.currency.toLowerCase();
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
console.error(chalk.red("Could not extract total from cart"));
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
const spinner = ora("Creating payment session...").start();
|
|
215
|
+
try {
|
|
216
|
+
const res = await apiPost("/api/pay/session", {
|
|
217
|
+
agent_slug: opts.agent,
|
|
218
|
+
cart,
|
|
219
|
+
total_cents: totalCents,
|
|
220
|
+
currency,
|
|
221
|
+
});
|
|
222
|
+
spinner.stop();
|
|
223
|
+
console.log(JSON.stringify(res, null, 2));
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
spinner.fail("Failed to create session");
|
|
227
|
+
console.error(chalk.red(String(err)));
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
signCommand
|
|
232
|
+
.command("get")
|
|
233
|
+
.description("Check the status of a payment session")
|
|
234
|
+
.argument("<code>", "Session code")
|
|
235
|
+
.action(async (code) => {
|
|
236
|
+
requireAuth();
|
|
237
|
+
const spinner = ora("Fetching session...").start();
|
|
238
|
+
try {
|
|
239
|
+
const res = await apiGet(`/api/pay/session/${encodeURIComponent(code)}`);
|
|
240
|
+
spinner.stop();
|
|
241
|
+
console.log(JSON.stringify(res, null, 2));
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
spinner.fail("Failed to fetch session");
|
|
245
|
+
console.error(chalk.red(String(err)));
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
// ─── Root pay command ─────────────────────────────────────
|
|
250
|
+
export const payCommand = new Command("pay")
|
|
251
|
+
.description("AP2 payment mandates — wallet, intents, checkout, signing")
|
|
252
|
+
.addCommand(walletCommand)
|
|
253
|
+
.addCommand(intentCommand)
|
|
254
|
+
.addCommand(checkoutCommand)
|
|
255
|
+
.addCommand(signCommand);
|
package/dist/commands/send.js
CHANGED
|
@@ -83,6 +83,7 @@ export const sendCommand = new Command("send")
|
|
|
83
83
|
.option("--new", "Start a new conversation (clears saved context)")
|
|
84
84
|
.option("--context <id>", "Use a specific context ID")
|
|
85
85
|
.option("--task-id <id>", "Reply to a specific task (e.g. one in input-required state)")
|
|
86
|
+
.option("--pay <token>", "Attach a payment token as an ap2.mandates.PaymentMandate data part")
|
|
86
87
|
.action(async (rawAgent, userMessage, opts) => {
|
|
87
88
|
// Normalize slug: @user/name → @user~name (DB format)
|
|
88
89
|
const agent = rawAgent.includes("/") && !isUrl(rawAgent)
|
|
@@ -196,7 +197,37 @@ export const sendCommand = new Command("send")
|
|
|
196
197
|
message: {
|
|
197
198
|
messageId: uuidv4(),
|
|
198
199
|
role: "user",
|
|
199
|
-
parts: [
|
|
200
|
+
parts: [
|
|
201
|
+
{ kind: "text", text: userMessage },
|
|
202
|
+
...(opts.pay
|
|
203
|
+
? [
|
|
204
|
+
{
|
|
205
|
+
kind: "data",
|
|
206
|
+
data: {
|
|
207
|
+
"ap2.mandates.PaymentMandate": {
|
|
208
|
+
payment_mandate_contents: {
|
|
209
|
+
payment_mandate_id: "",
|
|
210
|
+
payment_details_id: "",
|
|
211
|
+
payment_details_total: {
|
|
212
|
+
label: "Total",
|
|
213
|
+
amount: { currency: "USD", value: 0 },
|
|
214
|
+
refund_period: 3,
|
|
215
|
+
},
|
|
216
|
+
payment_response: {
|
|
217
|
+
request_id: "",
|
|
218
|
+
method_name: "WEB42_WALLET",
|
|
219
|
+
details: {},
|
|
220
|
+
},
|
|
221
|
+
merchant_agent: agent,
|
|
222
|
+
timestamp: new Date().toISOString(),
|
|
223
|
+
},
|
|
224
|
+
user_authorization: opts.pay,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
]
|
|
229
|
+
: []),
|
|
230
|
+
],
|
|
200
231
|
kind: "message",
|
|
201
232
|
contextId,
|
|
202
233
|
...(opts.taskId ? { taskId: opts.taskId } : {}),
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { authCommand } from "./commands/auth.js";
|
|
4
|
+
import { payCommand } from "./commands/pay.js";
|
|
4
5
|
import { registerCommand } from "./commands/register.js";
|
|
5
6
|
import { searchCommand } from "./commands/search.js";
|
|
6
7
|
import { sendCommand } from "./commands/send.js";
|
|
@@ -21,6 +22,7 @@ program
|
|
|
21
22
|
}
|
|
22
23
|
});
|
|
23
24
|
program.addCommand(authCommand);
|
|
25
|
+
program.addCommand(payCommand);
|
|
24
26
|
program.addCommand(registerCommand);
|
|
25
27
|
program.addCommand(searchCommand);
|
|
26
28
|
program.addCommand(sendCommand);
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.18";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.1.
|
|
1
|
+
export const CLI_VERSION = "0.1.18";
|