agentmall 0.1.1 → 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;
@@ -80,6 +83,10 @@ var AgentMall = class {
80
83
  this.refunds = new AgentMallRefunds(this);
81
84
  }
82
85
  /** @internal */
86
+ hasApiSecret() {
87
+ return Boolean(this.apiSecret);
88
+ }
89
+ /** @internal */
83
90
  async request(method, path, options) {
84
91
  const url = new URL(`${this.baseUrl}${path}`);
85
92
  if (options?.params) {
@@ -159,8 +166,19 @@ var AgentMallPurchases = class {
159
166
  }
160
167
  });
161
168
  }
162
- /** Get a single purchase by ID. Requires apiSecret. */
163
- async get(id) {
169
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
170
+ async get(id, options) {
171
+ if (options?.buyerToken) {
172
+ return this.client.request("POST", "/api/purchases/status", {
173
+ body: {
174
+ id,
175
+ buyer_token: options.buyerToken
176
+ }
177
+ });
178
+ }
179
+ if (!this.client.hasApiSecret()) {
180
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
181
+ }
164
182
  return this.client.request("GET", "/api/purchases", {
165
183
  auth: true,
166
184
  params: { id }
@@ -208,6 +226,33 @@ var AgentMallRefunds = class {
208
226
  params: { id }
209
227
  });
210
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
+ }
211
256
  /** List refunds. Requires apiSecret. */
212
257
  async list(filters) {
213
258
  const result = await this.client.request(
@@ -219,31 +264,24 @@ var AgentMallRefunds = class {
219
264
  }
220
265
  };
221
266
 
222
- // src/purchase.ts
223
- async function purchase(config) {
224
- const { account, baseUrl, ...body } = config;
225
- let mppxClient;
226
- try {
227
- mppxClient = await import("mppx/client");
228
- } catch {
229
- throw new Error("mppx is required for purchases. Install it: npm install mppx");
230
- }
231
- const Mppx = mppxClient.Mppx;
232
- const tempo = mppxClient.tempo;
233
- if (!Mppx || !tempo) {
234
- throw new Error("mppx is required for purchases. Install it: npm install mppx");
235
- }
236
- const mppx = Mppx.create({
237
- methods: [tempo({ account })]
238
- });
239
- const client = new AgentMall({
240
- baseUrl: baseUrl ?? BASE_URL,
241
- fetch: mppx.fetch.bind(mppx)
242
- });
243
- return client.purchases.create(body);
244
- }
245
-
246
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
+ };
247
285
  function formatCents(cents) {
248
286
  return `$${(cents / 100).toFixed(2)}`;
249
287
  }
@@ -268,7 +306,7 @@ function displayProduct(product) {
268
306
  }
269
307
  console.log();
270
308
  console.log(
271
- ` 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)`
272
310
  );
273
311
  console.log(
274
312
  ` Service fee: ${formatCents(SERVICE_FEE_CENTS)}`
@@ -314,6 +352,9 @@ function displayOrderResult(order) {
314
352
  }
315
353
  console.log(` ID: \x1B[1m${order.id}\x1B[0m`);
316
354
  console.log(` Status: ${order.status}`);
355
+ if (order.buyerToken) {
356
+ console.log(" Saved local access token for status and refund checks.");
357
+ }
317
358
  console.log();
318
359
  console.log(
319
360
  order.status === "failed" ? " Check status for failure details and refund progress:" : " Check status in 5-10 minutes:"
@@ -321,26 +362,43 @@ function displayOrderResult(order) {
321
362
  console.log(` npx agentmall status ${order.id}`);
322
363
  console.log();
323
364
  }
324
- function displayStatus(purchase2) {
365
+ function displayStatus(purchase) {
325
366
  console.log();
326
- console.log(` \x1B[1mOrder ${purchase2.id}\x1B[0m`);
327
- console.log(` Status: ${purchase2.status}`);
328
- if (purchase2.items?.length) {
329
- 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) {
330
371
  const price = item.price ? ` \u2014 ${formatCents(item.price)}` : "";
331
372
  console.log(` ${item.quantity}x ${item.title ?? item.productRef}${price}`);
332
373
  }
333
374
  }
334
- if (purchase2.finalTotal) {
335
- console.log(` Final total: ${formatCents(purchase2.finalTotal)}`);
375
+ if (purchase.finalTotal) {
376
+ console.log(` Final total: ${formatCents(purchase.finalTotal)}`);
336
377
  }
337
- if (purchase2.failureReason) {
338
- 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
+ );
339
382
  }
340
- if (purchase2.deliveryMethod) {
341
- 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()}`);
342
401
  }
343
- console.log(` Created: ${new Date(purchase2.createdAt).toLocaleString()}`);
344
402
  console.log();
345
403
  }
346
404
 
@@ -416,34 +474,181 @@ async function promptAddress() {
416
474
  async function promptConfirm(message) {
417
475
  return confirm({ message });
418
476
  }
419
- 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) {
420
492
  const value = await input({
421
- message: `Max budget in cents (suggested: ${suggested})`,
493
+ message: `Max budget in cents (e.g. ${suggested} = ${formatCents2(suggested)})`,
422
494
  default: String(suggested),
423
495
  validate: (v) => {
424
- const n = parseInt(v, 10);
425
- 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
+ }
426
503
  return true;
427
504
  }
428
505
  });
429
- 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;
430
555
  }
431
556
 
432
557
  // src/cli/index.ts
433
558
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
434
- 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() {
435
595
  try {
436
- const { resolveAccount: resolve } = await import("mppx/cli");
437
- 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);
438
599
  } catch {
439
- const key = process.env.MPPX_PRIVATE_KEY;
440
- if (!key) {
441
- console.error("\x1B[31mNo wallet found.\x1B[0m");
442
- console.error("Set MPPX_PRIVATE_KEY or run: npx mppx account create");
443
- process.exit(1);
444
- }
445
- const { privateKeyToAccount } = await import("viem/accounts");
446
- return privateKeyToAccount(key);
600
+ return null;
601
+ }
602
+ }
603
+ async function ensureTempoInstalled() {
604
+ try {
605
+ const result = await runCommandCapture("tempo", ["--version"]);
606
+ if (result.code === 0) return;
607
+ } catch {
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)"}`);
447
652
  }
448
653
  }
449
654
  async function buy(urlArg) {
@@ -451,15 +656,14 @@ async function buy(urlArg) {
451
656
  console.log();
452
657
  console.log(" \x1B[1magentmall\x1B[0m \u2014 Buy anything with a URL");
453
658
  console.log();
454
- const account = await resolveAccount();
455
659
  const url = urlArg ?? await promptProductUrl();
456
660
  const client = new AgentMall();
457
- console.log(" Fetching product info...");
661
+ console.log(" Looking up product...");
458
662
  const product = await client.products.lookup(url);
459
663
  displayProduct(product);
460
664
  const variantSelections = product.variants?.length ? await promptVariants(product.variants) : void 0;
461
665
  const address = await promptAddress();
462
- const maxBudget = await promptBudget(product.suggestedMaxBudget);
666
+ const maxBudget = await promptBudget(product.suggestedMaxBudget, product.price);
463
667
  const { input: input2 } = await import("@inquirer/prompts");
464
668
  const buyerEmail = await input2({
465
669
  message: "Email for order updates",
@@ -483,11 +687,16 @@ async function buy(urlArg) {
483
687
  console.log(" Cancelled.");
484
688
  return;
485
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
+ }
486
695
  console.log(" Placing order...");
487
- const order = await purchase({
488
- ...body,
489
- account
490
- });
696
+ const order = await createPurchaseWithTempo(body);
697
+ if (order.buyerToken) {
698
+ await saveBuyerToken(order.id, order.buyerToken);
699
+ }
491
700
  displayOrderResult(order);
492
701
  } catch (error) {
493
702
  console.log();
@@ -516,14 +725,48 @@ async function buy(urlArg) {
516
725
  }
517
726
  async function status(purchaseId) {
518
727
  const apiSecret = process.env.AGENTMALL_API_SECRET;
519
- if (!apiSecret) {
520
- console.error("\x1B[31mAGENTMALL_API_SECRET is required.\x1B[0m");
521
- process.exit(1);
522
- }
523
- const client = new AgentMall({ apiSecret });
524
- 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
+ );
525
737
  displayStatus(result);
526
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
+ }
527
770
 
528
771
  // src/cli.ts
529
772
  var args = process.argv.slice(2);
@@ -541,6 +784,17 @@ async function main() {
541
784
  }
542
785
  await status(args[1]);
543
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;
544
798
  case "help":
545
799
  case "--help":
546
800
  case "-h":
@@ -566,14 +820,12 @@ function printHelp() {
566
820
  \x1B[1magentmall\x1B[0m \u2014 Buy anything with a URL
567
821
 
568
822
  Usage:
823
+ agentmall onboard Log in to Tempo and show wallet funding steps
569
824
  agentmall [url] Buy a product (interactive)
570
825
  agentmall buy <url> Buy a product
571
- agentmall status <id> Check order status
826
+ agentmall status <id> Check order status using the saved buyer token
827
+ agentmall refund <id> Check refund status using the saved buyer token
572
828
  agentmall help Show this help
573
-
574
- Environment:
575
- MPPX_PRIVATE_KEY Wallet private key for payments
576
- AGENTMALL_API_SECRET Operator secret for read endpoints
577
829
  `);
578
830
  }
579
831
  main();
package/dist/index.cjs CHANGED
@@ -123,6 +123,10 @@ var AgentMall = class {
123
123
  this.refunds = new AgentMallRefunds(this);
124
124
  }
125
125
  /** @internal */
126
+ hasApiSecret() {
127
+ return Boolean(this.apiSecret);
128
+ }
129
+ /** @internal */
126
130
  async request(method, path, options) {
127
131
  const url = new URL(`${this.baseUrl}${path}`);
128
132
  if (options?.params) {
@@ -202,8 +206,19 @@ var AgentMallPurchases = class {
202
206
  }
203
207
  });
204
208
  }
205
- /** Get a single purchase by ID. Requires apiSecret. */
206
- async get(id) {
209
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
210
+ async get(id, options) {
211
+ if (options?.buyerToken) {
212
+ return this.client.request("POST", "/api/purchases/status", {
213
+ body: {
214
+ id,
215
+ buyer_token: options.buyerToken
216
+ }
217
+ });
218
+ }
219
+ if (!this.client.hasApiSecret()) {
220
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
221
+ }
207
222
  return this.client.request("GET", "/api/purchases", {
208
223
  auth: true,
209
224
  params: { id }
@@ -251,6 +266,33 @@ var AgentMallRefunds = class {
251
266
  params: { id }
252
267
  });
253
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
+ }
254
296
  /** List refunds. Requires apiSecret. */
255
297
  async list(filters) {
256
298
  const result = await this.client.request(
package/dist/index.d.cts CHANGED
@@ -1,11 +1,17 @@
1
1
  type AgentMallConfig = {
2
2
  /** API base URL. Defaults to https://api.agentmall.sh */
3
3
  baseUrl?: string;
4
- /** Operator API secret for read endpoints (Bearer auth). */
4
+ /** Operator API secret for admin read endpoints (Bearer auth). */
5
5
  apiSecret?: string;
6
6
  /** Custom fetch implementation (e.g. mppx-wrapped fetch for automatic 402 handling). */
7
7
  fetch?: typeof globalThis.fetch;
8
8
  };
9
+ type WalletAccount = {
10
+ address: string;
11
+ signMessage(args: {
12
+ message: string;
13
+ }): Promise<string>;
14
+ };
9
15
  type ProductVariant = {
10
16
  label?: string;
11
17
  value: string;
@@ -25,7 +31,7 @@ type ProductLookup = {
25
31
  availability: string;
26
32
  imageUrl?: string;
27
33
  retailer: string;
28
- /** Suggested max_budget in cents (list price + 15% buffer). */
34
+ /** Suggested max_budget in cents (list price + a buffer, with a minimum $8 buffer). */
29
35
  suggestedMaxBudget: number;
30
36
  variants?: ProductVariant[];
31
37
  /** True if served from cache. Price may not reflect current listing. */
@@ -64,7 +70,14 @@ type CreatePurchaseResponse = {
64
70
  id: string;
65
71
  status: PurchaseLifecycleStatus;
66
72
  deduplicated?: boolean;
73
+ buyerToken?: string;
74
+ };
75
+ type PurchaseStatusChallenge = {
76
+ challenge: string;
77
+ message: string;
78
+ expiresAt: number;
67
79
  };
80
+ type RefundStatusChallenge = PurchaseStatusChallenge;
68
81
  type PurchaseItem = {
69
82
  productRef: string;
70
83
  quantity: number;
@@ -138,6 +151,8 @@ declare class AgentMall {
138
151
  refunds: AgentMallRefunds;
139
152
  constructor(config?: AgentMallConfig);
140
153
  /** @internal */
154
+ hasApiSecret(): boolean;
155
+ /** @internal */
141
156
  request<T>(method: string, path: string, options?: {
142
157
  body?: unknown;
143
158
  auth?: boolean;
@@ -159,8 +174,10 @@ declare class AgentMallPurchases {
159
174
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
160
175
  */
161
176
  create(body: CreatePurchaseRequest): Promise<CreatePurchaseResponse>;
162
- /** Get a single purchase by ID. Requires apiSecret. */
163
- get(id: string): Promise<Purchase>;
177
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
178
+ get(id: string, options?: {
179
+ buyerToken?: string;
180
+ }): Promise<Purchase>;
164
181
  /** List purchases. Requires apiSecret. */
165
182
  list(filters?: PurchaseFilters): Promise<Purchase[]>;
166
183
  }
@@ -177,6 +194,10 @@ declare class AgentMallRefunds {
177
194
  constructor(client: AgentMall);
178
195
  /** Get a single refund by public ID. Requires apiSecret. */
179
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>;
180
201
  /** List refunds. Requires apiSecret. */
181
202
  list(filters?: RefundFilters): Promise<Refund[]>;
182
203
  }
@@ -230,4 +251,4 @@ declare const BASE_URL = "https://api.agentmall.sh";
230
251
  declare const SERVICE_FEE_CENTS = 150;
231
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"];
232
253
 
233
- 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, RateLimitError, type Refund, type RefundFilters, SERVICE_FEE_CENTS, SUPPORTED_RETAILERS, ValidationError, type VariantSelection, 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
@@ -1,11 +1,17 @@
1
1
  type AgentMallConfig = {
2
2
  /** API base URL. Defaults to https://api.agentmall.sh */
3
3
  baseUrl?: string;
4
- /** Operator API secret for read endpoints (Bearer auth). */
4
+ /** Operator API secret for admin read endpoints (Bearer auth). */
5
5
  apiSecret?: string;
6
6
  /** Custom fetch implementation (e.g. mppx-wrapped fetch for automatic 402 handling). */
7
7
  fetch?: typeof globalThis.fetch;
8
8
  };
9
+ type WalletAccount = {
10
+ address: string;
11
+ signMessage(args: {
12
+ message: string;
13
+ }): Promise<string>;
14
+ };
9
15
  type ProductVariant = {
10
16
  label?: string;
11
17
  value: string;
@@ -25,7 +31,7 @@ type ProductLookup = {
25
31
  availability: string;
26
32
  imageUrl?: string;
27
33
  retailer: string;
28
- /** Suggested max_budget in cents (list price + 15% buffer). */
34
+ /** Suggested max_budget in cents (list price + a buffer, with a minimum $8 buffer). */
29
35
  suggestedMaxBudget: number;
30
36
  variants?: ProductVariant[];
31
37
  /** True if served from cache. Price may not reflect current listing. */
@@ -64,7 +70,14 @@ type CreatePurchaseResponse = {
64
70
  id: string;
65
71
  status: PurchaseLifecycleStatus;
66
72
  deduplicated?: boolean;
73
+ buyerToken?: string;
74
+ };
75
+ type PurchaseStatusChallenge = {
76
+ challenge: string;
77
+ message: string;
78
+ expiresAt: number;
67
79
  };
80
+ type RefundStatusChallenge = PurchaseStatusChallenge;
68
81
  type PurchaseItem = {
69
82
  productRef: string;
70
83
  quantity: number;
@@ -138,6 +151,8 @@ declare class AgentMall {
138
151
  refunds: AgentMallRefunds;
139
152
  constructor(config?: AgentMallConfig);
140
153
  /** @internal */
154
+ hasApiSecret(): boolean;
155
+ /** @internal */
141
156
  request<T>(method: string, path: string, options?: {
142
157
  body?: unknown;
143
158
  auth?: boolean;
@@ -159,8 +174,10 @@ declare class AgentMallPurchases {
159
174
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
160
175
  */
161
176
  create(body: CreatePurchaseRequest): Promise<CreatePurchaseResponse>;
162
- /** Get a single purchase by ID. Requires apiSecret. */
163
- get(id: string): Promise<Purchase>;
177
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
178
+ get(id: string, options?: {
179
+ buyerToken?: string;
180
+ }): Promise<Purchase>;
164
181
  /** List purchases. Requires apiSecret. */
165
182
  list(filters?: PurchaseFilters): Promise<Purchase[]>;
166
183
  }
@@ -177,6 +194,10 @@ declare class AgentMallRefunds {
177
194
  constructor(client: AgentMall);
178
195
  /** Get a single refund by public ID. Requires apiSecret. */
179
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>;
180
201
  /** List refunds. Requires apiSecret. */
181
202
  list(filters?: RefundFilters): Promise<Refund[]>;
182
203
  }
@@ -230,4 +251,4 @@ declare const BASE_URL = "https://api.agentmall.sh";
230
251
  declare const SERVICE_FEE_CENTS = 150;
231
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"];
232
253
 
233
- 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, RateLimitError, type Refund, type RefundFilters, SERVICE_FEE_CENTS, SUPPORTED_RETAILERS, ValidationError, type VariantSelection, 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
@@ -78,6 +78,10 @@ var AgentMall = class {
78
78
  this.refunds = new AgentMallRefunds(this);
79
79
  }
80
80
  /** @internal */
81
+ hasApiSecret() {
82
+ return Boolean(this.apiSecret);
83
+ }
84
+ /** @internal */
81
85
  async request(method, path, options) {
82
86
  const url = new URL(`${this.baseUrl}${path}`);
83
87
  if (options?.params) {
@@ -157,8 +161,19 @@ var AgentMallPurchases = class {
157
161
  }
158
162
  });
159
163
  }
160
- /** Get a single purchase by ID. Requires apiSecret. */
161
- async get(id) {
164
+ /** Get a single purchase by ID using either operator auth or a buyer token. */
165
+ async get(id, options) {
166
+ if (options?.buyerToken) {
167
+ return this.client.request("POST", "/api/purchases/status", {
168
+ body: {
169
+ id,
170
+ buyer_token: options.buyerToken
171
+ }
172
+ });
173
+ }
174
+ if (!this.client.hasApiSecret()) {
175
+ throw new Error("buyerToken or apiSecret is required for this endpoint");
176
+ }
162
177
  return this.client.request("GET", "/api/purchases", {
163
178
  auth: true,
164
179
  params: { id }
@@ -206,6 +221,33 @@ var AgentMallRefunds = class {
206
221
  params: { id }
207
222
  });
208
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
+ }
209
251
  /** List refunds. Requires apiSecret. */
210
252
  async list(filters) {
211
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.1",
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",