@web42/w42 0.1.17 → 0.1.19
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.js +121 -56
- package/dist/commands/send.js +54 -35
- package/dist/utils/tx-store.d.ts +25 -0
- package/dist/utils/tx-store.js +52 -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
|
package/dist/commands/pay.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Command } from "commander";
|
|
|
3
3
|
import ora from "ora";
|
|
4
4
|
import { apiGet, apiPost } from "../utils/api.js";
|
|
5
5
|
import { requireAuth, setConfigValue, } from "../utils/config.js";
|
|
6
|
+
import { getTx, listTxs, updateTx } from "../utils/tx-store.js";
|
|
6
7
|
// ─── Wallet ───────────────────────────────────────────────
|
|
7
8
|
const walletCommand = new Command("wallet")
|
|
8
9
|
.description("View or top up your wallet balance")
|
|
@@ -112,31 +113,68 @@ intentCommand
|
|
|
112
113
|
process.exit(1);
|
|
113
114
|
}
|
|
114
115
|
});
|
|
116
|
+
intentCommand
|
|
117
|
+
.command("propose")
|
|
118
|
+
.description("Generate an intent creation URL for the user to authorize in the browser")
|
|
119
|
+
.requiredOption("--nick <nick>", "Short identifier for the intent")
|
|
120
|
+
.requiredOption("--agents <slugs>", "Comma-separated merchant agent slugs (e.g. @x~starbucks)")
|
|
121
|
+
.requiredOption("--max-amount <dollars>", "Max amount per transaction in dollars")
|
|
122
|
+
.requiredOption("--prompt-playback <text>", "Human-readable description of the intent")
|
|
123
|
+
.option("--currency <code>", "Currency code", "USD")
|
|
124
|
+
.option("--recurring <type>", "Recurring type: once, daily, weekly, monthly", "once")
|
|
125
|
+
.option("--budget <dollars>", "Lifetime budget in dollars")
|
|
126
|
+
.option("--expires <date>", "Expiry date (ISO 8601)")
|
|
127
|
+
.action(async (opts) => {
|
|
128
|
+
const cfg = requireAuth();
|
|
129
|
+
const username = cfg.username;
|
|
130
|
+
if (!username) {
|
|
131
|
+
console.error(chalk.red("No username found. Please log in again."));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
const baseUrl = cfg.apiUrl ?? "https://web42-network.vercel.app";
|
|
135
|
+
const params = new URLSearchParams({
|
|
136
|
+
nick: opts.nick,
|
|
137
|
+
agents: opts.agents,
|
|
138
|
+
max_amount: opts.maxAmount,
|
|
139
|
+
currency: opts.currency,
|
|
140
|
+
recurring: opts.recurring,
|
|
141
|
+
prompt_playback: opts.promptPlayback,
|
|
142
|
+
});
|
|
143
|
+
if (opts.budget)
|
|
144
|
+
params.set("budget", opts.budget);
|
|
145
|
+
if (opts.expires)
|
|
146
|
+
params.set("expires_at", opts.expires);
|
|
147
|
+
const url = `${baseUrl}/@${username}/intents/create?${params.toString()}`;
|
|
148
|
+
console.log(JSON.stringify({ url }, null, 2));
|
|
149
|
+
});
|
|
115
150
|
// ─── Checkout ─────────────────────────────────────────────
|
|
116
151
|
const checkoutCommand = new Command("checkout")
|
|
117
152
|
.description("Execute a payment against a matching intent (no human needed)")
|
|
118
|
-
.requiredOption("--
|
|
119
|
-
.requiredOption("--agent <slug>", "Merchant agent slug")
|
|
153
|
+
.requiredOption("--tx <id>", "Transaction ID from tx-store")
|
|
120
154
|
.requiredOption("--intent <nick>", "Intent nick to use")
|
|
121
155
|
.action(async (opts) => {
|
|
122
156
|
requireAuth();
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
console.error(chalk.red("Invalid cart JSON"));
|
|
157
|
+
const tx = getTx(opts.tx);
|
|
158
|
+
if (!tx) {
|
|
159
|
+
console.error(chalk.red(`Transaction ${opts.tx} not found. Run: w42 pay list`));
|
|
129
160
|
process.exit(1);
|
|
130
161
|
}
|
|
131
162
|
const spinner = ora("Processing checkout...").start();
|
|
132
163
|
try {
|
|
133
164
|
const res = await apiPost("/api/pay/checkout", {
|
|
134
|
-
cart,
|
|
135
|
-
agent_slug:
|
|
165
|
+
cart: tx.cartMandate,
|
|
166
|
+
agent_slug: tx.agentSlug,
|
|
136
167
|
intent_nick: opts.intent,
|
|
137
168
|
});
|
|
138
169
|
spinner.stop();
|
|
139
|
-
|
|
170
|
+
// Store the payment mandate on the tx
|
|
171
|
+
if (res.payment_mandate) {
|
|
172
|
+
updateTx(opts.tx, {
|
|
173
|
+
paymentMandate: res.payment_mandate,
|
|
174
|
+
status: "approved",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
console.log(JSON.stringify({ tx: opts.tx, ...res }, null, 2));
|
|
140
178
|
}
|
|
141
179
|
catch (err) {
|
|
142
180
|
spinner.fail("Checkout failed");
|
|
@@ -148,25 +186,20 @@ const checkoutCommand = new Command("checkout")
|
|
|
148
186
|
const signCommand = new Command("sign").description("Create a payment session for human approval");
|
|
149
187
|
signCommand
|
|
150
188
|
.command("create")
|
|
151
|
-
.description("Create a new payment session")
|
|
152
|
-
.requiredOption("--
|
|
153
|
-
.requiredOption("--agent <slug>", "Merchant agent slug")
|
|
189
|
+
.description("Create a new payment session for human approval")
|
|
190
|
+
.requiredOption("--tx <id>", "Transaction ID from tx-store")
|
|
154
191
|
.action(async (opts) => {
|
|
155
192
|
requireAuth();
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
console.error(chalk.red("Invalid cart JSON"));
|
|
193
|
+
const tx = getTx(opts.tx);
|
|
194
|
+
if (!tx) {
|
|
195
|
+
console.error(chalk.red(`Transaction ${opts.tx} not found. Run: w42 pay list`));
|
|
162
196
|
process.exit(1);
|
|
163
197
|
}
|
|
164
|
-
// Extract total from cart
|
|
198
|
+
// Extract total from stored cart
|
|
165
199
|
let totalCents = 0;
|
|
166
200
|
let currency = "usd";
|
|
167
201
|
try {
|
|
168
|
-
const
|
|
169
|
-
const contents = c.contents;
|
|
202
|
+
const contents = tx.cartMandate.contents;
|
|
170
203
|
const pr = contents?.payment_request;
|
|
171
204
|
const details = pr?.details;
|
|
172
205
|
const total = details?.total;
|
|
@@ -174,19 +207,24 @@ signCommand
|
|
|
174
207
|
currency = total.amount.currency.toLowerCase();
|
|
175
208
|
}
|
|
176
209
|
catch {
|
|
177
|
-
console.error(chalk.red("Could not extract total from cart"));
|
|
210
|
+
console.error(chalk.red("Could not extract total from stored cart"));
|
|
178
211
|
process.exit(1);
|
|
179
212
|
}
|
|
180
213
|
const spinner = ora("Creating payment session...").start();
|
|
181
214
|
try {
|
|
182
215
|
const res = await apiPost("/api/pay/session", {
|
|
183
|
-
agent_slug:
|
|
184
|
-
cart,
|
|
216
|
+
agent_slug: tx.agentSlug,
|
|
217
|
+
cart: tx.cartMandate,
|
|
185
218
|
total_cents: totalCents,
|
|
186
219
|
currency,
|
|
187
220
|
});
|
|
188
221
|
spinner.stop();
|
|
189
|
-
|
|
222
|
+
updateTx(opts.tx, {
|
|
223
|
+
sessionCode: res.code,
|
|
224
|
+
signingUrl: res.signing_url,
|
|
225
|
+
status: "session_created",
|
|
226
|
+
});
|
|
227
|
+
console.log(JSON.stringify({ tx: opts.tx, signing_url: res.signing_url }, null, 2));
|
|
190
228
|
}
|
|
191
229
|
catch (err) {
|
|
192
230
|
spinner.fail("Failed to create session");
|
|
@@ -195,37 +233,63 @@ signCommand
|
|
|
195
233
|
}
|
|
196
234
|
});
|
|
197
235
|
signCommand
|
|
198
|
-
.command("
|
|
199
|
-
.description("
|
|
200
|
-
.argument("<
|
|
201
|
-
.action(async (
|
|
236
|
+
.command("get")
|
|
237
|
+
.description("Check the status of a payment session")
|
|
238
|
+
.argument("<tx_id>", "Transaction ID")
|
|
239
|
+
.action(async (txId) => {
|
|
202
240
|
requireAuth();
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
241
|
+
const tx = getTx(txId);
|
|
242
|
+
if (!tx) {
|
|
243
|
+
console.error(chalk.red(`Transaction ${txId} not found. Run: w42 pay list`));
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
if (!tx.sessionCode) {
|
|
247
|
+
console.error(chalk.red(`No session created yet. Run: w42 pay sign create --tx ${txId}`));
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
const spinner = ora("Fetching session...").start();
|
|
251
|
+
try {
|
|
252
|
+
const res = await apiGet(`/api/pay/session/${encodeURIComponent(tx.sessionCode)}`);
|
|
253
|
+
spinner.stop();
|
|
254
|
+
// If session is completed, store the payment mandate
|
|
255
|
+
if (res.status === "completed" && res.payment_token) {
|
|
256
|
+
updateTx(txId, {
|
|
257
|
+
paymentMandate: res.payment_mandate ?? {
|
|
258
|
+
payment_mandate_contents: {},
|
|
259
|
+
user_authorization: res.payment_token,
|
|
260
|
+
},
|
|
261
|
+
status: "approved",
|
|
262
|
+
});
|
|
225
263
|
}
|
|
264
|
+
console.log(JSON.stringify({ tx: txId, ...res }, null, 2));
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
spinner.fail("Failed to fetch session");
|
|
268
|
+
console.error(chalk.red(String(err)));
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
// ─── List (transactions) ──────────────────────────────────
|
|
273
|
+
const listCommand = new Command("list")
|
|
274
|
+
.description("List local payment transactions")
|
|
275
|
+
.option("--status <status>", "Filter by status (cart_received, session_created, approved, sent)")
|
|
276
|
+
.action((opts) => {
|
|
277
|
+
const txs = listTxs(opts.status);
|
|
278
|
+
if (txs.length === 0) {
|
|
279
|
+
console.log(chalk.dim("No transactions found."));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
for (const tx of txs) {
|
|
283
|
+
const cart = tx.cartMandate;
|
|
284
|
+
const contents = cart?.contents;
|
|
285
|
+
const pr = contents?.payment_request;
|
|
286
|
+
const details = pr?.details;
|
|
287
|
+
const total = details?.total;
|
|
288
|
+
const amount = total?.amount
|
|
289
|
+
? `${total.amount.currency} ${total.amount.value?.toFixed(2)}`
|
|
290
|
+
: "?";
|
|
291
|
+
console.log(`${chalk.cyan(tx.id)} ${chalk.dim(tx.status.padEnd(16))} ${amount} ${chalk.dim(tx.agentSlug)}`);
|
|
226
292
|
}
|
|
227
|
-
spinner.fail("Session still pending. Run again to continue polling.");
|
|
228
|
-
process.exit(1);
|
|
229
293
|
});
|
|
230
294
|
// ─── Root pay command ─────────────────────────────────────
|
|
231
295
|
export const payCommand = new Command("pay")
|
|
@@ -233,4 +297,5 @@ export const payCommand = new Command("pay")
|
|
|
233
297
|
.addCommand(walletCommand)
|
|
234
298
|
.addCommand(intentCommand)
|
|
235
299
|
.addCommand(checkoutCommand)
|
|
236
|
-
.addCommand(signCommand)
|
|
300
|
+
.addCommand(signCommand)
|
|
301
|
+
.addCommand(listCommand);
|
package/dist/commands/send.js
CHANGED
|
@@ -3,12 +3,14 @@ import chalk from "chalk";
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import ora from "ora";
|
|
5
5
|
import { v4 as uuidv4 } from "uuid";
|
|
6
|
+
import { isCartMandatePart, parseCartMandate } from "@web42/auth";
|
|
6
7
|
import { apiPost } from "../utils/api.js";
|
|
7
8
|
import { getConfig, getConfigValue, isTelemetryEnabled, requireAuth, setConfigValue } from "../utils/config.js";
|
|
9
|
+
import { getTx, saveTx, updateTx } from "../utils/tx-store.js";
|
|
8
10
|
function isUrl(s) {
|
|
9
11
|
return s.startsWith("http://") || s.startsWith("https://");
|
|
10
12
|
}
|
|
11
|
-
function printPart(part) {
|
|
13
|
+
function printPart(part, agentSlug) {
|
|
12
14
|
if (part.kind === "text") {
|
|
13
15
|
if (part.text)
|
|
14
16
|
process.stdout.write(part.text);
|
|
@@ -25,6 +27,24 @@ function printPart(part) {
|
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
else if (part.kind === "data") {
|
|
30
|
+
// Detect AP2 CartMandate parts and auto-store them
|
|
31
|
+
if (isCartMandatePart(part)) {
|
|
32
|
+
const cart = parseCartMandate(part);
|
|
33
|
+
if (cart) {
|
|
34
|
+
const total = cart.contents.payment_request.details.total;
|
|
35
|
+
const txId = saveTx({
|
|
36
|
+
cartMandate: cart,
|
|
37
|
+
agentSlug: agentSlug ?? "unknown",
|
|
38
|
+
});
|
|
39
|
+
console.log(chalk.cyan(`\n[CartMandate] ${txId}`));
|
|
40
|
+
for (const item of cart.contents.payment_request.details.displayItems) {
|
|
41
|
+
console.log(` ${item.label}: ${item.amount.currency} ${item.amount.value.toFixed(2)}`);
|
|
42
|
+
}
|
|
43
|
+
console.log(chalk.bold(` Total: ${total.amount.currency} ${total.amount.value.toFixed(2)}`));
|
|
44
|
+
console.log(chalk.dim(`\nTo pay: w42 pay sign create --tx ${txId}`));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
28
48
|
process.stdout.write("\n" + JSON.stringify(part.data, null, 2) + "\n");
|
|
29
49
|
}
|
|
30
50
|
}
|
|
@@ -83,7 +103,7 @@ export const sendCommand = new Command("send")
|
|
|
83
103
|
.option("--new", "Start a new conversation (clears saved context)")
|
|
84
104
|
.option("--context <id>", "Use a specific context ID")
|
|
85
105
|
.option("--task-id <id>", "Reply to a specific task (e.g. one in input-required state)")
|
|
86
|
-
.option("--pay <
|
|
106
|
+
.option("--pay <tx_id>", "Attach PaymentMandate from a transaction (use tx ID from w42 pay list)")
|
|
87
107
|
.action(async (rawAgent, userMessage, opts) => {
|
|
88
108
|
// Normalize slug: @user/name → @user~name (DB format)
|
|
89
109
|
const agent = rawAgent.includes("/") && !isUrl(rawAgent)
|
|
@@ -193,40 +213,39 @@ export const sendCommand = new Command("send")
|
|
|
193
213
|
const startTime = Date.now();
|
|
194
214
|
let firstTokenMs;
|
|
195
215
|
try {
|
|
216
|
+
// Build payment part from tx-store if --pay is provided
|
|
217
|
+
const paymentParts = [];
|
|
218
|
+
if (opts.pay) {
|
|
219
|
+
const tx = getTx(opts.pay);
|
|
220
|
+
if (!tx) {
|
|
221
|
+
console.error(chalk.red(`Transaction ${opts.pay} not found. Run: w42 pay list`));
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
if (tx.status === "cart_received") {
|
|
225
|
+
console.error(chalk.red(`Session not created yet. Run: w42 pay sign create --tx ${opts.pay}`));
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
if (tx.status === "session_created") {
|
|
229
|
+
console.error(chalk.red(`Payment not yet approved. Run: w42 pay sign get ${opts.pay}`));
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
if (!tx.paymentMandate) {
|
|
233
|
+
console.error(chalk.red(`No payment mandate found on transaction ${opts.pay}`));
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
paymentParts.push({
|
|
237
|
+
kind: "data",
|
|
238
|
+
data: { "ap2.mandates.PaymentMandate": tx.paymentMandate },
|
|
239
|
+
});
|
|
240
|
+
updateTx(opts.pay, { status: "sent" });
|
|
241
|
+
}
|
|
196
242
|
const stream = client.sendMessageStream({
|
|
197
243
|
message: {
|
|
198
244
|
messageId: uuidv4(),
|
|
199
245
|
role: "user",
|
|
200
246
|
parts: [
|
|
201
247
|
{ kind: "text", text: userMessage },
|
|
202
|
-
...
|
|
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
|
-
: []),
|
|
248
|
+
...paymentParts,
|
|
230
249
|
],
|
|
231
250
|
kind: "message",
|
|
232
251
|
contextId,
|
|
@@ -238,7 +257,7 @@ export const sendCommand = new Command("send")
|
|
|
238
257
|
if (firstTokenMs === undefined)
|
|
239
258
|
firstTokenMs = Date.now() - startTime;
|
|
240
259
|
for (const part of event.parts)
|
|
241
|
-
printPart(part);
|
|
260
|
+
printPart(part, agentKey);
|
|
242
261
|
}
|
|
243
262
|
else if (event.kind === "artifact-update") {
|
|
244
263
|
if (firstTokenMs === undefined)
|
|
@@ -248,12 +267,12 @@ export const sendCommand = new Command("send")
|
|
|
248
267
|
if (!event.append)
|
|
249
268
|
process.stdout.write("\n");
|
|
250
269
|
for (const part of event.artifact.parts ?? [])
|
|
251
|
-
printPart(part);
|
|
270
|
+
printPart(part, agentKey);
|
|
252
271
|
}
|
|
253
272
|
else if (event.kind === "status-update") {
|
|
254
273
|
if (event.status?.message) {
|
|
255
274
|
for (const part of event.status.message.parts ?? [])
|
|
256
|
-
printPart(part);
|
|
275
|
+
printPart(part, agentKey);
|
|
257
276
|
}
|
|
258
277
|
handleTaskState(event.status?.state, event.taskId);
|
|
259
278
|
}
|
|
@@ -265,11 +284,11 @@ export const sendCommand = new Command("send")
|
|
|
265
284
|
for (const artifact of event.artifacts ?? []) {
|
|
266
285
|
process.stdout.write("\n");
|
|
267
286
|
for (const part of artifact.parts ?? [])
|
|
268
|
-
printPart(part);
|
|
287
|
+
printPart(part, agentKey);
|
|
269
288
|
}
|
|
270
289
|
if (event.status?.message) {
|
|
271
290
|
for (const part of event.status.message.parts ?? [])
|
|
272
|
-
printPart(part);
|
|
291
|
+
printPart(part, agentKey);
|
|
273
292
|
}
|
|
274
293
|
handleTaskState(event.status?.state, event.id);
|
|
275
294
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local transaction store for AP2 payment flows.
|
|
3
|
+
*
|
|
4
|
+
* Tracks the lifecycle of a payment transaction:
|
|
5
|
+
* cart_received → session_created → approved → sent
|
|
6
|
+
*
|
|
7
|
+
* Stored in ~/.config/w42-transactions/config.json via `conf`.
|
|
8
|
+
*/
|
|
9
|
+
export interface StoredTransaction {
|
|
10
|
+
id: string;
|
|
11
|
+
cartMandate: Record<string, unknown>;
|
|
12
|
+
agentSlug: string;
|
|
13
|
+
sessionCode?: string;
|
|
14
|
+
signingUrl?: string;
|
|
15
|
+
paymentMandate?: Record<string, unknown>;
|
|
16
|
+
status: "cart_received" | "session_created" | "approved" | "sent";
|
|
17
|
+
createdAt: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function saveTx(opts: {
|
|
20
|
+
cartMandate: Record<string, unknown>;
|
|
21
|
+
agentSlug: string;
|
|
22
|
+
}): string;
|
|
23
|
+
export declare function getTx(id: string): StoredTransaction | null;
|
|
24
|
+
export declare function updateTx(id: string, updates: Partial<Omit<StoredTransaction, "id">>): void;
|
|
25
|
+
export declare function listTxs(status?: string): StoredTransaction[];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local transaction store for AP2 payment flows.
|
|
3
|
+
*
|
|
4
|
+
* Tracks the lifecycle of a payment transaction:
|
|
5
|
+
* cart_received → session_created → approved → sent
|
|
6
|
+
*
|
|
7
|
+
* Stored in ~/.config/w42-transactions/config.json via `conf`.
|
|
8
|
+
*/
|
|
9
|
+
import Conf from "conf";
|
|
10
|
+
import crypto from "crypto";
|
|
11
|
+
// ─── Store ───────────────────────────────────────────────
|
|
12
|
+
const store = new Conf({
|
|
13
|
+
projectName: "w42-transactions",
|
|
14
|
+
defaults: { transactions: {} },
|
|
15
|
+
});
|
|
16
|
+
function generateId() {
|
|
17
|
+
return `tx_${crypto.randomBytes(4).toString("hex")}`;
|
|
18
|
+
}
|
|
19
|
+
// ─── Public API ──────────────────────────────────────────
|
|
20
|
+
export function saveTx(opts) {
|
|
21
|
+
const id = generateId();
|
|
22
|
+
const tx = {
|
|
23
|
+
id,
|
|
24
|
+
cartMandate: opts.cartMandate,
|
|
25
|
+
agentSlug: opts.agentSlug,
|
|
26
|
+
status: "cart_received",
|
|
27
|
+
createdAt: new Date().toISOString(),
|
|
28
|
+
};
|
|
29
|
+
const all = store.get("transactions");
|
|
30
|
+
all[id] = tx;
|
|
31
|
+
store.set("transactions", all);
|
|
32
|
+
return id;
|
|
33
|
+
}
|
|
34
|
+
export function getTx(id) {
|
|
35
|
+
const all = store.get("transactions");
|
|
36
|
+
return all[id] ?? null;
|
|
37
|
+
}
|
|
38
|
+
export function updateTx(id, updates) {
|
|
39
|
+
const all = store.get("transactions");
|
|
40
|
+
const tx = all[id];
|
|
41
|
+
if (!tx)
|
|
42
|
+
return;
|
|
43
|
+
Object.assign(tx, updates);
|
|
44
|
+
all[id] = tx;
|
|
45
|
+
store.set("transactions", all);
|
|
46
|
+
}
|
|
47
|
+
export function listTxs(status) {
|
|
48
|
+
const all = Object.values(store.get("transactions"));
|
|
49
|
+
if (!status)
|
|
50
|
+
return all;
|
|
51
|
+
return all.filter((tx) => tx.status === status);
|
|
52
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.19";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.1.
|
|
1
|
+
export const CLI_VERSION = "0.1.19";
|