agentmall 0.1.6 → 0.1.8

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.
Files changed (2) hide show
  1. package/dist/cli.js +79 -18
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -303,6 +303,7 @@ function displayProduct(product, quantity = 1) {
303
303
  const linePrice = product.price * quantityClamped;
304
304
  const lineListPrice = typeof product.listPrice === "number" ? product.listPrice * quantityClamped : void 0;
305
305
  console.log();
306
+ console.log(" \x1B[1m\u{1F4E6} Product\x1B[0m");
306
307
  console.log(` \x1B[1m${product.title}\x1B[0m`);
307
308
  console.log(` ${product.retailer} \u2014 ${product.availability}`);
308
309
  console.log();
@@ -325,6 +326,7 @@ function displayProduct(product, quantity = 1) {
325
326
  }
326
327
  }
327
328
  console.log();
329
+ console.log(" \x1B[1m\u{1F4B8} Checkout Estimate\x1B[0m");
328
330
  console.log(
329
331
  ` Suggested budget: \x1B[1m${formatCents(suggestedBudget)}\x1B[0m (includes 15% buffer for tax and an $8 minimum shipping buffer)`
330
332
  );
@@ -347,7 +349,7 @@ function displayProduct(product, quantity = 1) {
347
349
  }
348
350
  function displayOrderSummary(body) {
349
351
  console.log();
350
- console.log(" \x1B[1mOrder Summary\x1B[0m");
352
+ console.log(" \x1B[1m\u{1F9FE} Order Summary\x1B[0m");
351
353
  for (const item of body.items) {
352
354
  const url = item.product_url;
353
355
  const qty = item.quantity ?? 1;
@@ -360,6 +362,7 @@ function displayOrderSummary(body) {
360
362
  }
361
363
  const addr = body.delivery_address;
362
364
  console.log();
365
+ console.log(" \x1B[1m\u{1F4CD} Shipping\x1B[0m");
363
366
  console.log(` Ship to: ${addr.first_name} ${addr.last_name}`);
364
367
  console.log(` ${addr.address_line1}${addr.address_line2 ? `, ${addr.address_line2}` : ""}`);
365
368
  console.log(` ${addr.city}, ${addr.state ?? ""} ${addr.postal_code} ${addr.country}`);
@@ -370,16 +373,16 @@ function displayOrderSummary(body) {
370
373
  function displayOrderResult(order) {
371
374
  console.log();
372
375
  if (order.status === "failed") {
373
- console.log(` \x1B[31m\u2717\x1B[0m Order failed during submission.`);
376
+ console.log(` \x1B[31m\u274C Order failed during submission.\x1B[0m`);
374
377
  } else if (order.deduplicated) {
375
- console.log(` \x1B[33m\u21BA\x1B[0m Reused existing order.`);
378
+ console.log(` \x1B[33m\u267B\uFE0F Reused existing order.\x1B[0m`);
376
379
  } else {
377
- console.log(` \x1B[32m\u2713\x1B[0m Order placed!`);
380
+ console.log(` \x1B[32m\u2705 Order placed!\x1B[0m`);
378
381
  }
379
382
  console.log(` ID: \x1B[1m${order.id}\x1B[0m`);
380
383
  console.log(` Status: ${order.status}`);
381
384
  if (order.buyerToken) {
382
- console.log(" Saved local access token for status and refund checks.");
385
+ console.log(" \u{1F510} Saved local access token for status and refund checks.");
383
386
  }
384
387
  console.log();
385
388
  console.log(
@@ -390,7 +393,7 @@ function displayOrderResult(order) {
390
393
  }
391
394
  function displayStatus(purchase) {
392
395
  console.log();
393
- console.log(` \x1B[1mOrder ${purchase.id}\x1B[0m`);
396
+ console.log(` \x1B[1m\u{1F4E6} Order ${purchase.id}\x1B[0m`);
394
397
  console.log(` Status: ${purchase.status}`);
395
398
  if (purchase.items?.length) {
396
399
  for (const item of purchase.items) {
@@ -414,7 +417,7 @@ function displayStatus(purchase) {
414
417
  }
415
418
  function displayRefund(refund2) {
416
419
  console.log();
417
- console.log(` \x1B[1mRefund ${refund2.id}\x1B[0m`);
420
+ console.log(` \x1B[1m\u{1F4B8} Refund ${refund2.id}\x1B[0m`);
418
421
  console.log(` Status: ${refund2.status}`);
419
422
  console.log(` Amount: ${formatCents(refund2.amountCents)}`);
420
423
  console.log(` Reason: ${refund2.reason}`);
@@ -596,6 +599,13 @@ async function getBuyerToken(purchaseId) {
596
599
 
597
600
  // src/cli/index.ts
598
601
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
602
+ function parseTempoBalance(balance) {
603
+ const parsed = Number.parseFloat(balance ?? "0");
604
+ return Number.isFinite(parsed) ? parsed : 0;
605
+ }
606
+ function formatUsdcNumber(amount) {
607
+ return amount.toFixed(6).replace(/\.?0+$/, "");
608
+ }
599
609
  async function runCommandCapture(command2, args2, envOverrides) {
600
610
  return await new Promise((resolve, reject) => {
601
611
  const child = spawn(command2, args2, {
@@ -672,6 +682,40 @@ async function ensureTempoReady() {
672
682
  function formatUsdAmount(cents) {
673
683
  return (cents / 100).toFixed(2);
674
684
  }
685
+ async function ensureSufficientBalance(requiredCents, wallet) {
686
+ const required = Number.parseFloat(formatUsdAmount(requiredCents));
687
+ let available = parseTempoBalance(wallet.balance?.available);
688
+ if (available >= required) {
689
+ return wallet;
690
+ }
691
+ console.log();
692
+ console.log(" \x1B[1m\u{1F4B3} Balance Check\x1B[0m");
693
+ console.log(
694
+ ` Tempo wallet balance is too low for this order: have ${formatUsdcNumber(available)} USDC, need ${formatUsdAmount(requiredCents)} USDC.`
695
+ );
696
+ console.log(` Shortfall: ${formatUsdcNumber(required - available)} USDC`);
697
+ const fundNow = await promptConfirm("Open Tempo funding flow now?");
698
+ if (!fundNow) {
699
+ throw new Error(
700
+ `Insufficient USDC balance: have ${formatUsdcNumber(available)}, need ${formatUsdAmount(requiredCents)}.`
701
+ );
702
+ }
703
+ const fundCode = await runInteractiveCommand("tempo", ["wallet", "fund"]);
704
+ if (fundCode !== 0) {
705
+ throw new Error("Tempo wallet funding was cancelled.");
706
+ }
707
+ const refreshed = await ensureTempoReady();
708
+ available = parseTempoBalance(refreshed.balance?.available);
709
+ if (available < required) {
710
+ throw new Error(
711
+ `Still not enough USDC after funding: have ${formatUsdcNumber(available)}, need ${formatUsdAmount(requiredCents)}.`
712
+ );
713
+ }
714
+ console.log(
715
+ ` Funding complete. New balance: ${formatUsdcNumber(available)} ${refreshed.balance?.symbol ?? "USDC"}`
716
+ );
717
+ return refreshed;
718
+ }
675
719
  async function createPurchaseWithTempo(body) {
676
720
  const totalChargeCents = body.max_budget + SERVICE_FEE_CENTS;
677
721
  const result = await runCommandCapture(
@@ -690,7 +734,15 @@ async function createPurchaseWithTempo(body) {
690
734
  }
691
735
  );
692
736
  if (result.code !== 0) {
693
- const message = result.stderr.trim() || result.stdout.trim() || "Tempo request failed";
737
+ const raw = result.stderr.trim() || result.stdout.trim();
738
+ const payload = raw ? (() => {
739
+ try {
740
+ return JSON.parse(raw);
741
+ } catch {
742
+ return null;
743
+ }
744
+ })() : null;
745
+ const message = (payload?.message ?? raw) || "Tempo request failed";
694
746
  throw new Error(message);
695
747
  }
696
748
  try {
@@ -702,7 +754,7 @@ async function createPurchaseWithTempo(body) {
702
754
  async function buy(urlArg) {
703
755
  try {
704
756
  console.log();
705
- console.log(" \x1B[1magentmall\x1B[0m \u2014 Buy anything with a URL");
757
+ console.log(" \x1B[1m\u{1F6D2} agentmall\x1B[0m \u2014 Buy anything with a URL");
706
758
  console.log();
707
759
  const url = urlArg ?? await promptProductUrl();
708
760
  const client = new AgentMall();
@@ -740,7 +792,11 @@ async function buy(urlArg) {
740
792
  console.log(" Cancelled.");
741
793
  return;
742
794
  }
743
- const wallet = await ensureTempoReady();
795
+ const totalChargeCents = maxBudget + SERVICE_FEE_CENTS;
796
+ const wallet = await ensureSufficientBalance(
797
+ totalChargeCents,
798
+ await ensureTempoReady()
799
+ );
744
800
  if (wallet.wallet) {
745
801
  const balance = wallet.balance?.available ? `, balance ${wallet.balance.available} ${wallet.balance?.symbol ?? "USDC"}` : "";
746
802
  console.log(` Paying with Tempo wallet ${wallet.wallet}${balance}`);
@@ -773,6 +829,11 @@ async function buy(urlArg) {
773
829
  process.exitCode = 1;
774
830
  return;
775
831
  }
832
+ if (error instanceof Error) {
833
+ console.error(` \x1B[31mError:\x1B[0m ${error.message}`);
834
+ process.exitCode = 1;
835
+ return;
836
+ }
776
837
  throw error;
777
838
  }
778
839
  }
@@ -804,7 +865,7 @@ async function refund(purchaseId) {
804
865
  }
805
866
  async function onboard() {
806
867
  console.log();
807
- console.log(" \x1B[1magentmall onboarding\x1B[0m");
868
+ console.log(" \x1B[1m\u{1F511} agentmall onboarding\x1B[0m");
808
869
  console.log();
809
870
  const wallet = await ensureTempoReady();
810
871
  console.log();
@@ -870,15 +931,15 @@ async function main() {
870
931
  }
871
932
  function printHelp() {
872
933
  console.log(`
873
- \x1B[1magentmall\x1B[0m \u2014 Buy anything with a URL
934
+ \x1B[1m\u{1F6D2} agentmall\x1B[0m \u2014 Buy anything with a URL
874
935
 
875
936
  Usage:
876
- agentmall onboard Log in to Tempo and show wallet funding steps
877
- agentmall [url] Buy a product (interactive)
878
- agentmall buy <url> Buy a product
879
- agentmall status <id> Check order status using the saved buyer token
880
- agentmall refund <id> Check refund status using the saved buyer token
881
- agentmall help Show this help
937
+ \u{1F511} agentmall onboard Log in to Tempo and show wallet funding steps
938
+ \u{1F6CD}\uFE0F agentmall [url] Buy a product (interactive)
939
+ \u{1F6CD}\uFE0F agentmall buy <url> Buy a product
940
+ \u{1F4E6} agentmall status <id> Check order status using the saved buyer token
941
+ \u{1F4B8} agentmall refund <id> Check refund status using the saved buyer token
942
+ \u2753 agentmall help Show this help
882
943
  `);
883
944
  }
884
945
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentmall",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "SDK and CLI for the AgentMall API — let AI agents buy physical products from US retailers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",