agentmall 0.1.2 → 0.1.4

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 CHANGED
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // src/cli/index.ts
4
+ import { spawn } from "child_process";
5
+
3
6
  // src/constants.ts
4
7
  var BASE_URL = "https://api.agentmall.sh";
5
8
  var SERVICE_FEE_CENTS = 150;
@@ -65,7 +68,6 @@ function createIdempotencyKey() {
65
68
  var AgentMall = class {
66
69
  baseUrl;
67
70
  apiSecret;
68
- account;
69
71
  _fetch;
70
72
  products;
71
73
  purchases;
@@ -74,7 +76,6 @@ var AgentMall = class {
74
76
  constructor(config = {}) {
75
77
  this.baseUrl = (config.baseUrl ?? BASE_URL).replace(/\/$/, "");
76
78
  this.apiSecret = config.apiSecret;
77
- this.account = config.account;
78
79
  this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);
79
80
  this.products = new AgentMallProducts(this);
80
81
  this.purchases = new AgentMallPurchases(this);
@@ -82,8 +83,8 @@ var AgentMall = class {
82
83
  this.refunds = new AgentMallRefunds(this);
83
84
  }
84
85
  /** @internal */
85
- getAccount() {
86
- return this.account;
86
+ hasApiSecret() {
87
+ return Boolean(this.apiSecret);
87
88
  }
88
89
  /** @internal */
89
90
  async request(method, path, options) {
@@ -165,28 +166,19 @@ var AgentMallPurchases = class {
165
166
  }
166
167
  });
167
168
  }
168
- /** Get a single purchase by ID using either operator auth or the payer wallet. */
169
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
169
170
  async get(id, options) {
170
- const account = options?.account ?? this.client.getAccount();
171
- if (account) {
172
- const challenge = await this.client.request(
173
- "POST",
174
- "/api/purchases/status/challenge",
175
- {
176
- body: { id }
177
- }
178
- );
179
- const signature = await account.signMessage({
180
- message: challenge.message
181
- });
171
+ if (options?.buyerToken) {
182
172
  return this.client.request("POST", "/api/purchases/status", {
183
173
  body: {
184
174
  id,
185
- challenge: challenge.challenge,
186
- signature
175
+ buyer_token: options.buyerToken
187
176
  }
188
177
  });
189
178
  }
179
+ if (!this.client.hasApiSecret()) {
180
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
181
+ }
190
182
  return this.client.request("GET", "/api/purchases", {
191
183
  auth: true,
192
184
  params: { id }
@@ -234,6 +226,33 @@ var AgentMallRefunds = class {
234
226
  params: { id }
235
227
  });
236
228
  }
229
+ /** Get refund status for a purchase using either operator auth or a buyer token. */
230
+ async getByPurchase(purchaseId, options) {
231
+ if (options?.buyerToken) {
232
+ return this.client.request("POST", "/api/refunds/status", {
233
+ body: {
234
+ purchase_id: purchaseId,
235
+ buyer_token: options.buyerToken
236
+ }
237
+ });
238
+ }
239
+ if (!this.client.hasApiSecret()) {
240
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
241
+ }
242
+ const result = await this.client.request(
243
+ "GET",
244
+ "/api/refunds",
245
+ {
246
+ auth: true,
247
+ params: { purchase_id: purchaseId }
248
+ }
249
+ );
250
+ const refund2 = result.refunds[0];
251
+ if (!refund2) {
252
+ throw new AgentMallError(404, { error: "Not found" });
253
+ }
254
+ return refund2;
255
+ }
237
256
  /** List refunds. Requires apiSecret. */
238
257
  async list(filters) {
239
258
  const result = await this.client.request(
@@ -245,31 +264,24 @@ var AgentMallRefunds = class {
245
264
  }
246
265
  };
247
266
 
248
- // src/purchase.ts
249
- async function purchase(config) {
250
- const { account, baseUrl, ...body } = config;
251
- let mppxClient;
252
- try {
253
- mppxClient = await import("mppx/client");
254
- } catch {
255
- throw new Error("mppx is required for purchases. Install it: npm install mppx");
256
- }
257
- const Mppx = mppxClient.Mppx;
258
- const tempo = mppxClient.tempo;
259
- if (!Mppx || !tempo) {
260
- throw new Error("mppx is required for purchases. Install it: npm install mppx");
261
- }
262
- const mppx = Mppx.create({
263
- methods: [tempo({ account })]
264
- });
265
- const client = new AgentMall({
266
- baseUrl: baseUrl ?? BASE_URL,
267
- fetch: mppx.fetch.bind(mppx)
268
- });
269
- return client.purchases.create(body);
270
- }
271
-
272
267
  // src/cli/display.ts
268
+ var FAILURE_MESSAGES = {
269
+ budget_exceeded: "Actual total exceeded your maximum budget.",
270
+ out_of_stock: "Item is out of stock.",
271
+ product_not_found: "Product is no longer available.",
272
+ product_unavailable: "Product cannot be purchased right now.",
273
+ variant_required: "A required size, color, or variant must be selected.",
274
+ variant_unavailable: "Selected variant is unavailable.",
275
+ quantity_unavailable: "Requested quantity is unavailable.",
276
+ quantity_limit: "Retailer quantity limit exceeded.",
277
+ invalid_url: "Product URL is invalid.",
278
+ invalid_address: "Shipping address could not be validated.",
279
+ shipping_unavailable: "Item cannot be shipped to this address.",
280
+ retailer_unavailable: "Retailer is temporarily unavailable.",
281
+ unsupported_retailer: "Retailer is not supported.",
282
+ validation_failed: "Order request failed validation.",
283
+ cancelled: "Order was cancelled."
284
+ };
273
285
  function formatCents(cents) {
274
286
  return `$${(cents / 100).toFixed(2)}`;
275
287
  }
@@ -294,7 +306,7 @@ function displayProduct(product) {
294
306
  }
295
307
  console.log();
296
308
  console.log(
297
- ` Suggested budget: \x1B[1m${formatCents(product.suggestedMaxBudget)}\x1B[0m (includes 15% buffer for tax & shipping)`
309
+ ` Suggested budget: \x1B[1m${formatCents(product.suggestedMaxBudget)}\x1B[0m (includes 15% buffer for tax and an $8 minimum shipping buffer)`
298
310
  );
299
311
  console.log(
300
312
  ` Service fee: ${formatCents(SERVICE_FEE_CENTS)}`
@@ -340,6 +352,9 @@ function displayOrderResult(order) {
340
352
  }
341
353
  console.log(` ID: \x1B[1m${order.id}\x1B[0m`);
342
354
  console.log(` Status: ${order.status}`);
355
+ if (order.buyerToken) {
356
+ console.log(" Saved local access token for status and refund checks.");
357
+ }
343
358
  console.log();
344
359
  console.log(
345
360
  order.status === "failed" ? " Check status for failure details and refund progress:" : " Check status in 5-10 minutes:"
@@ -347,26 +362,43 @@ function displayOrderResult(order) {
347
362
  console.log(` npx agentmall status ${order.id}`);
348
363
  console.log();
349
364
  }
350
- function displayStatus(purchase2) {
365
+ function displayStatus(purchase) {
351
366
  console.log();
352
- console.log(` \x1B[1mOrder ${purchase2.id}\x1B[0m`);
353
- console.log(` Status: ${purchase2.status}`);
354
- if (purchase2.items?.length) {
355
- for (const item of purchase2.items) {
367
+ console.log(` \x1B[1mOrder ${purchase.id}\x1B[0m`);
368
+ console.log(` Status: ${purchase.status}`);
369
+ if (purchase.items?.length) {
370
+ for (const item of purchase.items) {
356
371
  const price = item.price ? ` \u2014 ${formatCents(item.price)}` : "";
357
372
  console.log(` ${item.quantity}x ${item.title ?? item.productRef}${price}`);
358
373
  }
359
374
  }
360
- if (purchase2.finalTotal) {
361
- console.log(` Final total: ${formatCents(purchase2.finalTotal)}`);
375
+ if (purchase.finalTotal) {
376
+ console.log(` Final total: ${formatCents(purchase.finalTotal)}`);
362
377
  }
363
- if (purchase2.failureReason) {
364
- console.log(` \x1B[31mFailure: ${purchase2.failureReason}\x1B[0m`);
378
+ if (purchase.failureReason) {
379
+ console.log(
380
+ ` \x1B[31mFailure: ${FAILURE_MESSAGES[purchase.failureReason] ?? purchase.failureReason}\x1B[0m`
381
+ );
365
382
  }
366
- if (purchase2.deliveryMethod) {
367
- console.log(` Shipping: ${purchase2.deliveryMethod}`);
383
+ if (purchase.deliveryMethod) {
384
+ console.log(` Shipping: ${purchase.deliveryMethod}`);
385
+ }
386
+ console.log(` Created: ${new Date(purchase.createdAt).toLocaleString()}`);
387
+ console.log();
388
+ }
389
+ function displayRefund(refund2) {
390
+ console.log();
391
+ console.log(` \x1B[1mRefund ${refund2.id}\x1B[0m`);
392
+ console.log(` Status: ${refund2.status}`);
393
+ console.log(` Amount: ${formatCents(refund2.amountCents)}`);
394
+ console.log(` Reason: ${refund2.reason}`);
395
+ if (refund2.txHash) {
396
+ console.log(` TX: ${refund2.txHash}`);
397
+ }
398
+ console.log(` Created: ${new Date(refund2.createdAt).toLocaleString()}`);
399
+ if (refund2.processedAt) {
400
+ console.log(` Processed: ${new Date(refund2.processedAt).toLocaleString()}`);
368
401
  }
369
- console.log(` Created: ${new Date(purchase2.createdAt).toLocaleString()}`);
370
402
  console.log();
371
403
  }
372
404
 
@@ -442,34 +474,181 @@ async function promptAddress() {
442
474
  async function promptConfirm(message) {
443
475
  return confirm({ message });
444
476
  }
445
- async function promptBudget(suggested) {
477
+ function formatCents2(cents) {
478
+ return `$${(cents / 100).toFixed(2)}`;
479
+ }
480
+ function parseBudgetInput(value) {
481
+ const trimmed = value.trim();
482
+ if (!trimmed) return null;
483
+ if (/^\d+\.\d{1,2}$/.test(trimmed)) {
484
+ return Math.round(Number(trimmed) * 100);
485
+ }
486
+ if (/^\d+$/.test(trimmed)) {
487
+ return parseInt(trimmed, 10);
488
+ }
489
+ return null;
490
+ }
491
+ async function promptBudget(suggested, minimumCents) {
446
492
  const value = await input({
447
- message: `Max budget in cents (suggested: ${suggested})`,
493
+ message: `Max budget in cents (e.g. ${suggested} = ${formatCents2(suggested)})`,
448
494
  default: String(suggested),
449
495
  validate: (v) => {
450
- const n = parseInt(v, 10);
451
- if (isNaN(n) || n <= 0) return "Must be a positive integer";
496
+ const n = parseBudgetInput(v);
497
+ if (n === null || n <= 0) {
498
+ return "Enter cents like 1540 or dollars like 15.40";
499
+ }
500
+ if (n < minimumCents) {
501
+ return `Max budget must be at least the current product price ${formatCents2(minimumCents)}`;
502
+ }
452
503
  return true;
453
504
  }
454
505
  });
455
- return parseInt(value, 10);
506
+ const parsed = parseBudgetInput(value);
507
+ if (parsed === null) {
508
+ throw new Error("Invalid budget input");
509
+ }
510
+ return parsed;
511
+ }
512
+
513
+ // src/cli/tokenStore.ts
514
+ import { chmod, mkdir, readFile, writeFile } from "fs/promises";
515
+ import { homedir } from "os";
516
+ import { dirname, join } from "path";
517
+ var STORE_PATH = join(homedir(), ".agentmall", "tokens.json");
518
+ function emptyStore() {
519
+ return {
520
+ version: 1,
521
+ purchases: {}
522
+ };
523
+ }
524
+ async function readStore() {
525
+ try {
526
+ const raw = await readFile(STORE_PATH, "utf8");
527
+ const parsed = JSON.parse(raw);
528
+ return {
529
+ version: 1,
530
+ purchases: parsed.purchases ?? {}
531
+ };
532
+ } catch {
533
+ return emptyStore();
534
+ }
535
+ }
536
+ async function writeStore(store) {
537
+ await mkdir(dirname(STORE_PATH), { recursive: true });
538
+ await writeFile(STORE_PATH, JSON.stringify(store, null, 2), "utf8");
539
+ try {
540
+ await chmod(STORE_PATH, 384);
541
+ } catch {
542
+ }
543
+ }
544
+ async function saveBuyerToken(purchaseId, buyerToken) {
545
+ const store = await readStore();
546
+ store.purchases[purchaseId] = {
547
+ buyerToken,
548
+ savedAt: Date.now()
549
+ };
550
+ await writeStore(store);
551
+ }
552
+ async function getBuyerToken(purchaseId) {
553
+ const store = await readStore();
554
+ return store.purchases[purchaseId]?.buyerToken ?? null;
456
555
  }
457
556
 
458
557
  // src/cli/index.ts
459
558
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
460
- async function resolveAccount() {
559
+ async function runCommandCapture(command2, args2) {
560
+ return await new Promise((resolve, reject) => {
561
+ const child = spawn(command2, args2, {
562
+ stdio: ["ignore", "pipe", "pipe"],
563
+ shell: process.platform === "win32"
564
+ });
565
+ let stdout = "";
566
+ let stderr = "";
567
+ child.stdout?.on("data", (chunk) => {
568
+ stdout += chunk.toString();
569
+ });
570
+ child.stderr?.on("data", (chunk) => {
571
+ stderr += chunk.toString();
572
+ });
573
+ child.on("error", reject);
574
+ child.on(
575
+ "exit",
576
+ (code) => resolve({
577
+ code: code ?? 1,
578
+ stdout,
579
+ stderr
580
+ })
581
+ );
582
+ });
583
+ }
584
+ async function runInteractiveCommand(command2, args2) {
585
+ return await new Promise((resolve, reject) => {
586
+ const child = spawn(command2, args2, {
587
+ stdio: "inherit",
588
+ shell: process.platform === "win32"
589
+ });
590
+ child.on("error", reject);
591
+ child.on("exit", (code) => resolve(code ?? 1));
592
+ });
593
+ }
594
+ async function readTempoWhoami() {
461
595
  try {
462
- const { resolveAccount: resolve } = await import("mppx/cli");
463
- return await resolve();
596
+ const result = await runCommandCapture("tempo", ["wallet", "-j", "whoami"]);
597
+ if (result.code !== 0) return null;
598
+ return JSON.parse(result.stdout);
599
+ } catch {
600
+ return null;
601
+ }
602
+ }
603
+ async function ensureTempoInstalled() {
604
+ try {
605
+ const result = await runCommandCapture("tempo", ["--version"]);
606
+ if (result.code === 0) return;
464
607
  } catch {
465
- const key = process.env.MPPX_PRIVATE_KEY;
466
- if (!key) {
467
- console.error("\x1B[31mNo wallet found.\x1B[0m");
468
- console.error("Set MPPX_PRIVATE_KEY or run: npx mppx account create");
469
- process.exit(1);
470
- }
471
- const { privateKeyToAccount } = await import("viem/accounts");
472
- return privateKeyToAccount(key);
608
+ }
609
+ console.error("\x1B[31mTempo CLI is required for purchases.\x1B[0m");
610
+ console.error("");
611
+ console.error("Install it first:");
612
+ console.error(" curl -fsSL https://tempo.xyz/install | bash");
613
+ process.exit(1);
614
+ }
615
+ async function ensureTempoReady() {
616
+ await ensureTempoInstalled();
617
+ const whoami = await readTempoWhoami();
618
+ if (whoami?.ready) return whoami;
619
+ console.log(" Tempo wallet login required...");
620
+ const loginCode = await runInteractiveCommand("tempo", ["wallet", "login"]);
621
+ if (loginCode !== 0) {
622
+ throw new Error("Tempo wallet login failed");
623
+ }
624
+ const afterLogin = await readTempoWhoami();
625
+ if (afterLogin?.ready) return afterLogin;
626
+ throw new Error("Tempo wallet is not ready after login");
627
+ }
628
+ function formatUsdAmount(cents) {
629
+ return (cents / 100).toFixed(2);
630
+ }
631
+ async function createPurchaseWithTempo(body) {
632
+ const totalChargeCents = body.max_budget + SERVICE_FEE_CENTS;
633
+ const result = await runCommandCapture("tempo", [
634
+ "request",
635
+ "-s",
636
+ "--max-spend",
637
+ formatUsdAmount(totalChargeCents),
638
+ "-X",
639
+ "POST",
640
+ "--json",
641
+ JSON.stringify(body),
642
+ `${BASE_URL}/api/purchases`
643
+ ]);
644
+ if (result.code !== 0) {
645
+ const message = result.stderr.trim() || result.stdout.trim() || "Tempo request failed";
646
+ throw new Error(message);
647
+ }
648
+ try {
649
+ return JSON.parse(result.stdout);
650
+ } catch {
651
+ throw new Error(`Unexpected response from Tempo request: ${result.stdout.trim() || "(empty)"}`);
473
652
  }
474
653
  }
475
654
  async function buy(urlArg) {
@@ -477,15 +656,14 @@ async function buy(urlArg) {
477
656
  console.log();
478
657
  console.log(" \x1B[1magentmall\x1B[0m \u2014 Buy anything with a URL");
479
658
  console.log();
480
- const account = await resolveAccount();
481
659
  const url = urlArg ?? await promptProductUrl();
482
660
  const client = new AgentMall();
483
- console.log(" Fetching product info...");
661
+ console.log(" Looking up product...");
484
662
  const product = await client.products.lookup(url);
485
663
  displayProduct(product);
486
664
  const variantSelections = product.variants?.length ? await promptVariants(product.variants) : void 0;
487
665
  const address = await promptAddress();
488
- const maxBudget = await promptBudget(product.suggestedMaxBudget);
666
+ const maxBudget = await promptBudget(product.suggestedMaxBudget, product.price);
489
667
  const { input: input2 } = await import("@inquirer/prompts");
490
668
  const buyerEmail = await input2({
491
669
  message: "Email for order updates",
@@ -509,11 +687,16 @@ async function buy(urlArg) {
509
687
  console.log(" Cancelled.");
510
688
  return;
511
689
  }
690
+ const wallet = await ensureTempoReady();
691
+ if (wallet.wallet) {
692
+ const balance = wallet.balance?.available ? `, balance ${wallet.balance.available} ${wallet.balance?.symbol ?? "USDC"}` : "";
693
+ console.log(` Paying with Tempo wallet ${wallet.wallet}${balance}`);
694
+ }
512
695
  console.log(" Placing order...");
513
- const order = await purchase({
514
- ...body,
515
- account
516
- });
696
+ const order = await createPurchaseWithTempo(body);
697
+ if (order.buyerToken) {
698
+ await saveBuyerToken(order.id, order.buyerToken);
699
+ }
517
700
  displayOrderResult(order);
518
701
  } catch (error) {
519
702
  console.log();
@@ -542,10 +725,48 @@ async function buy(urlArg) {
542
725
  }
543
726
  async function status(purchaseId) {
544
727
  const apiSecret = process.env.AGENTMALL_API_SECRET;
545
- const client = apiSecret ? new AgentMall({ apiSecret }) : new AgentMall({ account: await resolveAccount() });
546
- const result = await client.purchases.get(purchaseId);
728
+ const buyerToken = apiSecret ? null : await getBuyerToken(purchaseId);
729
+ const client = apiSecret ? new AgentMall({ apiSecret }) : new AgentMall();
730
+ if (!apiSecret && !buyerToken) {
731
+ throw new Error("No saved buyer token found for this order on this machine.");
732
+ }
733
+ const result = await client.purchases.get(
734
+ purchaseId,
735
+ buyerToken ? { buyerToken } : void 0
736
+ );
547
737
  displayStatus(result);
548
738
  }
739
+ async function refund(purchaseId) {
740
+ const apiSecret = process.env.AGENTMALL_API_SECRET;
741
+ const buyerToken = apiSecret ? null : await getBuyerToken(purchaseId);
742
+ const client = apiSecret ? new AgentMall({ apiSecret }) : new AgentMall();
743
+ if (!apiSecret && !buyerToken) {
744
+ throw new Error("No saved buyer token found for this order on this machine.");
745
+ }
746
+ const result = await client.refunds.getByPurchase(
747
+ purchaseId,
748
+ buyerToken ? { buyerToken } : void 0
749
+ );
750
+ displayRefund(result);
751
+ }
752
+ async function onboard() {
753
+ console.log();
754
+ console.log(" \x1B[1magentmall onboarding\x1B[0m");
755
+ console.log();
756
+ const wallet = await ensureTempoReady();
757
+ console.log();
758
+ if (wallet.wallet) {
759
+ console.log(` Wallet ready: ${wallet.wallet}`);
760
+ }
761
+ if (wallet.balance?.available) {
762
+ console.log(` Available balance: ${wallet.balance.available} ${wallet.balance?.symbol ?? "USDC"}`);
763
+ }
764
+ console.log(" Next steps:");
765
+ console.log(" 1. Run \x1B[1mtempo wallet whoami\x1B[0m to review your wallet");
766
+ console.log(" 2. Add funds to your Tempo wallet if needed");
767
+ console.log(" 3. Retry your purchase with \x1B[1mnpx agentmall buy <url>\x1B[0m");
768
+ console.log();
769
+ }
549
770
 
550
771
  // src/cli.ts
551
772
  var args = process.argv.slice(2);
@@ -563,6 +784,17 @@ async function main() {
563
784
  }
564
785
  await status(args[1]);
565
786
  break;
787
+ case "refund":
788
+ if (!args[1]) {
789
+ console.error("Usage: agentmall refund <purchase_id>");
790
+ process.exit(1);
791
+ }
792
+ await refund(args[1]);
793
+ break;
794
+ case "onboard":
795
+ case "setup":
796
+ await onboard();
797
+ break;
566
798
  case "help":
567
799
  case "--help":
568
800
  case "-h":
@@ -588,14 +820,12 @@ function printHelp() {
588
820
  \x1B[1magentmall\x1B[0m \u2014 Buy anything with a URL
589
821
 
590
822
  Usage:
823
+ agentmall onboard Log in to Tempo and show wallet funding steps
591
824
  agentmall [url] Buy a product (interactive)
592
825
  agentmall buy <url> Buy a product
593
- agentmall status <id> Check order status with the payer wallet
826
+ agentmall status <id> Check order status using the saved buyer token
827
+ agentmall refund <id> Check refund status using the saved buyer token
594
828
  agentmall help Show this help
595
-
596
- Environment:
597
- MPPX_PRIVATE_KEY Wallet private key for payments and buyer status checks
598
- AGENTMALL_API_SECRET Operator secret for internal admin read endpoints
599
829
  `);
600
830
  }
601
831
  main();
package/dist/index.cjs CHANGED
@@ -108,7 +108,6 @@ function createIdempotencyKey() {
108
108
  var AgentMall = class {
109
109
  baseUrl;
110
110
  apiSecret;
111
- account;
112
111
  _fetch;
113
112
  products;
114
113
  purchases;
@@ -117,7 +116,6 @@ var AgentMall = class {
117
116
  constructor(config = {}) {
118
117
  this.baseUrl = (config.baseUrl ?? BASE_URL).replace(/\/$/, "");
119
118
  this.apiSecret = config.apiSecret;
120
- this.account = config.account;
121
119
  this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);
122
120
  this.products = new AgentMallProducts(this);
123
121
  this.purchases = new AgentMallPurchases(this);
@@ -125,8 +123,8 @@ var AgentMall = class {
125
123
  this.refunds = new AgentMallRefunds(this);
126
124
  }
127
125
  /** @internal */
128
- getAccount() {
129
- return this.account;
126
+ hasApiSecret() {
127
+ return Boolean(this.apiSecret);
130
128
  }
131
129
  /** @internal */
132
130
  async request(method, path, options) {
@@ -208,28 +206,19 @@ var AgentMallPurchases = class {
208
206
  }
209
207
  });
210
208
  }
211
- /** Get a single purchase by ID using either operator auth or the payer wallet. */
209
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
212
210
  async get(id, options) {
213
- const account = options?.account ?? this.client.getAccount();
214
- if (account) {
215
- const challenge = await this.client.request(
216
- "POST",
217
- "/api/purchases/status/challenge",
218
- {
219
- body: { id }
220
- }
221
- );
222
- const signature = await account.signMessage({
223
- message: challenge.message
224
- });
211
+ if (options?.buyerToken) {
225
212
  return this.client.request("POST", "/api/purchases/status", {
226
213
  body: {
227
214
  id,
228
- challenge: challenge.challenge,
229
- signature
215
+ buyer_token: options.buyerToken
230
216
  }
231
217
  });
232
218
  }
219
+ if (!this.client.hasApiSecret()) {
220
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
221
+ }
233
222
  return this.client.request("GET", "/api/purchases", {
234
223
  auth: true,
235
224
  params: { id }
@@ -277,6 +266,33 @@ var AgentMallRefunds = class {
277
266
  params: { id }
278
267
  });
279
268
  }
269
+ /** Get refund status for a purchase using either operator auth or a buyer token. */
270
+ async getByPurchase(purchaseId, options) {
271
+ if (options?.buyerToken) {
272
+ return this.client.request("POST", "/api/refunds/status", {
273
+ body: {
274
+ purchase_id: purchaseId,
275
+ buyer_token: options.buyerToken
276
+ }
277
+ });
278
+ }
279
+ if (!this.client.hasApiSecret()) {
280
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
281
+ }
282
+ const result = await this.client.request(
283
+ "GET",
284
+ "/api/refunds",
285
+ {
286
+ auth: true,
287
+ params: { purchase_id: purchaseId }
288
+ }
289
+ );
290
+ const refund = result.refunds[0];
291
+ if (!refund) {
292
+ throw new AgentMallError(404, { error: "Not found" });
293
+ }
294
+ return refund;
295
+ }
280
296
  /** List refunds. Requires apiSecret. */
281
297
  async list(filters) {
282
298
  const result = await this.client.request(
package/dist/index.d.cts CHANGED
@@ -3,8 +3,6 @@ type AgentMallConfig = {
3
3
  baseUrl?: string;
4
4
  /** Operator API secret for admin read endpoints (Bearer auth). */
5
5
  apiSecret?: string;
6
- /** Wallet account used for buyer-authenticated purchase status reads. */
7
- account?: WalletAccount;
8
6
  /** Custom fetch implementation (e.g. mppx-wrapped fetch for automatic 402 handling). */
9
7
  fetch?: typeof globalThis.fetch;
10
8
  };
@@ -33,7 +31,7 @@ type ProductLookup = {
33
31
  availability: string;
34
32
  imageUrl?: string;
35
33
  retailer: string;
36
- /** Suggested max_budget in cents (list price + 15% buffer). */
34
+ /** Suggested max_budget in cents (list price + a buffer, with a minimum $8 buffer). */
37
35
  suggestedMaxBudget: number;
38
36
  variants?: ProductVariant[];
39
37
  /** True if served from cache. Price may not reflect current listing. */
@@ -72,12 +70,14 @@ type CreatePurchaseResponse = {
72
70
  id: string;
73
71
  status: PurchaseLifecycleStatus;
74
72
  deduplicated?: boolean;
73
+ buyerToken?: string;
75
74
  };
76
75
  type PurchaseStatusChallenge = {
77
76
  challenge: string;
78
77
  message: string;
79
78
  expiresAt: number;
80
79
  };
80
+ type RefundStatusChallenge = PurchaseStatusChallenge;
81
81
  type PurchaseItem = {
82
82
  productRef: string;
83
83
  quantity: number;
@@ -144,7 +144,6 @@ type RefundFilters = {
144
144
  declare class AgentMall {
145
145
  private baseUrl;
146
146
  private apiSecret?;
147
- private account?;
148
147
  private _fetch;
149
148
  products: AgentMallProducts;
150
149
  purchases: AgentMallPurchases;
@@ -152,7 +151,7 @@ declare class AgentMall {
152
151
  refunds: AgentMallRefunds;
153
152
  constructor(config?: AgentMallConfig);
154
153
  /** @internal */
155
- getAccount(): WalletAccount | undefined;
154
+ hasApiSecret(): boolean;
156
155
  /** @internal */
157
156
  request<T>(method: string, path: string, options?: {
158
157
  body?: unknown;
@@ -175,9 +174,9 @@ declare class AgentMallPurchases {
175
174
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
176
175
  */
177
176
  create(body: CreatePurchaseRequest): Promise<CreatePurchaseResponse>;
178
- /** Get a single purchase by ID using either operator auth or the payer wallet. */
177
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
179
178
  get(id: string, options?: {
180
- account?: WalletAccount;
179
+ buyerToken?: string;
181
180
  }): Promise<Purchase>;
182
181
  /** List purchases. Requires apiSecret. */
183
182
  list(filters?: PurchaseFilters): Promise<Purchase[]>;
@@ -195,6 +194,10 @@ declare class AgentMallRefunds {
195
194
  constructor(client: AgentMall);
196
195
  /** Get a single refund by public ID. Requires apiSecret. */
197
196
  get(id: string): Promise<Refund>;
197
+ /** Get refund status for a purchase using either operator auth or a buyer token. */
198
+ getByPurchase(purchaseId: string, options?: {
199
+ buyerToken?: string;
200
+ }): Promise<Refund>;
198
201
  /** List refunds. Requires apiSecret. */
199
202
  list(filters?: RefundFilters): Promise<Refund[]>;
200
203
  }
@@ -248,4 +251,4 @@ declare const BASE_URL = "https://api.agentmall.sh";
248
251
  declare const SERVICE_FEE_CENTS = 150;
249
252
  declare const SUPPORTED_RETAILERS: readonly ["amazon.com", "walmart.com", "target.com", "bestbuy.com", "homedepot.com", "ebay.com", "lowes.com", "wayfair.com", "acehardware.com", "1800flowers.com", "pokemoncenter.com"];
250
253
 
251
- export { AgentMall, type AgentMallConfig, AgentMallError, BASE_URL, ConflictError, type CreatePurchaseRequest, type CreatePurchaseResponse, type DeliveryAddress, type Payment, type PaymentFilters, PaymentRequiredError, type ProductLookup, type ProductVariant, type Purchase, type PurchaseFilters, type PurchaseItem, type PurchaseItemInput, type PurchaseLifecycleStatus, type PurchaseStatusChallenge, RateLimitError, type Refund, type RefundFilters, SERVICE_FEE_CENTS, SUPPORTED_RETAILERS, ValidationError, type VariantSelection, type WalletAccount, purchase };
254
+ export { AgentMall, type AgentMallConfig, AgentMallError, BASE_URL, ConflictError, type CreatePurchaseRequest, type CreatePurchaseResponse, type DeliveryAddress, type Payment, type PaymentFilters, PaymentRequiredError, type ProductLookup, type ProductVariant, type Purchase, type PurchaseFilters, type PurchaseItem, type PurchaseItemInput, type PurchaseLifecycleStatus, type PurchaseStatusChallenge, RateLimitError, type Refund, type RefundFilters, type RefundStatusChallenge, SERVICE_FEE_CENTS, SUPPORTED_RETAILERS, ValidationError, type VariantSelection, type WalletAccount, purchase };
package/dist/index.d.ts CHANGED
@@ -3,8 +3,6 @@ type AgentMallConfig = {
3
3
  baseUrl?: string;
4
4
  /** Operator API secret for admin read endpoints (Bearer auth). */
5
5
  apiSecret?: string;
6
- /** Wallet account used for buyer-authenticated purchase status reads. */
7
- account?: WalletAccount;
8
6
  /** Custom fetch implementation (e.g. mppx-wrapped fetch for automatic 402 handling). */
9
7
  fetch?: typeof globalThis.fetch;
10
8
  };
@@ -33,7 +31,7 @@ type ProductLookup = {
33
31
  availability: string;
34
32
  imageUrl?: string;
35
33
  retailer: string;
36
- /** Suggested max_budget in cents (list price + 15% buffer). */
34
+ /** Suggested max_budget in cents (list price + a buffer, with a minimum $8 buffer). */
37
35
  suggestedMaxBudget: number;
38
36
  variants?: ProductVariant[];
39
37
  /** True if served from cache. Price may not reflect current listing. */
@@ -72,12 +70,14 @@ type CreatePurchaseResponse = {
72
70
  id: string;
73
71
  status: PurchaseLifecycleStatus;
74
72
  deduplicated?: boolean;
73
+ buyerToken?: string;
75
74
  };
76
75
  type PurchaseStatusChallenge = {
77
76
  challenge: string;
78
77
  message: string;
79
78
  expiresAt: number;
80
79
  };
80
+ type RefundStatusChallenge = PurchaseStatusChallenge;
81
81
  type PurchaseItem = {
82
82
  productRef: string;
83
83
  quantity: number;
@@ -144,7 +144,6 @@ type RefundFilters = {
144
144
  declare class AgentMall {
145
145
  private baseUrl;
146
146
  private apiSecret?;
147
- private account?;
148
147
  private _fetch;
149
148
  products: AgentMallProducts;
150
149
  purchases: AgentMallPurchases;
@@ -152,7 +151,7 @@ declare class AgentMall {
152
151
  refunds: AgentMallRefunds;
153
152
  constructor(config?: AgentMallConfig);
154
153
  /** @internal */
155
- getAccount(): WalletAccount | undefined;
154
+ hasApiSecret(): boolean;
156
155
  /** @internal */
157
156
  request<T>(method: string, path: string, options?: {
158
157
  body?: unknown;
@@ -175,9 +174,9 @@ declare class AgentMallPurchases {
175
174
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
176
175
  */
177
176
  create(body: CreatePurchaseRequest): Promise<CreatePurchaseResponse>;
178
- /** Get a single purchase by ID using either operator auth or the payer wallet. */
177
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
179
178
  get(id: string, options?: {
180
- account?: WalletAccount;
179
+ buyerToken?: string;
181
180
  }): Promise<Purchase>;
182
181
  /** List purchases. Requires apiSecret. */
183
182
  list(filters?: PurchaseFilters): Promise<Purchase[]>;
@@ -195,6 +194,10 @@ declare class AgentMallRefunds {
195
194
  constructor(client: AgentMall);
196
195
  /** Get a single refund by public ID. Requires apiSecret. */
197
196
  get(id: string): Promise<Refund>;
197
+ /** Get refund status for a purchase using either operator auth or a buyer token. */
198
+ getByPurchase(purchaseId: string, options?: {
199
+ buyerToken?: string;
200
+ }): Promise<Refund>;
198
201
  /** List refunds. Requires apiSecret. */
199
202
  list(filters?: RefundFilters): Promise<Refund[]>;
200
203
  }
@@ -248,4 +251,4 @@ declare const BASE_URL = "https://api.agentmall.sh";
248
251
  declare const SERVICE_FEE_CENTS = 150;
249
252
  declare const SUPPORTED_RETAILERS: readonly ["amazon.com", "walmart.com", "target.com", "bestbuy.com", "homedepot.com", "ebay.com", "lowes.com", "wayfair.com", "acehardware.com", "1800flowers.com", "pokemoncenter.com"];
250
253
 
251
- export { AgentMall, type AgentMallConfig, AgentMallError, BASE_URL, ConflictError, type CreatePurchaseRequest, type CreatePurchaseResponse, type DeliveryAddress, type Payment, type PaymentFilters, PaymentRequiredError, type ProductLookup, type ProductVariant, type Purchase, type PurchaseFilters, type PurchaseItem, type PurchaseItemInput, type PurchaseLifecycleStatus, type PurchaseStatusChallenge, RateLimitError, type Refund, type RefundFilters, SERVICE_FEE_CENTS, SUPPORTED_RETAILERS, ValidationError, type VariantSelection, type WalletAccount, purchase };
254
+ export { AgentMall, type AgentMallConfig, AgentMallError, BASE_URL, ConflictError, type CreatePurchaseRequest, type CreatePurchaseResponse, type DeliveryAddress, type Payment, type PaymentFilters, PaymentRequiredError, type ProductLookup, type ProductVariant, type Purchase, type PurchaseFilters, type PurchaseItem, type PurchaseItemInput, type PurchaseLifecycleStatus, type PurchaseStatusChallenge, RateLimitError, type Refund, type RefundFilters, type RefundStatusChallenge, SERVICE_FEE_CENTS, SUPPORTED_RETAILERS, ValidationError, type VariantSelection, type WalletAccount, purchase };
package/dist/index.js CHANGED
@@ -63,7 +63,6 @@ function createIdempotencyKey() {
63
63
  var AgentMall = class {
64
64
  baseUrl;
65
65
  apiSecret;
66
- account;
67
66
  _fetch;
68
67
  products;
69
68
  purchases;
@@ -72,7 +71,6 @@ var AgentMall = class {
72
71
  constructor(config = {}) {
73
72
  this.baseUrl = (config.baseUrl ?? BASE_URL).replace(/\/$/, "");
74
73
  this.apiSecret = config.apiSecret;
75
- this.account = config.account;
76
74
  this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);
77
75
  this.products = new AgentMallProducts(this);
78
76
  this.purchases = new AgentMallPurchases(this);
@@ -80,8 +78,8 @@ var AgentMall = class {
80
78
  this.refunds = new AgentMallRefunds(this);
81
79
  }
82
80
  /** @internal */
83
- getAccount() {
84
- return this.account;
81
+ hasApiSecret() {
82
+ return Boolean(this.apiSecret);
85
83
  }
86
84
  /** @internal */
87
85
  async request(method, path, options) {
@@ -163,28 +161,19 @@ var AgentMallPurchases = class {
163
161
  }
164
162
  });
165
163
  }
166
- /** Get a single purchase by ID using either operator auth or the payer wallet. */
164
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
167
165
  async get(id, options) {
168
- const account = options?.account ?? this.client.getAccount();
169
- if (account) {
170
- const challenge = await this.client.request(
171
- "POST",
172
- "/api/purchases/status/challenge",
173
- {
174
- body: { id }
175
- }
176
- );
177
- const signature = await account.signMessage({
178
- message: challenge.message
179
- });
166
+ if (options?.buyerToken) {
180
167
  return this.client.request("POST", "/api/purchases/status", {
181
168
  body: {
182
169
  id,
183
- challenge: challenge.challenge,
184
- signature
170
+ buyer_token: options.buyerToken
185
171
  }
186
172
  });
187
173
  }
174
+ if (!this.client.hasApiSecret()) {
175
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
176
+ }
188
177
  return this.client.request("GET", "/api/purchases", {
189
178
  auth: true,
190
179
  params: { id }
@@ -232,6 +221,33 @@ var AgentMallRefunds = class {
232
221
  params: { id }
233
222
  });
234
223
  }
224
+ /** Get refund status for a purchase using either operator auth or a buyer token. */
225
+ async getByPurchase(purchaseId, options) {
226
+ if (options?.buyerToken) {
227
+ return this.client.request("POST", "/api/refunds/status", {
228
+ body: {
229
+ purchase_id: purchaseId,
230
+ buyer_token: options.buyerToken
231
+ }
232
+ });
233
+ }
234
+ if (!this.client.hasApiSecret()) {
235
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
236
+ }
237
+ const result = await this.client.request(
238
+ "GET",
239
+ "/api/refunds",
240
+ {
241
+ auth: true,
242
+ params: { purchase_id: purchaseId }
243
+ }
244
+ );
245
+ const refund = result.refunds[0];
246
+ if (!refund) {
247
+ throw new AgentMallError(404, { error: "Not found" });
248
+ }
249
+ return refund;
250
+ }
235
251
  /** List refunds. Requires apiSecret. */
236
252
  async list(filters) {
237
253
  const result = await this.client.request(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentmall",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",