agentmall 0.1.12 → 0.1.14
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/cli.js +366 -183
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -264,6 +264,126 @@ var AgentMallRefunds = class {
|
|
|
264
264
|
}
|
|
265
265
|
};
|
|
266
266
|
|
|
267
|
+
// src/cli/ui.ts
|
|
268
|
+
var c = {
|
|
269
|
+
reset: "\x1B[0m",
|
|
270
|
+
bold: "\x1B[1m",
|
|
271
|
+
dim: "\x1B[2m",
|
|
272
|
+
italic: "\x1B[3m",
|
|
273
|
+
strike: "\x1B[9m",
|
|
274
|
+
red: "\x1B[31m",
|
|
275
|
+
green: "\x1B[32m",
|
|
276
|
+
yellow: "\x1B[33m",
|
|
277
|
+
blue: "\x1B[34m",
|
|
278
|
+
magenta: "\x1B[35m",
|
|
279
|
+
cyan: "\x1B[36m",
|
|
280
|
+
gray: "\x1B[90m"
|
|
281
|
+
};
|
|
282
|
+
var RULE_WIDTH = 48;
|
|
283
|
+
function banner() {
|
|
284
|
+
console.log();
|
|
285
|
+
console.log(
|
|
286
|
+
` ${c.bold}\u25CE agentmall${c.reset} ${c.dim}\u2014 buy anything with a URL${c.reset}`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
function section(title) {
|
|
290
|
+
const pad = Math.max(2, RULE_WIDTH - title.length - 2);
|
|
291
|
+
console.log();
|
|
292
|
+
console.log(
|
|
293
|
+
` ${c.dim}\u2500\u2500\u2500${c.reset} ${c.bold}${title}${c.reset} ${c.dim}${"\u2500".repeat(pad)}${c.reset}`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
function kv(key, value, opts) {
|
|
297
|
+
const k = `${c.gray}${key.padEnd(16)}${c.reset}`;
|
|
298
|
+
const color = opts?.color ?? "";
|
|
299
|
+
const reset = color ? c.reset : "";
|
|
300
|
+
const v = opts?.dim ? `${c.dim}${value}${c.reset}` : `${color}${value}${reset}`;
|
|
301
|
+
console.log(` ${k} ${v}`);
|
|
302
|
+
}
|
|
303
|
+
function line(text) {
|
|
304
|
+
console.log(` ${text}`);
|
|
305
|
+
}
|
|
306
|
+
function gap() {
|
|
307
|
+
console.log();
|
|
308
|
+
}
|
|
309
|
+
function muted(text) {
|
|
310
|
+
console.log(` ${c.dim}${text}${c.reset}`);
|
|
311
|
+
}
|
|
312
|
+
function hint(text) {
|
|
313
|
+
console.log(` ${c.dim}${text}${c.reset}`);
|
|
314
|
+
}
|
|
315
|
+
var STATUS_COLORS = {
|
|
316
|
+
payment_received: c.yellow,
|
|
317
|
+
submitted: c.blue,
|
|
318
|
+
pending: c.blue,
|
|
319
|
+
confirmed: c.green,
|
|
320
|
+
shipped: c.cyan,
|
|
321
|
+
delivered: c.green,
|
|
322
|
+
completed: c.green,
|
|
323
|
+
ordered: c.green,
|
|
324
|
+
failed: c.red,
|
|
325
|
+
cancelled: c.red,
|
|
326
|
+
approved: c.yellow,
|
|
327
|
+
processing: c.blue,
|
|
328
|
+
processed: c.green,
|
|
329
|
+
in_stock: c.green,
|
|
330
|
+
out_of_stock: c.red,
|
|
331
|
+
low_stock: c.yellow
|
|
332
|
+
};
|
|
333
|
+
function statusBadge(status2) {
|
|
334
|
+
const color = STATUS_COLORS[status2] ?? c.gray;
|
|
335
|
+
return `${color}\u25CF${c.reset} ${status2}`;
|
|
336
|
+
}
|
|
337
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
338
|
+
async function spin(label, task) {
|
|
339
|
+
let frame = 0;
|
|
340
|
+
const render = (char, color, text) => {
|
|
341
|
+
process.stdout.write(`\r ${color}${char}${c.reset} ${text}\x1B[K`);
|
|
342
|
+
};
|
|
343
|
+
render(FRAMES[0], c.dim, `${c.dim}${label}${c.reset}`);
|
|
344
|
+
const interval = setInterval(() => {
|
|
345
|
+
frame = (frame + 1) % FRAMES.length;
|
|
346
|
+
render(FRAMES[frame], c.dim, `${c.dim}${label}${c.reset}`);
|
|
347
|
+
}, 80);
|
|
348
|
+
try {
|
|
349
|
+
const result = await task();
|
|
350
|
+
clearInterval(interval);
|
|
351
|
+
render("\u2714", c.green, label);
|
|
352
|
+
process.stdout.write("\n");
|
|
353
|
+
return result;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
clearInterval(interval);
|
|
356
|
+
render("\u2716", c.red, label);
|
|
357
|
+
process.stdout.write("\n");
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function stripAnsi(s) {
|
|
362
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
363
|
+
}
|
|
364
|
+
function box(lines) {
|
|
365
|
+
const maxLen = Math.max(
|
|
366
|
+
...lines.map((l) => stripAnsi(l).length),
|
|
367
|
+
38
|
|
368
|
+
);
|
|
369
|
+
const w = maxLen + 2;
|
|
370
|
+
console.log(` ${c.dim}\u256D${"\u2500".repeat(w)}\u256E${c.reset}`);
|
|
371
|
+
for (const l of lines) {
|
|
372
|
+
const visible = stripAnsi(l).length;
|
|
373
|
+
const pad = maxLen - visible;
|
|
374
|
+
console.log(
|
|
375
|
+
` ${c.dim}\u2502${c.reset} ${l}${" ".repeat(Math.max(0, pad))} ${c.dim}\u2502${c.reset}`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
console.log(` ${c.dim}\u2570${"\u2500".repeat(w)}\u256F${c.reset}`);
|
|
379
|
+
}
|
|
380
|
+
function errorMsg(label, message) {
|
|
381
|
+
console.log();
|
|
382
|
+
console.log(` ${c.red}\u2716 ${label}${c.reset}`);
|
|
383
|
+
console.log(` ${message}`);
|
|
384
|
+
console.log();
|
|
385
|
+
}
|
|
386
|
+
|
|
267
387
|
// src/cli/display.ts
|
|
268
388
|
var FAILURE_MESSAGES = {
|
|
269
389
|
budget_exceeded: "Actual total exceeded your maximum budget.",
|
|
@@ -303,26 +423,27 @@ function calculateMinimumBudget(product, quantity = 1, variantUnitPriceCents) {
|
|
|
303
423
|
return unitPriceCents * Math.max(1, quantity);
|
|
304
424
|
}
|
|
305
425
|
function displayProduct(product) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
console.log();
|
|
426
|
+
section("Product");
|
|
427
|
+
line(`${c.bold}${product.title}${c.reset}`);
|
|
428
|
+
line(`${c.dim}${product.retailer} \xB7 ${statusBadge(product.availability)}${c.reset}`);
|
|
429
|
+
gap();
|
|
311
430
|
if (product.listPrice && product.discountPercent) {
|
|
312
|
-
|
|
313
|
-
|
|
431
|
+
kv(
|
|
432
|
+
"Price",
|
|
433
|
+
`${c.green}${formatCents(product.price)}${c.reset} ${c.dim}${c.strike}${formatCents(product.listPrice)}${c.reset} ${c.yellow}-${product.discountPercent}%${c.reset}`
|
|
314
434
|
);
|
|
315
435
|
} else {
|
|
316
|
-
|
|
436
|
+
kv("Price", formatCents(product.price), { color: c.green });
|
|
317
437
|
}
|
|
318
438
|
if (product.variants?.length) {
|
|
319
|
-
|
|
439
|
+
gap();
|
|
320
440
|
for (const v of product.variants) {
|
|
321
|
-
const label = v.label ? `${v.label}
|
|
322
|
-
|
|
441
|
+
const label = v.label ? `${c.dim}${v.label}:${c.reset} ` : "";
|
|
442
|
+
line(
|
|
443
|
+
`${label}${c.bold}${v.value}${c.reset} ${c.dim}\u2014 $${v.price.toFixed(2)}${c.reset}`
|
|
444
|
+
);
|
|
323
445
|
}
|
|
324
446
|
}
|
|
325
|
-
console.log();
|
|
326
447
|
}
|
|
327
448
|
function displayCheckoutEstimate(product, quantity = 1, variantSelections, variantUnitPriceCents) {
|
|
328
449
|
const quantityClamped = Math.max(1, quantity);
|
|
@@ -335,124 +456,131 @@ function displayCheckoutEstimate(product, quantity = 1, variantSelections, varia
|
|
|
335
456
|
const linePrice = unitPriceCents * quantityClamped;
|
|
336
457
|
const listBaseUnitPrice = typeof product.listPrice === "number" ? Math.max(product.listPrice, variantUnitPriceCents ?? 0) : void 0;
|
|
337
458
|
const lineListPrice = typeof listBaseUnitPrice === "number" ? listBaseUnitPrice * quantityClamped : void 0;
|
|
338
|
-
|
|
339
|
-
console.log(" \x1B[1m\u{1F4B8} Checkout Estimate\x1B[0m");
|
|
459
|
+
section("Checkout Estimate");
|
|
340
460
|
if (lineListPrice && product.discountPercent) {
|
|
341
|
-
|
|
342
|
-
|
|
461
|
+
kv(
|
|
462
|
+
"Price",
|
|
463
|
+
`${c.green}${formatCents(linePrice)}${c.reset} ${c.dim}${c.strike}${formatCents(lineListPrice)}${c.reset} ${c.yellow}-${product.discountPercent}%${c.reset}`
|
|
343
464
|
);
|
|
344
465
|
} else {
|
|
345
|
-
|
|
466
|
+
kv("Price", formatCents(linePrice), { color: c.green });
|
|
346
467
|
}
|
|
347
|
-
|
|
468
|
+
kv("Quantity", `${quantityClamped}`);
|
|
348
469
|
if (quantityClamped > 1) {
|
|
349
|
-
|
|
470
|
+
kv("Unit price", formatCents(unitPriceCents), { dim: true });
|
|
350
471
|
}
|
|
351
472
|
if (variantSelections?.length) {
|
|
352
473
|
for (const variant of variantSelections) {
|
|
353
|
-
|
|
474
|
+
kv(variant.label, variant.value);
|
|
354
475
|
}
|
|
355
476
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
477
|
+
kv("Budget", `${c.bold}${formatCents(suggestedBudget)}${c.reset} ${c.dim}(suggested)${c.reset}`);
|
|
478
|
+
kv("Fee", formatCents(SERVICE_FEE_CENTS), { dim: true });
|
|
479
|
+
kv(
|
|
480
|
+
"Total",
|
|
481
|
+
`${c.bold}${formatCents(suggestedBudget + SERVICE_FEE_CENTS)}${c.reset} USDC`
|
|
361
482
|
);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
console.log(
|
|
366
|
-
` Any unused amount from your max budget is refunded automatically after checkout.`
|
|
367
|
-
);
|
|
368
|
-
console.log(
|
|
369
|
-
` The buffer covers unforeseen tax, shipping, and retailer price changes at final checkout.`
|
|
483
|
+
gap();
|
|
484
|
+
hint(
|
|
485
|
+
"Budget includes a 15% tax/price buffer plus an $8 shipping buffer."
|
|
370
486
|
);
|
|
487
|
+
hint("Unused amount is refunded automatically after checkout.");
|
|
371
488
|
if (product.cached) {
|
|
372
|
-
|
|
489
|
+
gap();
|
|
490
|
+
line(
|
|
491
|
+
`${c.yellow}\u26A0${c.reset} ${c.yellow}Price from cache \u2014 may not reflect current listing${c.reset}`
|
|
492
|
+
);
|
|
373
493
|
}
|
|
374
|
-
console.log();
|
|
375
494
|
}
|
|
376
495
|
function displayOrderSummary(body) {
|
|
377
|
-
|
|
378
|
-
console.log(" \x1B[1m\u{1F9FE} Order Summary\x1B[0m");
|
|
496
|
+
section("Order Summary");
|
|
379
497
|
for (const item of body.items) {
|
|
380
|
-
const url = item.product_url;
|
|
381
498
|
const qty = item.quantity ?? 1;
|
|
382
|
-
|
|
499
|
+
line(`${c.dim}${qty}x${c.reset} ${item.product_url}`);
|
|
383
500
|
if (item.variant?.length) {
|
|
384
501
|
for (const v of item.variant) {
|
|
385
|
-
|
|
502
|
+
line(` ${c.dim}${v.label}:${c.reset} ${v.value}`);
|
|
386
503
|
}
|
|
387
504
|
}
|
|
388
505
|
}
|
|
506
|
+
section("Shipping");
|
|
389
507
|
const addr = body.delivery_address;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
508
|
+
line(`${addr.first_name} ${addr.last_name}`);
|
|
509
|
+
line(
|
|
510
|
+
`${addr.address_line1}${addr.address_line2 ? `, ${addr.address_line2}` : ""}`
|
|
511
|
+
);
|
|
512
|
+
line(
|
|
513
|
+
`${addr.city}, ${addr.state ?? ""} ${addr.postal_code} ${addr.country}`
|
|
514
|
+
);
|
|
396
515
|
}
|
|
397
516
|
function displayOrderResult(order) {
|
|
398
|
-
|
|
517
|
+
gap();
|
|
518
|
+
const lines = [];
|
|
399
519
|
if (order.status === "failed") {
|
|
400
|
-
|
|
520
|
+
lines.push(`${c.red}\u2716 Order failed during submission${c.reset}`);
|
|
401
521
|
} else if (order.deduplicated) {
|
|
402
|
-
|
|
522
|
+
lines.push(`${c.yellow}\u267B Reused existing order${c.reset}`);
|
|
403
523
|
} else {
|
|
404
|
-
|
|
524
|
+
lines.push(`${c.green}\u2714 Order placed${c.reset}`);
|
|
405
525
|
}
|
|
406
|
-
|
|
407
|
-
|
|
526
|
+
lines.push("");
|
|
527
|
+
lines.push(`${c.gray}ID${c.reset} ${c.bold}${order.id}${c.reset}`);
|
|
528
|
+
lines.push(`${c.gray}Status${c.reset} ${statusBadge(order.status)}`);
|
|
408
529
|
if (order.buyerToken) {
|
|
409
|
-
|
|
530
|
+
lines.push("");
|
|
531
|
+
lines.push(`${c.dim}\u{1F510} Saved buyer token for status checks${c.reset}`);
|
|
410
532
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
order.status === "failed" ?
|
|
533
|
+
lines.push("");
|
|
534
|
+
lines.push(
|
|
535
|
+
order.status === "failed" ? `${c.dim}Check failure details and refund progress:${c.reset}` : `${c.dim}Check status in 5-10 minutes:${c.reset}`
|
|
414
536
|
);
|
|
415
|
-
|
|
416
|
-
|
|
537
|
+
lines.push(`${c.cyan}npx agentmall status ${order.id}${c.reset}`);
|
|
538
|
+
box(lines);
|
|
539
|
+
gap();
|
|
417
540
|
}
|
|
418
541
|
function displayStatus(purchase) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
console.log(` Status: ${purchase.status}`);
|
|
542
|
+
section(`Order ${purchase.id}`);
|
|
543
|
+
kv("Status", statusBadge(purchase.status));
|
|
422
544
|
if (purchase.items?.length) {
|
|
545
|
+
gap();
|
|
423
546
|
for (const item of purchase.items) {
|
|
424
|
-
const price = item.price ? ` \u2014 ${formatCents(item.price)}` : "";
|
|
425
|
-
|
|
547
|
+
const price = item.price ? ` ${c.dim}\u2014 ${formatCents(item.price)}${c.reset}` : "";
|
|
548
|
+
line(
|
|
549
|
+
`${c.dim}${item.quantity}x${c.reset} ${item.title ?? item.productRef}${price}`
|
|
550
|
+
);
|
|
426
551
|
}
|
|
427
552
|
}
|
|
553
|
+
gap();
|
|
428
554
|
if (purchase.finalTotal) {
|
|
429
|
-
|
|
555
|
+
kv("Final total", formatCents(purchase.finalTotal));
|
|
430
556
|
}
|
|
431
557
|
if (purchase.failureReason) {
|
|
432
|
-
|
|
433
|
-
|
|
558
|
+
kv(
|
|
559
|
+
"Failure",
|
|
560
|
+
`${c.red}${FAILURE_MESSAGES[purchase.failureReason] ?? purchase.failureReason}${c.reset}`
|
|
434
561
|
);
|
|
435
562
|
}
|
|
436
563
|
if (purchase.deliveryMethod) {
|
|
437
|
-
|
|
564
|
+
kv("Shipping", purchase.deliveryMethod);
|
|
438
565
|
}
|
|
439
|
-
|
|
440
|
-
|
|
566
|
+
kv("Created", new Date(purchase.createdAt).toLocaleString(), { dim: true });
|
|
567
|
+
gap();
|
|
441
568
|
}
|
|
442
569
|
function displayRefund(refund2) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
console.log(` Reason: ${refund2.reason}`);
|
|
570
|
+
section(`Refund ${refund2.id}`);
|
|
571
|
+
kv("Status", statusBadge(refund2.status));
|
|
572
|
+
kv("Amount", formatCents(refund2.amountCents), { color: c.green });
|
|
573
|
+
kv("Reason", refund2.reason);
|
|
448
574
|
if (refund2.txHash) {
|
|
449
|
-
|
|
575
|
+
kv("TX", refund2.txHash, { dim: true });
|
|
450
576
|
}
|
|
451
|
-
|
|
577
|
+
kv("Created", new Date(refund2.createdAt).toLocaleString(), { dim: true });
|
|
452
578
|
if (refund2.processedAt) {
|
|
453
|
-
|
|
579
|
+
kv("Processed", new Date(refund2.processedAt).toLocaleString(), {
|
|
580
|
+
dim: true
|
|
581
|
+
});
|
|
454
582
|
}
|
|
455
|
-
|
|
583
|
+
gap();
|
|
456
584
|
}
|
|
457
585
|
|
|
458
586
|
// src/cli/prompts.ts
|
|
@@ -673,7 +801,11 @@ async function runInteractiveCommand(command2, args2) {
|
|
|
673
801
|
}
|
|
674
802
|
async function readTempoWhoami() {
|
|
675
803
|
try {
|
|
676
|
-
const result = await runCommandCapture("tempo", [
|
|
804
|
+
const result = await runCommandCapture("tempo", [
|
|
805
|
+
"wallet",
|
|
806
|
+
"-j",
|
|
807
|
+
"whoami"
|
|
808
|
+
]);
|
|
677
809
|
if (result.code !== 0) return null;
|
|
678
810
|
return JSON.parse(result.stdout);
|
|
679
811
|
} catch {
|
|
@@ -686,17 +818,18 @@ async function ensureTempoInstalled() {
|
|
|
686
818
|
if (result.code === 0) return;
|
|
687
819
|
} catch {
|
|
688
820
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
821
|
+
errorMsg(
|
|
822
|
+
"Tempo CLI required",
|
|
823
|
+
`Install it first:
|
|
824
|
+
${c.cyan}curl -fsSL https://tempo.xyz/install | bash${c.reset}`
|
|
825
|
+
);
|
|
693
826
|
process.exit(1);
|
|
694
827
|
}
|
|
695
828
|
async function ensureTempoReady() {
|
|
696
829
|
await ensureTempoInstalled();
|
|
697
830
|
const whoami = await readTempoWhoami();
|
|
698
831
|
if (whoami?.ready) return whoami;
|
|
699
|
-
|
|
832
|
+
muted("Tempo wallet login required...");
|
|
700
833
|
const loginCode = await runInteractiveCommand("tempo", ["wallet", "login"]);
|
|
701
834
|
if (loginCode !== 0) {
|
|
702
835
|
throw new Error("Tempo wallet login failed");
|
|
@@ -711,30 +844,6 @@ function formatUsdAmount(cents) {
|
|
|
711
844
|
function sleep(ms) {
|
|
712
845
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
713
846
|
}
|
|
714
|
-
async function withSpinner(label, task) {
|
|
715
|
-
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
716
|
-
let frame = 0;
|
|
717
|
-
const render = (prefix) => {
|
|
718
|
-
process.stdout.write(`\r ${prefix} ${label}`);
|
|
719
|
-
};
|
|
720
|
-
render(frames[frame]);
|
|
721
|
-
const interval = setInterval(() => {
|
|
722
|
-
frame = (frame + 1) % frames.length;
|
|
723
|
-
render(frames[frame]);
|
|
724
|
-
}, 80);
|
|
725
|
-
try {
|
|
726
|
-
const result = await task();
|
|
727
|
-
clearInterval(interval);
|
|
728
|
-
render("\x1B[32m\u2714\x1B[0m");
|
|
729
|
-
process.stdout.write("\n");
|
|
730
|
-
return result;
|
|
731
|
-
} catch (error) {
|
|
732
|
-
clearInterval(interval);
|
|
733
|
-
render("\x1B[31m\u2716\x1B[0m");
|
|
734
|
-
process.stdout.write("\n");
|
|
735
|
-
throw error;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
847
|
function getSelectedVariantUnitPriceCents(selectedVariants) {
|
|
739
848
|
const highestVariantPrice = selectedVariants?.reduce((highest, variant) => {
|
|
740
849
|
const variantPriceCents = typeof variant.price === "number" ? Math.round(variant.price * 100) : 0;
|
|
@@ -748,12 +857,14 @@ async function ensureSufficientBalance(requiredCents, wallet) {
|
|
|
748
857
|
if (available >= required) {
|
|
749
858
|
return wallet;
|
|
750
859
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
` Tempo wallet balance is too low for this order: have ${formatUsdcNumber(available)} USDC, need ${formatUsdAmount(requiredCents)} USDC.`
|
|
860
|
+
section("Balance Check");
|
|
861
|
+
line(
|
|
862
|
+
`Wallet balance too low: have ${c.bold}${formatUsdcNumber(available)} USDC${c.reset}, need ${c.bold}${formatUsdAmount(requiredCents)} USDC${c.reset}`
|
|
755
863
|
);
|
|
756
|
-
|
|
864
|
+
kv("Shortfall", `${formatUsdcNumber(required - available)} USDC`, {
|
|
865
|
+
color: c.yellow
|
|
866
|
+
});
|
|
867
|
+
gap();
|
|
757
868
|
const fundNow = await promptConfirm("Open Tempo funding flow now?");
|
|
758
869
|
if (!fundNow) {
|
|
759
870
|
throw new Error(
|
|
@@ -768,13 +879,13 @@ async function ensureSufficientBalance(requiredCents, wallet) {
|
|
|
768
879
|
const refreshed = await ensureTempoReady();
|
|
769
880
|
available = parseTempoBalance(refreshed.balance?.available);
|
|
770
881
|
if (available >= required) {
|
|
771
|
-
|
|
772
|
-
|
|
882
|
+
line(
|
|
883
|
+
`${c.green}\u2714${c.reset} Funding complete \u2014 ${c.bold}${formatUsdcNumber(available)} ${refreshed.balance?.symbol ?? "USDC"}${c.reset}`
|
|
773
884
|
);
|
|
774
885
|
return refreshed;
|
|
775
886
|
}
|
|
776
887
|
if (attempt < 5) {
|
|
777
|
-
|
|
888
|
+
muted("Waiting for updated wallet balance...");
|
|
778
889
|
await sleep(3e3);
|
|
779
890
|
}
|
|
780
891
|
}
|
|
@@ -814,27 +925,28 @@ async function createPurchaseWithTempo(body) {
|
|
|
814
925
|
try {
|
|
815
926
|
return JSON.parse(result.stdout);
|
|
816
927
|
} catch {
|
|
817
|
-
throw new Error(
|
|
928
|
+
throw new Error(
|
|
929
|
+
`Unexpected response from Tempo request: ${result.stdout.trim() || "(empty)"}`
|
|
930
|
+
);
|
|
818
931
|
}
|
|
819
932
|
}
|
|
820
933
|
async function buy(urlArg) {
|
|
821
934
|
try {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
console.log();
|
|
935
|
+
banner();
|
|
936
|
+
gap();
|
|
825
937
|
const url = urlArg ?? await promptProductUrl();
|
|
826
938
|
const client = new AgentMall();
|
|
827
|
-
const product = await
|
|
828
|
-
"Looking up product
|
|
939
|
+
const product = await spin(
|
|
940
|
+
"Looking up product",
|
|
829
941
|
() => client.products.lookup(url)
|
|
830
942
|
);
|
|
831
943
|
displayProduct(product);
|
|
832
944
|
const quantity = await promptQuantity();
|
|
833
945
|
const selectedVariants = product.variants?.length ? await promptVariants(product.variants) : void 0;
|
|
834
|
-
const variantSelections = selectedVariants?.map(
|
|
835
|
-
|
|
836
|
-
selectedVariants
|
|
946
|
+
const variantSelections = selectedVariants?.map(
|
|
947
|
+
(variant) => variant.selection
|
|
837
948
|
);
|
|
949
|
+
const selectedVariantUnitPriceCents = getSelectedVariantUnitPriceCents(selectedVariants);
|
|
838
950
|
const suggestedMaxBudget = calculateSuggestedBudget(
|
|
839
951
|
product,
|
|
840
952
|
quantity,
|
|
@@ -851,6 +963,7 @@ async function buy(urlArg) {
|
|
|
851
963
|
variantSelections,
|
|
852
964
|
selectedVariantUnitPriceCents
|
|
853
965
|
);
|
|
966
|
+
gap();
|
|
854
967
|
const address = await promptAddress();
|
|
855
968
|
const maxBudget = await promptBudget(suggestedMaxBudget, minimumBudget);
|
|
856
969
|
const { input: input2 } = await import("@inquirer/prompts");
|
|
@@ -859,66 +972,82 @@ async function buy(urlArg) {
|
|
|
859
972
|
validate: (value) => EMAIL_PATTERN.test(value.trim()) ? true : "A valid email is required"
|
|
860
973
|
});
|
|
861
974
|
const body = {
|
|
862
|
-
items: [
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
975
|
+
items: [
|
|
976
|
+
{
|
|
977
|
+
product_url: url,
|
|
978
|
+
quantity,
|
|
979
|
+
...variantSelections?.length ? { variant: variantSelections } : {}
|
|
980
|
+
}
|
|
981
|
+
],
|
|
867
982
|
delivery_address: address,
|
|
868
983
|
max_budget: maxBudget,
|
|
869
984
|
buyer_email: buyerEmail
|
|
870
985
|
};
|
|
871
986
|
displayOrderSummary(body);
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
987
|
+
gap();
|
|
988
|
+
kv("Budget cap", `${c.bold}${formatCents(maxBudget)}${c.reset}`);
|
|
989
|
+
kv("Fee", formatCents(SERVICE_FEE_CENTS), { dim: true });
|
|
990
|
+
kv(
|
|
991
|
+
"Total charge",
|
|
992
|
+
`${c.bold}${formatCents(maxBudget + SERVICE_FEE_CENTS)}${c.reset} USDC on Tempo`
|
|
993
|
+
);
|
|
994
|
+
gap();
|
|
995
|
+
muted(
|
|
996
|
+
"Unused budget is refunded automatically after checkout."
|
|
997
|
+
);
|
|
998
|
+
muted(
|
|
999
|
+
"The buffer covers unforeseen tax, shipping, and retailer price changes."
|
|
1000
|
+
);
|
|
1001
|
+
gap();
|
|
878
1002
|
const proceed = await promptConfirm("Place order?");
|
|
879
1003
|
if (!proceed) {
|
|
880
|
-
|
|
1004
|
+
muted("Cancelled.");
|
|
881
1005
|
return;
|
|
882
1006
|
}
|
|
883
1007
|
const totalChargeCents = maxBudget + SERVICE_FEE_CENTS;
|
|
884
|
-
const wallet = await
|
|
885
|
-
|
|
886
|
-
await ensureTempoReady()
|
|
887
|
-
);
|
|
1008
|
+
const wallet = await spin("Connecting wallet", () => ensureTempoReady());
|
|
1009
|
+
await ensureSufficientBalance(totalChargeCents, wallet);
|
|
888
1010
|
if (wallet.wallet) {
|
|
889
|
-
const
|
|
890
|
-
|
|
1011
|
+
const addr = wallet.wallet;
|
|
1012
|
+
const short = `${addr.slice(0, 6)}\u2026${addr.slice(-4)}`;
|
|
1013
|
+
const bal = wallet.balance?.available ? ` \xB7 ${wallet.balance.available} ${wallet.balance?.symbol ?? "USDC"}` : "";
|
|
1014
|
+
muted(`Wallet ${short}${bal}`);
|
|
891
1015
|
}
|
|
892
|
-
|
|
893
|
-
|
|
1016
|
+
const order = await spin(
|
|
1017
|
+
"Placing order",
|
|
1018
|
+
() => createPurchaseWithTempo(body)
|
|
1019
|
+
);
|
|
894
1020
|
if (order.buyerToken) {
|
|
895
1021
|
await saveBuyerToken(order.id, order.buyerToken);
|
|
896
1022
|
}
|
|
897
1023
|
displayOrderResult(order);
|
|
898
1024
|
} catch (error) {
|
|
899
|
-
|
|
1025
|
+
gap();
|
|
900
1026
|
if (error instanceof ValidationError) {
|
|
901
|
-
|
|
1027
|
+
errorMsg("Validation error", error.message);
|
|
902
1028
|
process.exitCode = 1;
|
|
903
1029
|
return;
|
|
904
1030
|
}
|
|
905
1031
|
if (error instanceof ConflictError) {
|
|
906
|
-
|
|
1032
|
+
errorMsg("Conflict", error.message);
|
|
907
1033
|
process.exitCode = 1;
|
|
908
1034
|
return;
|
|
909
1035
|
}
|
|
910
1036
|
if (error instanceof RateLimitError) {
|
|
911
|
-
|
|
1037
|
+
errorMsg(
|
|
1038
|
+
"Rate limited",
|
|
1039
|
+
`Retry in about ${Math.ceil(error.retryAfterMs / 1e3)}s`
|
|
1040
|
+
);
|
|
912
1041
|
process.exitCode = 1;
|
|
913
1042
|
return;
|
|
914
1043
|
}
|
|
915
1044
|
if (error instanceof AgentMallError) {
|
|
916
|
-
|
|
1045
|
+
errorMsg("Request failed", error.message);
|
|
917
1046
|
process.exitCode = 1;
|
|
918
1047
|
return;
|
|
919
1048
|
}
|
|
920
1049
|
if (error instanceof Error) {
|
|
921
|
-
|
|
1050
|
+
errorMsg("Error", error.message);
|
|
922
1051
|
process.exitCode = 1;
|
|
923
1052
|
return;
|
|
924
1053
|
}
|
|
@@ -926,51 +1055,79 @@ async function buy(urlArg) {
|
|
|
926
1055
|
}
|
|
927
1056
|
}
|
|
928
1057
|
async function status(purchaseId) {
|
|
1058
|
+
banner();
|
|
929
1059
|
const apiSecret = process.env.AGENTMALL_API_SECRET;
|
|
930
1060
|
const buyerToken = apiSecret ? null : await getBuyerToken(purchaseId);
|
|
931
1061
|
const client = apiSecret ? new AgentMall({ apiSecret }) : new AgentMall();
|
|
932
1062
|
if (!apiSecret && !buyerToken) {
|
|
933
|
-
throw new Error(
|
|
1063
|
+
throw new Error(
|
|
1064
|
+
"No saved buyer token found for this order on this machine."
|
|
1065
|
+
);
|
|
934
1066
|
}
|
|
935
|
-
const result = await
|
|
936
|
-
|
|
937
|
-
buyerToken ? { buyerToken } : void 0
|
|
1067
|
+
const result = await spin(
|
|
1068
|
+
"Fetching order",
|
|
1069
|
+
() => client.purchases.get(purchaseId, buyerToken ? { buyerToken } : void 0)
|
|
938
1070
|
);
|
|
939
1071
|
displayStatus(result);
|
|
940
1072
|
}
|
|
941
1073
|
async function refund(purchaseId) {
|
|
1074
|
+
banner();
|
|
942
1075
|
const apiSecret = process.env.AGENTMALL_API_SECRET;
|
|
943
1076
|
const buyerToken = apiSecret ? null : await getBuyerToken(purchaseId);
|
|
944
1077
|
const client = apiSecret ? new AgentMall({ apiSecret }) : new AgentMall();
|
|
945
1078
|
if (!apiSecret && !buyerToken) {
|
|
946
|
-
throw new Error(
|
|
1079
|
+
throw new Error(
|
|
1080
|
+
"No saved buyer token found for this order on this machine."
|
|
1081
|
+
);
|
|
947
1082
|
}
|
|
948
|
-
const result = await
|
|
949
|
-
|
|
950
|
-
|
|
1083
|
+
const result = await spin(
|
|
1084
|
+
"Fetching refund",
|
|
1085
|
+
() => client.refunds.getByPurchase(
|
|
1086
|
+
purchaseId,
|
|
1087
|
+
buyerToken ? { buyerToken } : void 0
|
|
1088
|
+
)
|
|
951
1089
|
);
|
|
952
1090
|
displayRefund(result);
|
|
953
1091
|
}
|
|
954
1092
|
async function onboard() {
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1093
|
+
banner();
|
|
1094
|
+
gap();
|
|
1095
|
+
const wallet = await spin(
|
|
1096
|
+
"Checking Tempo wallet",
|
|
1097
|
+
() => ensureTempoReady()
|
|
1098
|
+
);
|
|
1099
|
+
section("Wallet");
|
|
960
1100
|
if (wallet.wallet) {
|
|
961
|
-
|
|
1101
|
+
kv("Address", wallet.wallet);
|
|
962
1102
|
}
|
|
963
1103
|
if (wallet.balance?.available) {
|
|
964
|
-
|
|
1104
|
+
kv(
|
|
1105
|
+
"Balance",
|
|
1106
|
+
`${wallet.balance.available} ${wallet.balance?.symbol ?? "USDC"}`,
|
|
1107
|
+
{ color: c.green }
|
|
1108
|
+
);
|
|
965
1109
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1110
|
+
section("Next Steps");
|
|
1111
|
+
line(
|
|
1112
|
+
`${c.dim}1.${c.reset} Run ${c.bold}tempo wallet whoami${c.reset} to review your wallet`
|
|
1113
|
+
);
|
|
1114
|
+
line(
|
|
1115
|
+
`${c.dim}2.${c.reset} Add funds to your Tempo wallet if needed`
|
|
1116
|
+
);
|
|
1117
|
+
line(
|
|
1118
|
+
`${c.dim}3.${c.reset} Buy something: ${c.bold}npx agentmall buy <url>${c.reset}`
|
|
1119
|
+
);
|
|
1120
|
+
gap();
|
|
971
1121
|
}
|
|
972
1122
|
|
|
973
1123
|
// src/cli.ts
|
|
1124
|
+
var c2 = {
|
|
1125
|
+
reset: "\x1B[0m",
|
|
1126
|
+
bold: "\x1B[1m",
|
|
1127
|
+
dim: "\x1B[2m",
|
|
1128
|
+
cyan: "\x1B[36m",
|
|
1129
|
+
gray: "\x1B[90m"
|
|
1130
|
+
};
|
|
974
1131
|
var args = process.argv.slice(2);
|
|
975
1132
|
var command = args[0];
|
|
976
1133
|
async function main() {
|
|
@@ -1009,7 +1166,7 @@ async function main() {
|
|
|
1009
1166
|
} catch (err) {
|
|
1010
1167
|
if (err instanceof Error) {
|
|
1011
1168
|
console.error(`
|
|
1012
|
-
\
|
|
1169
|
+
${c2.dim}\u2716${c2.reset} ${err.message}
|
|
1013
1170
|
`);
|
|
1014
1171
|
} else {
|
|
1015
1172
|
console.error(err);
|
|
@@ -1018,16 +1175,42 @@ async function main() {
|
|
|
1018
1175
|
}
|
|
1019
1176
|
}
|
|
1020
1177
|
function printHelp() {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
`);
|
|
1178
|
+
const W = 48;
|
|
1179
|
+
const rule = (title) => {
|
|
1180
|
+
const pad = Math.max(2, W - title.length - 2);
|
|
1181
|
+
return `${c2.dim}\u2500\u2500\u2500${c2.reset} ${c2.bold}${title}${c2.reset} ${c2.dim}${"\u2500".repeat(pad)}${c2.reset}`;
|
|
1182
|
+
};
|
|
1183
|
+
console.log();
|
|
1184
|
+
console.log(
|
|
1185
|
+
` ${c2.bold}\u25CE agentmall${c2.reset} ${c2.dim}\u2014 buy anything with a URL${c2.reset}`
|
|
1186
|
+
);
|
|
1187
|
+
console.log();
|
|
1188
|
+
console.log(` ${rule("Commands")}`);
|
|
1189
|
+
console.log(
|
|
1190
|
+
` ${c2.cyan}agentmall${c2.reset} [url] ${c2.dim}Interactive purchase${c2.reset}`
|
|
1191
|
+
);
|
|
1192
|
+
console.log(
|
|
1193
|
+
` ${c2.cyan}agentmall buy${c2.reset} <url> ${c2.dim}Buy a product${c2.reset}`
|
|
1194
|
+
);
|
|
1195
|
+
console.log(
|
|
1196
|
+
` ${c2.cyan}agentmall status${c2.reset} <id> ${c2.dim}Check order status${c2.reset}`
|
|
1197
|
+
);
|
|
1198
|
+
console.log(
|
|
1199
|
+
` ${c2.cyan}agentmall refund${c2.reset} <id> ${c2.dim}Check refund status${c2.reset}`
|
|
1200
|
+
);
|
|
1201
|
+
console.log(
|
|
1202
|
+
` ${c2.cyan}agentmall onboard${c2.reset} ${c2.dim}Set up Tempo wallet${c2.reset}`
|
|
1203
|
+
);
|
|
1204
|
+
console.log(
|
|
1205
|
+
` ${c2.cyan}agentmall help${c2.reset} ${c2.dim}Show this help${c2.reset}`
|
|
1206
|
+
);
|
|
1207
|
+
console.log();
|
|
1208
|
+
console.log(` ${rule("Examples")}`);
|
|
1209
|
+
console.log(
|
|
1210
|
+
` ${c2.dim}$${c2.reset} npx agentmall https://amazon.com/dp/B0DWTMJHCG`
|
|
1211
|
+
);
|
|
1212
|
+
console.log(` ${c2.dim}$${c2.reset} npx agentmall status pur_abc123`);
|
|
1213
|
+
console.log(` ${c2.dim}$${c2.reset} npx agentmall refund pur_abc123`);
|
|
1214
|
+
console.log();
|
|
1032
1215
|
}
|
|
1033
1216
|
main();
|
package/dist/index.d.cts
CHANGED
|
@@ -65,7 +65,7 @@ type CreatePurchaseRequest = {
|
|
|
65
65
|
idempotency_key?: string;
|
|
66
66
|
metadata?: unknown;
|
|
67
67
|
};
|
|
68
|
-
type PurchaseLifecycleStatus = "payment_received" | "submitted" | "pending" | "
|
|
68
|
+
type PurchaseLifecycleStatus = "payment_received" | "submitted" | "pending" | "ordered" | "failed";
|
|
69
69
|
type CreatePurchaseResponse = {
|
|
70
70
|
id: string;
|
|
71
71
|
status: PurchaseLifecycleStatus;
|
package/dist/index.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ type CreatePurchaseRequest = {
|
|
|
65
65
|
idempotency_key?: string;
|
|
66
66
|
metadata?: unknown;
|
|
67
67
|
};
|
|
68
|
-
type PurchaseLifecycleStatus = "payment_received" | "submitted" | "pending" | "
|
|
68
|
+
type PurchaseLifecycleStatus = "payment_received" | "submitted" | "pending" | "ordered" | "failed";
|
|
69
69
|
type CreatePurchaseResponse = {
|
|
70
70
|
id: string;
|
|
71
71
|
status: PurchaseLifecycleStatus;
|