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 +325 -73
- package/dist/index.cjs +44 -2
- package/dist/index.d.cts +26 -5
- package/dist/index.d.ts +26 -5
- package/dist/index.js +44 -2
- package/package.json +1 -1
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
|
|
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
|
|
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(
|
|
365
|
+
function displayStatus(purchase) {
|
|
325
366
|
console.log();
|
|
326
|
-
console.log(` \x1B[1mOrder ${
|
|
327
|
-
console.log(` Status: ${
|
|
328
|
-
if (
|
|
329
|
-
for (const item of
|
|
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 (
|
|
335
|
-
console.log(` Final total: ${formatCents(
|
|
375
|
+
if (purchase.finalTotal) {
|
|
376
|
+
console.log(` Final total: ${formatCents(purchase.finalTotal)}`);
|
|
336
377
|
}
|
|
337
|
-
if (
|
|
338
|
-
console.log(
|
|
378
|
+
if (purchase.failureReason) {
|
|
379
|
+
console.log(
|
|
380
|
+
` \x1B[31mFailure: ${FAILURE_MESSAGES[purchase.failureReason] ?? purchase.failureReason}\x1B[0m`
|
|
381
|
+
);
|
|
339
382
|
}
|
|
340
|
-
if (
|
|
341
|
-
console.log(` Shipping: ${
|
|
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
|
-
|
|
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
|
|
493
|
+
message: `Max budget in cents (e.g. ${suggested} = ${formatCents2(suggested)})`,
|
|
422
494
|
default: String(suggested),
|
|
423
495
|
validate: (v) => {
|
|
424
|
-
const n =
|
|
425
|
-
if (
|
|
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
|
-
|
|
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
|
|
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
|
|
437
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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("
|
|
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
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const result = await client.purchases.get(
|
|
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
|
|
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 +
|
|
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
|
|
163
|
-
get(id: string
|
|
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 +
|
|
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
|
|
163
|
-
get(id: string
|
|
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
|
|
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(
|