agentmall 0.1.8 → 0.1.9
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 +106 -44
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -288,35 +288,32 @@ function formatCents(cents) {
|
|
|
288
288
|
function getBudgetBasePrice(product) {
|
|
289
289
|
return product.listPrice ?? product.price;
|
|
290
290
|
}
|
|
291
|
-
function calculateSuggestedBudget(product, quantity = 1) {
|
|
291
|
+
function calculateSuggestedBudget(product, quantity = 1, variantUnitPriceCents) {
|
|
292
292
|
const quantityClamped = Math.max(1, quantity);
|
|
293
|
-
const
|
|
293
|
+
const baseUnitPriceCents = Math.max(
|
|
294
|
+
getBudgetBasePrice(product),
|
|
295
|
+
variantUnitPriceCents ?? 0
|
|
296
|
+
);
|
|
297
|
+
const basePriceCents = baseUnitPriceCents * quantityClamped;
|
|
294
298
|
const variableBufferCents = Math.round(basePriceCents * 0.15);
|
|
295
299
|
return basePriceCents + Math.max(variableBufferCents, 800);
|
|
296
300
|
}
|
|
297
|
-
function calculateMinimumBudget(product, quantity = 1) {
|
|
298
|
-
|
|
301
|
+
function calculateMinimumBudget(product, quantity = 1, variantUnitPriceCents) {
|
|
302
|
+
const unitPriceCents = Math.max(product.price, variantUnitPriceCents ?? 0);
|
|
303
|
+
return unitPriceCents * Math.max(1, quantity);
|
|
299
304
|
}
|
|
300
|
-
function displayProduct(product
|
|
301
|
-
const quantityClamped = Math.max(1, quantity);
|
|
302
|
-
const suggestedBudget = calculateSuggestedBudget(product, quantityClamped);
|
|
303
|
-
const linePrice = product.price * quantityClamped;
|
|
304
|
-
const lineListPrice = typeof product.listPrice === "number" ? product.listPrice * quantityClamped : void 0;
|
|
305
|
+
function displayProduct(product) {
|
|
305
306
|
console.log();
|
|
306
307
|
console.log(" \x1B[1m\u{1F4E6} Product\x1B[0m");
|
|
307
308
|
console.log(` \x1B[1m${product.title}\x1B[0m`);
|
|
308
309
|
console.log(` ${product.retailer} \u2014 ${product.availability}`);
|
|
309
310
|
console.log();
|
|
310
|
-
if (
|
|
311
|
+
if (product.listPrice && product.discountPercent) {
|
|
311
312
|
console.log(
|
|
312
|
-
` Price: \x1B[32m${formatCents(
|
|
313
|
+
` Price: \x1B[32m${formatCents(product.price)}\x1B[0m \x1B[2m\x1B[9m${formatCents(product.listPrice)}\x1B[0m \x1B[33m-${product.discountPercent}%\x1B[0m`
|
|
313
314
|
);
|
|
314
315
|
} else {
|
|
315
|
-
console.log(` Price: \x1B[32m${formatCents(
|
|
316
|
-
}
|
|
317
|
-
if (quantityClamped > 1) {
|
|
318
|
-
console.log(` Quantity: ${quantityClamped}`);
|
|
319
|
-
console.log(` Unit price: ${formatCents(product.price)}`);
|
|
316
|
+
console.log(` Price: \x1B[32m${formatCents(product.price)}\x1B[0m`);
|
|
320
317
|
}
|
|
321
318
|
if (product.variants?.length) {
|
|
322
319
|
console.log();
|
|
@@ -326,7 +323,36 @@ function displayProduct(product, quantity = 1) {
|
|
|
326
323
|
}
|
|
327
324
|
}
|
|
328
325
|
console.log();
|
|
326
|
+
}
|
|
327
|
+
function displayCheckoutEstimate(product, quantity = 1, variantSelections, variantUnitPriceCents) {
|
|
328
|
+
const quantityClamped = Math.max(1, quantity);
|
|
329
|
+
const unitPriceCents = Math.max(product.price, variantUnitPriceCents ?? 0);
|
|
330
|
+
const suggestedBudget = calculateSuggestedBudget(
|
|
331
|
+
product,
|
|
332
|
+
quantityClamped,
|
|
333
|
+
variantUnitPriceCents
|
|
334
|
+
);
|
|
335
|
+
const linePrice = unitPriceCents * quantityClamped;
|
|
336
|
+
const listBaseUnitPrice = typeof product.listPrice === "number" ? Math.max(product.listPrice, variantUnitPriceCents ?? 0) : void 0;
|
|
337
|
+
const lineListPrice = typeof listBaseUnitPrice === "number" ? listBaseUnitPrice * quantityClamped : void 0;
|
|
338
|
+
console.log();
|
|
329
339
|
console.log(" \x1B[1m\u{1F4B8} Checkout Estimate\x1B[0m");
|
|
340
|
+
if (lineListPrice && product.discountPercent) {
|
|
341
|
+
console.log(
|
|
342
|
+
` Price: \x1B[32m${formatCents(linePrice)}\x1B[0m \x1B[2m\x1B[9m${formatCents(lineListPrice)}\x1B[0m \x1B[33m-${product.discountPercent}%\x1B[0m`
|
|
343
|
+
);
|
|
344
|
+
} else {
|
|
345
|
+
console.log(` Price: \x1B[32m${formatCents(linePrice)}\x1B[0m`);
|
|
346
|
+
}
|
|
347
|
+
console.log(` Quantity: ${quantityClamped}`);
|
|
348
|
+
if (quantityClamped > 1) {
|
|
349
|
+
console.log(` Unit price: ${formatCents(unitPriceCents)}`);
|
|
350
|
+
}
|
|
351
|
+
if (variantSelections?.length) {
|
|
352
|
+
for (const variant of variantSelections) {
|
|
353
|
+
console.log(` ${variant.label}: ${variant.value}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
330
356
|
console.log(
|
|
331
357
|
` Suggested budget: \x1B[1m${formatCents(suggestedBudget)}\x1B[0m (includes 15% buffer for tax and an $8 minimum shipping buffer)`
|
|
332
358
|
);
|
|
@@ -469,7 +495,12 @@ async function promptVariants(variants) {
|
|
|
469
495
|
value: o.value
|
|
470
496
|
}))
|
|
471
497
|
});
|
|
472
|
-
|
|
498
|
+
const selected = options.find((o) => o.value === value);
|
|
499
|
+
selections.push({
|
|
500
|
+
selection: { label, value },
|
|
501
|
+
...selected?.price !== void 0 ? { price: selected.price } : {},
|
|
502
|
+
...selected?.url ? { url: selected.url } : {}
|
|
503
|
+
});
|
|
473
504
|
}
|
|
474
505
|
return selections;
|
|
475
506
|
}
|
|
@@ -517,36 +548,33 @@ async function promptQuantity() {
|
|
|
517
548
|
async function promptConfirm(message) {
|
|
518
549
|
return confirm({ message });
|
|
519
550
|
}
|
|
520
|
-
function
|
|
521
|
-
return
|
|
551
|
+
function formatUsd(cents) {
|
|
552
|
+
return (cents / 100).toFixed(2);
|
|
522
553
|
}
|
|
523
|
-
function
|
|
524
|
-
const trimmed = value.trim();
|
|
554
|
+
function parseBudgetUsdInput(value) {
|
|
555
|
+
const trimmed = value.trim().replace(/[$,\s]/g, "");
|
|
525
556
|
if (!trimmed) return null;
|
|
526
|
-
if (/^\d
|
|
557
|
+
if (/^\d+(\.\d{0,2})?$/.test(trimmed)) {
|
|
527
558
|
return Math.round(Number(trimmed) * 100);
|
|
528
559
|
}
|
|
529
|
-
if (/^\d+$/.test(trimmed)) {
|
|
530
|
-
return parseInt(trimmed, 10);
|
|
531
|
-
}
|
|
532
560
|
return null;
|
|
533
561
|
}
|
|
534
562
|
async function promptBudget(suggested, minimumCents) {
|
|
535
563
|
const value = await input({
|
|
536
|
-
message: `Max budget in
|
|
537
|
-
default:
|
|
564
|
+
message: `Max budget in USD`,
|
|
565
|
+
default: formatUsd(suggested),
|
|
538
566
|
validate: (v) => {
|
|
539
|
-
const n =
|
|
567
|
+
const n = parseBudgetUsdInput(v);
|
|
540
568
|
if (n === null || n <= 0) {
|
|
541
|
-
return "Enter
|
|
569
|
+
return "Enter a USD amount like 48 or 48.17";
|
|
542
570
|
}
|
|
543
571
|
if (n < minimumCents) {
|
|
544
|
-
return `Max budget must be at least
|
|
572
|
+
return `Max budget must be at least $${formatUsd(minimumCents)}`;
|
|
545
573
|
}
|
|
546
574
|
return true;
|
|
547
575
|
}
|
|
548
576
|
});
|
|
549
|
-
const parsed =
|
|
577
|
+
const parsed = parseBudgetUsdInput(value);
|
|
550
578
|
if (parsed === null) {
|
|
551
579
|
throw new Error("Invalid budget input");
|
|
552
580
|
}
|
|
@@ -682,6 +710,16 @@ async function ensureTempoReady() {
|
|
|
682
710
|
function formatUsdAmount(cents) {
|
|
683
711
|
return (cents / 100).toFixed(2);
|
|
684
712
|
}
|
|
713
|
+
function sleep(ms) {
|
|
714
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
715
|
+
}
|
|
716
|
+
function getSelectedVariantUnitPriceCents(selectedVariants) {
|
|
717
|
+
const highestVariantPrice = selectedVariants?.reduce((highest, variant) => {
|
|
718
|
+
const variantPriceCents = typeof variant.price === "number" ? Math.round(variant.price * 100) : 0;
|
|
719
|
+
return Math.max(highest, variantPriceCents);
|
|
720
|
+
}, 0) ?? 0;
|
|
721
|
+
return highestVariantPrice > 0 ? highestVariantPrice : void 0;
|
|
722
|
+
}
|
|
685
723
|
async function ensureSufficientBalance(requiredCents, wallet) {
|
|
686
724
|
const required = Number.parseFloat(formatUsdAmount(requiredCents));
|
|
687
725
|
let available = parseTempoBalance(wallet.balance?.available);
|
|
@@ -704,17 +742,23 @@ async function ensureSufficientBalance(requiredCents, wallet) {
|
|
|
704
742
|
if (fundCode !== 0) {
|
|
705
743
|
throw new Error("Tempo wallet funding was cancelled.");
|
|
706
744
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
745
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
746
|
+
const refreshed = await ensureTempoReady();
|
|
747
|
+
available = parseTempoBalance(refreshed.balance?.available);
|
|
748
|
+
if (available >= required) {
|
|
749
|
+
console.log(
|
|
750
|
+
` Funding complete. New balance: ${formatUsdcNumber(available)} ${refreshed.balance?.symbol ?? "USDC"}`
|
|
751
|
+
);
|
|
752
|
+
return refreshed;
|
|
753
|
+
}
|
|
754
|
+
if (attempt < 5) {
|
|
755
|
+
console.log(" Waiting for updated wallet balance...");
|
|
756
|
+
await sleep(3e3);
|
|
757
|
+
}
|
|
713
758
|
}
|
|
714
|
-
|
|
715
|
-
`
|
|
759
|
+
throw new Error(
|
|
760
|
+
`Still not enough USDC after funding: have ${formatUsdcNumber(available)}, need ${formatUsdAmount(requiredCents)}.`
|
|
716
761
|
);
|
|
717
|
-
return refreshed;
|
|
718
762
|
}
|
|
719
763
|
async function createPurchaseWithTempo(body) {
|
|
720
764
|
const totalChargeCents = body.max_budget + SERVICE_FEE_CENTS;
|
|
@@ -760,11 +804,29 @@ async function buy(urlArg) {
|
|
|
760
804
|
const client = new AgentMall();
|
|
761
805
|
console.log(" Looking up product...");
|
|
762
806
|
const product = await client.products.lookup(url);
|
|
807
|
+
displayProduct(product);
|
|
763
808
|
const quantity = await promptQuantity();
|
|
764
|
-
const
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
809
|
+
const selectedVariants = product.variants?.length ? await promptVariants(product.variants) : void 0;
|
|
810
|
+
const variantSelections = selectedVariants?.map((variant) => variant.selection);
|
|
811
|
+
const selectedVariantUnitPriceCents = getSelectedVariantUnitPriceCents(
|
|
812
|
+
selectedVariants
|
|
813
|
+
);
|
|
814
|
+
const suggestedMaxBudget = calculateSuggestedBudget(
|
|
815
|
+
product,
|
|
816
|
+
quantity,
|
|
817
|
+
selectedVariantUnitPriceCents
|
|
818
|
+
);
|
|
819
|
+
const minimumBudget = calculateMinimumBudget(
|
|
820
|
+
product,
|
|
821
|
+
quantity,
|
|
822
|
+
selectedVariantUnitPriceCents
|
|
823
|
+
);
|
|
824
|
+
displayCheckoutEstimate(
|
|
825
|
+
product,
|
|
826
|
+
quantity,
|
|
827
|
+
variantSelections,
|
|
828
|
+
selectedVariantUnitPriceCents
|
|
829
|
+
);
|
|
768
830
|
const address = await promptAddress();
|
|
769
831
|
const maxBudget = await promptBudget(suggestedMaxBudget, minimumBudget);
|
|
770
832
|
const { input: input2 } = await import("@inquirer/prompts");
|