@web42/w42 0.1.16 → 0.1.17
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/dist/commands/pay.d.ts +2 -0
- package/dist/commands/pay.js +236 -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
|
@@ -0,0 +1,236 @@
|
|
|
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
|
+
// ─── Checkout ─────────────────────────────────────────────
|
|
116
|
+
const checkoutCommand = new Command("checkout")
|
|
117
|
+
.description("Execute a payment against a matching intent (no human needed)")
|
|
118
|
+
.requiredOption("--cart <json>", "CartMandate JSON")
|
|
119
|
+
.requiredOption("--agent <slug>", "Merchant agent slug")
|
|
120
|
+
.requiredOption("--intent <nick>", "Intent nick to use")
|
|
121
|
+
.action(async (opts) => {
|
|
122
|
+
requireAuth();
|
|
123
|
+
let cart;
|
|
124
|
+
try {
|
|
125
|
+
cart = JSON.parse(opts.cart);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
console.error(chalk.red("Invalid cart JSON"));
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const spinner = ora("Processing checkout...").start();
|
|
132
|
+
try {
|
|
133
|
+
const res = await apiPost("/api/pay/checkout", {
|
|
134
|
+
cart,
|
|
135
|
+
agent_slug: opts.agent,
|
|
136
|
+
intent_nick: opts.intent,
|
|
137
|
+
});
|
|
138
|
+
spinner.stop();
|
|
139
|
+
console.log(JSON.stringify(res, null, 2));
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
spinner.fail("Checkout failed");
|
|
143
|
+
console.error(chalk.red(String(err)));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
// ─── Sign (payment session for human approval) ───────────
|
|
148
|
+
const signCommand = new Command("sign").description("Create a payment session for human approval");
|
|
149
|
+
signCommand
|
|
150
|
+
.command("create")
|
|
151
|
+
.description("Create a new payment session")
|
|
152
|
+
.requiredOption("--cart <json>", "CartMandate JSON")
|
|
153
|
+
.requiredOption("--agent <slug>", "Merchant agent slug")
|
|
154
|
+
.action(async (opts) => {
|
|
155
|
+
requireAuth();
|
|
156
|
+
let cart;
|
|
157
|
+
try {
|
|
158
|
+
cart = JSON.parse(opts.cart);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
console.error(chalk.red("Invalid cart JSON"));
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
// Extract total from cart
|
|
165
|
+
let totalCents = 0;
|
|
166
|
+
let currency = "usd";
|
|
167
|
+
try {
|
|
168
|
+
const c = cart;
|
|
169
|
+
const contents = c.contents;
|
|
170
|
+
const pr = contents?.payment_request;
|
|
171
|
+
const details = pr?.details;
|
|
172
|
+
const total = details?.total;
|
|
173
|
+
totalCents = Math.round(total.amount.value * 100);
|
|
174
|
+
currency = total.amount.currency.toLowerCase();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
console.error(chalk.red("Could not extract total from cart"));
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
const spinner = ora("Creating payment session...").start();
|
|
181
|
+
try {
|
|
182
|
+
const res = await apiPost("/api/pay/session", {
|
|
183
|
+
agent_slug: opts.agent,
|
|
184
|
+
cart,
|
|
185
|
+
total_cents: totalCents,
|
|
186
|
+
currency,
|
|
187
|
+
});
|
|
188
|
+
spinner.stop();
|
|
189
|
+
console.log(JSON.stringify(res, null, 2));
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
spinner.fail("Failed to create session");
|
|
193
|
+
console.error(chalk.red(String(err)));
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
signCommand
|
|
198
|
+
.command("poll")
|
|
199
|
+
.description("Poll a payment session until completed or expired")
|
|
200
|
+
.argument("<code>", "Session code")
|
|
201
|
+
.action(async (code) => {
|
|
202
|
+
requireAuth();
|
|
203
|
+
const maxAttempts = 30;
|
|
204
|
+
const intervalMs = 2000;
|
|
205
|
+
const spinner = ora("Waiting for approval...").start();
|
|
206
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
207
|
+
try {
|
|
208
|
+
const res = await apiGet(`/api/pay/session/${encodeURIComponent(code)}`);
|
|
209
|
+
if (res.status === "completed") {
|
|
210
|
+
spinner.stop();
|
|
211
|
+
console.log(JSON.stringify(res, null, 2));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (res.status === "expired") {
|
|
215
|
+
spinner.fail("Session expired");
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
// Still pending — wait and retry
|
|
219
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
spinner.fail("Failed to poll session");
|
|
223
|
+
console.error(chalk.red(String(err)));
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
spinner.fail("Session still pending. Run again to continue polling.");
|
|
228
|
+
process.exit(1);
|
|
229
|
+
});
|
|
230
|
+
// ─── Root pay command ─────────────────────────────────────
|
|
231
|
+
export const payCommand = new Command("pay")
|
|
232
|
+
.description("AP2 payment mandates — wallet, intents, checkout, signing")
|
|
233
|
+
.addCommand(walletCommand)
|
|
234
|
+
.addCommand(intentCommand)
|
|
235
|
+
.addCommand(checkoutCommand)
|
|
236
|
+
.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.17";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.1.
|
|
1
|
+
export const CLI_VERSION = "0.1.17";
|