agentmall 0.1.21 → 0.1.22

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
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/cli/index.ts
4
4
  import { spawn } from "child_process";
5
+ import { privateKeyToAccount } from "viem/accounts";
5
6
 
6
7
  // src/constants.ts
7
8
  var BASE_URL = "https://api.agentmall.sh";
@@ -142,6 +143,25 @@ var AgentMall = class {
142
143
  return await response.json();
143
144
  }
144
145
  };
146
+ async function createStoreAccountHeaders(client, account) {
147
+ const challenge = await client.request(
148
+ "POST",
149
+ "/api/managed-accounts/challenge",
150
+ {
151
+ body: {
152
+ address: account.address
153
+ }
154
+ }
155
+ );
156
+ const signature = await account.signMessage({
157
+ message: challenge.message
158
+ });
159
+ return {
160
+ "X-AgentMall-Wallet-Address": account.address,
161
+ "X-AgentMall-Auth-Challenge": challenge.challenge,
162
+ "X-AgentMall-Auth-Signature": signature
163
+ };
164
+ }
145
165
  var AgentMallProducts = class {
146
166
  constructor(client) {
147
167
  this.client = client;
@@ -161,18 +181,20 @@ var AgentMallPurchases = class {
161
181
  * Create a purchase. The first call returns a 402 MPP payment challenge.
162
182
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
163
183
  */
164
- async create(body) {
184
+ async create(body, options) {
165
185
  const idempotencyKey = body.idempotency_key ?? createIdempotencyKey();
166
186
  const storeAccountId = body.store_account_id ?? body.retailer_credentials_id;
187
+ const headers = {
188
+ ...storeAccountId && options?.account ? await createStoreAccountHeaders(this.client, options.account) : {},
189
+ "X-Idempotency-Key": idempotencyKey
190
+ };
167
191
  return this.client.request("POST", "/api/purchases", {
168
192
  body: {
169
193
  ...body,
170
194
  ...storeAccountId ? { retailer_credentials_id: storeAccountId } : {},
171
195
  idempotency_key: idempotencyKey
172
196
  },
173
- headers: {
174
- "X-Idempotency-Key": idempotencyKey
175
- }
197
+ headers
176
198
  });
177
199
  }
178
200
  /** Get a single purchase by ID using either operator auth or a buyer token. */
@@ -285,23 +307,7 @@ var AgentMallStoreAccounts = class {
285
307
  this.client = client;
286
308
  }
287
309
  async walletHeaders(account) {
288
- const challenge = await this.client.request(
289
- "POST",
290
- "/api/managed-accounts/challenge",
291
- {
292
- body: {
293
- address: account.address
294
- }
295
- }
296
- );
297
- const signature = await account.signMessage({
298
- message: challenge.message
299
- });
300
- return {
301
- "X-AgentMall-Wallet-Address": account.address,
302
- "X-AgentMall-Auth-Challenge": challenge.challenge,
303
- "X-AgentMall-Auth-Signature": signature
304
- };
310
+ return createStoreAccountHeaders(this.client, account);
305
311
  }
306
312
  /** List your store accounts. Requires either apiSecret or a wallet signer. */
307
313
  async list(options) {
@@ -702,6 +708,13 @@ function displayOrderSummary(body) {
702
708
  line(
703
709
  `${addr.city}, ${addr.state ?? ""} ${addr.postal_code} ${addr.country}`
704
710
  );
711
+ if (body.store_account_id || body.retailer_credentials_id) {
712
+ gap();
713
+ kv(
714
+ "Store account",
715
+ `${c.bold}${body.store_account_id ?? body.retailer_credentials_id}${c.reset}`
716
+ );
717
+ }
705
718
  }
706
719
  function displayOrderResult(order) {
707
720
  gap();
@@ -784,6 +797,35 @@ function displayRefund(refund2) {
784
797
  }
785
798
  gap();
786
799
  }
800
+ function formatTimestamp(value) {
801
+ return new Date(value).toLocaleString();
802
+ }
803
+ function displayStoreAccount(account, opts) {
804
+ section(opts?.title ?? `Store Account ${account.short_id}`);
805
+ kv("Short ID", account.short_id);
806
+ kv("Retailer", account.retailer ?? "default");
807
+ kv("Email", account.email);
808
+ kv("2FA", account.has_totp ? "configured" : "not set", {
809
+ color: account.has_totp ? c.green : c.yellow
810
+ });
811
+ kv("Forwarding", account.has_forwarding ? "ready" : "pending", {
812
+ color: account.has_forwarding ? c.green : c.yellow
813
+ });
814
+ kv("Forward Email", account.forwarding_email, { dim: true });
815
+ kv("Created", formatTimestamp(account.created_at), { dim: true });
816
+ kv("Updated", formatTimestamp(account.updated_at), { dim: true });
817
+ }
818
+ function displayStoreAccounts(accounts) {
819
+ section("Store Accounts");
820
+ if (accounts.length === 0) {
821
+ line("No store accounts found.");
822
+ return;
823
+ }
824
+ kv("Total", String(accounts.length));
825
+ for (const account of accounts) {
826
+ displayStoreAccount(account);
827
+ }
828
+ }
787
829
 
788
830
  // src/cli/prompts.ts
789
831
  import { input, confirm, select } from "@inquirer/prompts";
@@ -977,6 +1019,17 @@ function parseTempoBalance(balance) {
977
1019
  function formatUsdcNumber(amount) {
978
1020
  return amount.toFixed(6).replace(/\.?0+$/, "");
979
1021
  }
1022
+ function createIdempotencyKey2() {
1023
+ return globalThis.crypto?.randomUUID?.() ?? `agentmall_${Date.now()}_${Math.random().toString(16).slice(2)}`;
1024
+ }
1025
+ function normalizeAddress(value) {
1026
+ const trimmed = value?.trim();
1027
+ return trimmed ? trimmed.toLowerCase() : void 0;
1028
+ }
1029
+ function formatAddress(address) {
1030
+ if (!address) return null;
1031
+ return `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
1032
+ }
980
1033
  async function runCommandCapture(command2, args2, envOverrides) {
981
1034
  return await new Promise((resolve, reject) => {
982
1035
  const child = spawn(command2, args2, {
@@ -1029,6 +1082,15 @@ async function readTempoWhoami() {
1029
1082
  return null;
1030
1083
  }
1031
1084
  }
1085
+ async function readTempoKeys() {
1086
+ try {
1087
+ const result = await runCommandCapture("tempo", ["wallet", "keys", "-j"]);
1088
+ if (result.code !== 0) return null;
1089
+ return JSON.parse(result.stdout);
1090
+ } catch {
1091
+ return null;
1092
+ }
1093
+ }
1032
1094
  async function ensureTempoInstalled() {
1033
1095
  try {
1034
1096
  const result = await runCommandCapture("tempo", ["--version"]);
@@ -1061,6 +1123,94 @@ function formatUsdAmount(cents) {
1061
1123
  function sleep(ms) {
1062
1124
  return new Promise((resolve) => setTimeout(resolve, ms));
1063
1125
  }
1126
+ async function getTempoSignerContext(wallet) {
1127
+ const readyWallet = wallet ?? await ensureTempoReady();
1128
+ const fallbackKey = (await readTempoKeys())?.keys?.[0];
1129
+ const tempoKey = readyWallet.key ?? fallbackKey;
1130
+ const privateKey = tempoKey?.key?.trim();
1131
+ const signerAddress = normalizeAddress(tempoKey?.address);
1132
+ if (!privateKey || !signerAddress) {
1133
+ throw new Error(
1134
+ "Tempo wallet does not expose a signing key. Run `tempo wallet login` again."
1135
+ );
1136
+ }
1137
+ const account = privateKeyToAccount(privateKey);
1138
+ if (normalizeAddress(account.address) !== signerAddress) {
1139
+ throw new Error("Tempo signing key does not match the signer address.");
1140
+ }
1141
+ return {
1142
+ account,
1143
+ signerAddress,
1144
+ walletAddress: normalizeAddress(readyWallet.wallet) ?? normalizeAddress(tempoKey?.wallet_address)
1145
+ };
1146
+ }
1147
+ async function createStoreAccountAuthHeaders(account) {
1148
+ const response = await fetch(`${BASE_URL}/api/managed-accounts/challenge`, {
1149
+ method: "POST",
1150
+ headers: {
1151
+ "Content-Type": "application/json"
1152
+ },
1153
+ body: JSON.stringify({
1154
+ address: account.address
1155
+ })
1156
+ });
1157
+ const text = await response.text();
1158
+ let payload = null;
1159
+ try {
1160
+ payload = text ? JSON.parse(text) : null;
1161
+ } catch {
1162
+ payload = text || null;
1163
+ }
1164
+ if (!response.ok) {
1165
+ const message = typeof payload === "string" ? payload : payload?.error ?? "Unable to authorize store account";
1166
+ throw new Error(message);
1167
+ }
1168
+ const challenge = payload;
1169
+ if (!challenge?.challenge || !challenge.message) {
1170
+ throw new Error("Store account authorization challenge was invalid.");
1171
+ }
1172
+ const signature = await account.signMessage({
1173
+ message: challenge.message
1174
+ });
1175
+ return {
1176
+ "X-AgentMall-Wallet-Address": account.address,
1177
+ "X-AgentMall-Auth-Challenge": challenge.challenge,
1178
+ "X-AgentMall-Auth-Signature": signature
1179
+ };
1180
+ }
1181
+ function normalizeOptional(value) {
1182
+ const trimmed = value?.trim();
1183
+ return trimmed ? trimmed : void 0;
1184
+ }
1185
+ async function promptStoreAccountFields(mode) {
1186
+ const { input: input2 } = await import("@inquirer/prompts");
1187
+ const email = mode === "create" ? await input2({
1188
+ message: "Store account email",
1189
+ validate: (value) => EMAIL_PATTERN.test(value.trim()) ? true : "A valid email is required"
1190
+ }) : await input2({
1191
+ message: "Email (leave blank to keep current)",
1192
+ default: "",
1193
+ validate: (value) => !value.trim() || EMAIL_PATTERN.test(value.trim()) ? true : "Enter a valid email or leave blank"
1194
+ });
1195
+ const retailer = await input2({
1196
+ message: mode === "create" ? "Retailer" : "Retailer (leave blank to keep current)",
1197
+ default: mode === "create" ? "amazon" : ""
1198
+ });
1199
+ const password = await input2({
1200
+ message: mode === "create" ? "Password (optional)" : "Password (leave blank to keep current)",
1201
+ default: ""
1202
+ });
1203
+ const totpSecret = await input2({
1204
+ message: mode === "create" ? "TOTP secret (optional)" : "TOTP secret (leave blank to keep current)",
1205
+ default: ""
1206
+ });
1207
+ return {
1208
+ ...normalizeOptional(email) ? { email: normalizeOptional(email) } : {},
1209
+ ...normalizeOptional(retailer) ? { retailer: normalizeOptional(retailer) } : {},
1210
+ ...normalizeOptional(password) ? { password: normalizeOptional(password) } : {},
1211
+ ...normalizeOptional(totpSecret) ? { totpSecret: normalizeOptional(totpSecret) } : {}
1212
+ };
1213
+ }
1064
1214
  function getSelectedVariantUnitPriceCents(selectedVariants) {
1065
1215
  const highestVariantPrice = selectedVariants?.reduce((highest, variant) => {
1066
1216
  const variantPriceCents = typeof variant.price === "number" ? Math.round(variant.price * 100) : 0;
@@ -1110,23 +1260,25 @@ async function ensureSufficientBalance(requiredCents, wallet) {
1110
1260
  `Still not enough USDC after funding: have ${formatUsdcNumber(available)}, need ${formatUsdAmount(requiredCents)}.`
1111
1261
  );
1112
1262
  }
1113
- async function createPurchaseWithTempo(body) {
1114
- const totalChargeCents = body.max_budget + SERVICE_FEE_CENTS;
1115
- const result = await runCommandCapture(
1116
- "tempo",
1117
- [
1118
- "request",
1119
- "-s",
1120
- "-X",
1121
- "POST",
1122
- "--json",
1123
- JSON.stringify(body),
1124
- `${BASE_URL}/api/purchases`
1125
- ],
1126
- {
1127
- TEMPO_MAX_SPEND: formatUsdAmount(totalChargeCents)
1128
- }
1129
- );
1263
+ async function createPurchaseWithTempo(body, options) {
1264
+ const idempotencyKey = body.idempotency_key?.trim() || createIdempotencyKey2();
1265
+ const requestBody = {
1266
+ ...body,
1267
+ idempotency_key: idempotencyKey
1268
+ };
1269
+ const totalChargeCents = requestBody.max_budget + SERVICE_FEE_CENTS;
1270
+ const headers = {
1271
+ "X-Idempotency-Key": idempotencyKey,
1272
+ ...options?.headers ?? {}
1273
+ };
1274
+ const args2 = ["request", "-s", "-X", "POST"];
1275
+ for (const [key, value] of Object.entries(headers)) {
1276
+ args2.push("-H", `${key}: ${value}`);
1277
+ }
1278
+ args2.push("--json", JSON.stringify(requestBody), `${BASE_URL}/api/purchases`);
1279
+ const result = await runCommandCapture("tempo", args2, {
1280
+ TEMPO_MAX_SPEND: formatUsdAmount(totalChargeCents)
1281
+ });
1130
1282
  if (result.code !== 0) {
1131
1283
  const raw = result.stderr.trim() || result.stdout.trim();
1132
1284
  const payload = raw ? (() => {
@@ -1147,7 +1299,7 @@ async function createPurchaseWithTempo(body) {
1147
1299
  );
1148
1300
  }
1149
1301
  }
1150
- async function buy(urlArg) {
1302
+ async function buy(urlArg, options) {
1151
1303
  try {
1152
1304
  banner();
1153
1305
  gap();
@@ -1193,6 +1345,7 @@ async function buy(urlArg) {
1193
1345
  message: "Email for order updates",
1194
1346
  validate: (value) => EMAIL_PATTERN.test(value.trim()) ? true : "A valid email is required"
1195
1347
  });
1348
+ const storeAccountId = normalizeOptional(options?.storeAccountId);
1196
1349
  const body = {
1197
1350
  items: [
1198
1351
  {
@@ -1203,7 +1356,9 @@ async function buy(urlArg) {
1203
1356
  ],
1204
1357
  delivery_address: address,
1205
1358
  max_budget: maxBudget,
1206
- buyer_email: buyerEmail
1359
+ buyer_email: buyerEmail,
1360
+ ...storeAccountId ? { store_account_id: storeAccountId } : {},
1361
+ idempotency_key: createIdempotencyKey2()
1207
1362
  };
1208
1363
  displayOrderSummary(body);
1209
1364
  gap();
@@ -1227,17 +1382,32 @@ async function buy(urlArg) {
1227
1382
  return;
1228
1383
  }
1229
1384
  const totalChargeCents = maxBudget + SERVICE_FEE_CENTS;
1230
- const wallet = await spin("Connecting wallet", () => ensureTempoReady());
1231
- await ensureSufficientBalance(totalChargeCents, wallet);
1385
+ const initialWallet = await spin("Connecting wallet", () => ensureTempoReady());
1386
+ const wallet = await ensureSufficientBalance(totalChargeCents, initialWallet);
1232
1387
  if (wallet.wallet) {
1233
1388
  const addr = wallet.wallet;
1234
1389
  const short = `${addr.slice(0, 6)}\u2026${addr.slice(-4)}`;
1235
1390
  const bal = wallet.balance?.available ? ` \xB7 ${wallet.balance.available} ${wallet.balance?.symbol ?? "USDC"}` : "";
1236
1391
  muted(`Wallet ${short}${bal}`);
1237
1392
  }
1393
+ let purchaseHeaders;
1394
+ if (storeAccountId) {
1395
+ const signer = await spin("Authorizing store account", async () => {
1396
+ const context = await getTempoSignerContext(wallet);
1397
+ const headers = await createStoreAccountAuthHeaders(context.account);
1398
+ return { headers, context };
1399
+ });
1400
+ purchaseHeaders = signer.headers;
1401
+ const signerLine = formatAddress(signer.context.signerAddress);
1402
+ muted(
1403
+ signerLine ? `Store account ${storeAccountId} authorized by signer ${signerLine}` : `Store account ${storeAccountId} authorized`
1404
+ );
1405
+ }
1238
1406
  const order = await spin(
1239
1407
  "Placing order",
1240
- () => createPurchaseWithTempo(body)
1408
+ () => createPurchaseWithTempo(body, {
1409
+ ...purchaseHeaders ? { headers: purchaseHeaders } : {}
1410
+ })
1241
1411
  );
1242
1412
  if (order.buyerToken) {
1243
1413
  await saveBuyerToken(order.id, order.buyerToken);
@@ -1347,6 +1517,120 @@ async function onboard() {
1347
1517
  );
1348
1518
  gap();
1349
1519
  }
1520
+ async function storeAccountsList() {
1521
+ banner();
1522
+ gap();
1523
+ const wallet = await spin("Connecting wallet", () => ensureTempoReady());
1524
+ const signer = await getTempoSignerContext(wallet);
1525
+ const client = new AgentMall();
1526
+ const walletLabel = formatAddress(signer.walletAddress);
1527
+ const signerLabel = formatAddress(signer.signerAddress);
1528
+ if (walletLabel || signerLabel) {
1529
+ muted(
1530
+ `Using ${walletLabel ? `wallet ${walletLabel}` : "Tempo wallet"}${walletLabel && signerLabel ? " \xB7 " : ""}${signerLabel ? `signer ${signerLabel}` : ""}`
1531
+ );
1532
+ gap();
1533
+ }
1534
+ const accounts = await spin(
1535
+ "Loading store accounts",
1536
+ () => client.storeAccounts.list({ account: signer.account })
1537
+ );
1538
+ displayStoreAccounts(accounts);
1539
+ gap();
1540
+ }
1541
+ async function storeAccountsCreate(options) {
1542
+ banner();
1543
+ gap();
1544
+ const fields = {
1545
+ ...normalizeOptional(options?.email) ? { email: normalizeOptional(options?.email) } : {},
1546
+ ...normalizeOptional(options?.retailer) ? { retailer: normalizeOptional(options?.retailer) } : {},
1547
+ ...normalizeOptional(options?.password) ? { password: normalizeOptional(options?.password) } : {},
1548
+ ...normalizeOptional(options?.totpSecret) ? { totpSecret: normalizeOptional(options?.totpSecret) } : {}
1549
+ };
1550
+ const prompted = fields.email && fields.retailer !== void 0 ? fields : {
1551
+ ...fields,
1552
+ ...await promptStoreAccountFields("create")
1553
+ };
1554
+ if (!prompted.email) {
1555
+ throw new Error("Email is required.");
1556
+ }
1557
+ const body = {
1558
+ email: prompted.email,
1559
+ ...prompted.retailer ? { retailer: prompted.retailer } : {},
1560
+ ...prompted.password ? { password: prompted.password } : {},
1561
+ ...prompted.totpSecret ? { totp_secret: prompted.totpSecret } : {}
1562
+ };
1563
+ const wallet = await spin("Connecting wallet", () => ensureTempoReady());
1564
+ const signer = await getTempoSignerContext(wallet);
1565
+ const client = new AgentMall();
1566
+ const account = await spin(
1567
+ "Creating store account",
1568
+ () => client.storeAccounts.create(body, { account: signer.account })
1569
+ );
1570
+ displayStoreAccount(account, { title: "Store Account Created" });
1571
+ gap();
1572
+ line(
1573
+ `Use ${c.bold}store_account_id: "${account.short_id}"${c.reset} on purchases.`
1574
+ );
1575
+ gap();
1576
+ }
1577
+ async function storeAccountsUpdate(shortId, options) {
1578
+ banner();
1579
+ gap();
1580
+ if (!shortId.trim()) {
1581
+ throw new Error("short_id is required.");
1582
+ }
1583
+ let fields = {
1584
+ ...normalizeOptional(options?.email) ? { email: normalizeOptional(options?.email) } : {},
1585
+ ...normalizeOptional(options?.retailer) ? { retailer: normalizeOptional(options?.retailer) } : {},
1586
+ ...normalizeOptional(options?.password) ? { password: normalizeOptional(options?.password) } : {},
1587
+ ...normalizeOptional(options?.totpSecret) ? { totpSecret: normalizeOptional(options?.totpSecret) } : {}
1588
+ };
1589
+ if (Object.keys(fields).length === 0) {
1590
+ fields = await promptStoreAccountFields("update");
1591
+ }
1592
+ if (Object.keys(fields).length === 0) {
1593
+ throw new Error("No updates provided.");
1594
+ }
1595
+ const body = {
1596
+ short_id: shortId.trim(),
1597
+ ...fields.email ? { email: fields.email } : {},
1598
+ ...fields.retailer ? { retailer: fields.retailer } : {},
1599
+ ...fields.password ? { password: fields.password } : {},
1600
+ ...fields.totpSecret ? { totp_secret: fields.totpSecret } : {}
1601
+ };
1602
+ const wallet = await spin("Connecting wallet", () => ensureTempoReady());
1603
+ const signer = await getTempoSignerContext(wallet);
1604
+ const client = new AgentMall();
1605
+ const account = await spin(
1606
+ "Updating store account",
1607
+ () => client.storeAccounts.update(body, { account: signer.account })
1608
+ );
1609
+ displayStoreAccount(account, { title: "Store Account Updated" });
1610
+ gap();
1611
+ }
1612
+ async function storeAccountsDelete(shortId, options) {
1613
+ banner();
1614
+ gap();
1615
+ if (!shortId.trim()) {
1616
+ throw new Error("short_id is required.");
1617
+ }
1618
+ const confirmed = options?.yes ?? await promptConfirm(`Delete store account ${shortId.trim()}?`);
1619
+ if (!confirmed) {
1620
+ muted("Cancelled.");
1621
+ return;
1622
+ }
1623
+ const wallet = await spin("Connecting wallet", () => ensureTempoReady());
1624
+ const signer = await getTempoSignerContext(wallet);
1625
+ const client = new AgentMall();
1626
+ await spin(
1627
+ "Deleting store account",
1628
+ () => client.storeAccounts.delete(shortId.trim(), { account: signer.account })
1629
+ );
1630
+ section("Store Account Deleted");
1631
+ line(`${c.bold}${shortId.trim()}${c.reset} was removed.`);
1632
+ gap();
1633
+ }
1350
1634
 
1351
1635
  // src/cli.ts
1352
1636
  var c2 = {
@@ -1358,22 +1642,78 @@ var c2 = {
1358
1642
  };
1359
1643
  var args = process.argv.slice(2);
1360
1644
  var command = args[0];
1361
- function readBuyerTokenFlag(values) {
1362
- const inline = values.find((value) => value.startsWith("--buyer-token="));
1645
+ function readFlagValue(values, flag) {
1646
+ const inline = values.find((value2) => value2.startsWith(`${flag}=`));
1363
1647
  if (inline) {
1364
- const token2 = inline.slice("--buyer-token=".length).trim();
1365
- return token2 || void 0;
1648
+ const value2 = inline.slice(`${flag}=`.length).trim();
1649
+ return value2 || void 0;
1366
1650
  }
1367
- const index = values.indexOf("--buyer-token");
1651
+ const index = values.indexOf(flag);
1368
1652
  if (index === -1) return void 0;
1369
- const token = values[index + 1]?.trim();
1370
- return token || void 0;
1653
+ const value = values[index + 1]?.trim();
1654
+ return value || void 0;
1655
+ }
1656
+ function hasFlag(values, flag) {
1657
+ return values.includes(flag);
1658
+ }
1659
+ function readBuyerTokenFlag(values) {
1660
+ return readFlagValue(values, "--buyer-token");
1661
+ }
1662
+ function readStoreAccountFlag(values) {
1663
+ return readFlagValue(values, "--store-account");
1664
+ }
1665
+ async function handleStoreAccounts(values) {
1666
+ const subcommand = values[0];
1667
+ const rest = values.slice(1);
1668
+ switch (subcommand) {
1669
+ case "list":
1670
+ await storeAccountsList();
1671
+ break;
1672
+ case "create":
1673
+ await storeAccountsCreate({
1674
+ email: readFlagValue(rest, "--email"),
1675
+ retailer: readFlagValue(rest, "--retailer"),
1676
+ password: readFlagValue(rest, "--password"),
1677
+ totpSecret: readFlagValue(rest, "--totp-secret")
1678
+ });
1679
+ break;
1680
+ case "update":
1681
+ if (!rest[0]) {
1682
+ console.error(
1683
+ "Usage: agentmall store-accounts update <short_id> [--email <email>] [--retailer <name>] [--password <password>] [--totp-secret <secret>]"
1684
+ );
1685
+ process.exit(1);
1686
+ }
1687
+ await storeAccountsUpdate(rest[0], {
1688
+ email: readFlagValue(rest.slice(1), "--email"),
1689
+ retailer: readFlagValue(rest.slice(1), "--retailer"),
1690
+ password: readFlagValue(rest.slice(1), "--password"),
1691
+ totpSecret: readFlagValue(rest.slice(1), "--totp-secret")
1692
+ });
1693
+ break;
1694
+ case "delete":
1695
+ if (!rest[0]) {
1696
+ console.error("Usage: agentmall store-accounts delete <short_id> [--yes]");
1697
+ process.exit(1);
1698
+ }
1699
+ await storeAccountsDelete(rest[0], {
1700
+ yes: hasFlag(rest.slice(1), "--yes")
1701
+ });
1702
+ break;
1703
+ default:
1704
+ console.error(
1705
+ "Usage: agentmall store-accounts <list|create|update|delete> ..."
1706
+ );
1707
+ process.exit(1);
1708
+ }
1371
1709
  }
1372
1710
  async function main() {
1373
1711
  try {
1374
1712
  switch (command) {
1375
1713
  case "buy":
1376
- await buy(args[1]);
1714
+ await buy(args[1], {
1715
+ storeAccountId: readStoreAccountFlag(args.slice(2))
1716
+ });
1377
1717
  break;
1378
1718
  case "status":
1379
1719
  if (!args[1]) {
@@ -1393,6 +1733,9 @@ async function main() {
1393
1733
  case "setup":
1394
1734
  await onboard();
1395
1735
  break;
1736
+ case "store-accounts":
1737
+ await handleStoreAccounts(args.slice(1));
1738
+ break;
1396
1739
  case "help":
1397
1740
  case "--help":
1398
1741
  case "-h":
@@ -1431,6 +1774,9 @@ function printHelp() {
1431
1774
  console.log(
1432
1775
  ` ${c2.cyan}agentmall buy${c2.reset} <url> ${c2.dim}Buy a product${c2.reset}`
1433
1776
  );
1777
+ console.log(
1778
+ ` ${c2.cyan}agentmall store-accounts${c2.reset} ${c2.dim}Manage your store accounts${c2.reset}`
1779
+ );
1434
1780
  console.log(
1435
1781
  ` ${c2.cyan}agentmall status${c2.reset} <id> ${c2.dim}Check order status${c2.reset}`
1436
1782
  );
@@ -1448,6 +1794,12 @@ function printHelp() {
1448
1794
  console.log(
1449
1795
  ` ${c2.dim}$${c2.reset} npx agentmall https://amazon.com/dp/B0DWTMJHCG`
1450
1796
  );
1797
+ console.log(
1798
+ ` ${c2.dim}$${c2.reset} npx agentmall buy <url> --store-account zn_acct_abc123`
1799
+ );
1800
+ console.log(
1801
+ ` ${c2.dim}$${c2.reset} npx agentmall store-accounts create`
1802
+ );
1451
1803
  console.log(` ${c2.dim}$${c2.reset} npx agentmall status pur_abc123`);
1452
1804
  console.log(
1453
1805
  ` ${c2.dim}$${c2.reset} npx agentmall status pur_abc123 --buyer-token amtk_...`
package/dist/index.cjs CHANGED
@@ -182,6 +182,25 @@ var AgentMall = class {
182
182
  return await response.json();
183
183
  }
184
184
  };
185
+ async function createStoreAccountHeaders(client, account) {
186
+ const challenge = await client.request(
187
+ "POST",
188
+ "/api/managed-accounts/challenge",
189
+ {
190
+ body: {
191
+ address: account.address
192
+ }
193
+ }
194
+ );
195
+ const signature = await account.signMessage({
196
+ message: challenge.message
197
+ });
198
+ return {
199
+ "X-AgentMall-Wallet-Address": account.address,
200
+ "X-AgentMall-Auth-Challenge": challenge.challenge,
201
+ "X-AgentMall-Auth-Signature": signature
202
+ };
203
+ }
185
204
  var AgentMallProducts = class {
186
205
  constructor(client) {
187
206
  this.client = client;
@@ -201,18 +220,20 @@ var AgentMallPurchases = class {
201
220
  * Create a purchase. The first call returns a 402 MPP payment challenge.
202
221
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
203
222
  */
204
- async create(body) {
223
+ async create(body, options) {
205
224
  const idempotencyKey = body.idempotency_key ?? createIdempotencyKey();
206
225
  const storeAccountId = body.store_account_id ?? body.retailer_credentials_id;
226
+ const headers = {
227
+ ...storeAccountId && options?.account ? await createStoreAccountHeaders(this.client, options.account) : {},
228
+ "X-Idempotency-Key": idempotencyKey
229
+ };
207
230
  return this.client.request("POST", "/api/purchases", {
208
231
  body: {
209
232
  ...body,
210
233
  ...storeAccountId ? { retailer_credentials_id: storeAccountId } : {},
211
234
  idempotency_key: idempotencyKey
212
235
  },
213
- headers: {
214
- "X-Idempotency-Key": idempotencyKey
215
- }
236
+ headers
216
237
  });
217
238
  }
218
239
  /** Get a single purchase by ID using either operator auth or a buyer token. */
@@ -325,23 +346,7 @@ var AgentMallStoreAccounts = class {
325
346
  this.client = client;
326
347
  }
327
348
  async walletHeaders(account) {
328
- const challenge = await this.client.request(
329
- "POST",
330
- "/api/managed-accounts/challenge",
331
- {
332
- body: {
333
- address: account.address
334
- }
335
- }
336
- );
337
- const signature = await account.signMessage({
338
- message: challenge.message
339
- });
340
- return {
341
- "X-AgentMall-Wallet-Address": account.address,
342
- "X-AgentMall-Auth-Challenge": challenge.challenge,
343
- "X-AgentMall-Auth-Signature": signature
344
- };
349
+ return createStoreAccountHeaders(this.client, account);
345
350
  }
346
351
  /** List your store accounts. Requires either apiSecret or a wallet signer. */
347
352
  async list(options) {
@@ -394,7 +399,7 @@ async function purchase(config) {
394
399
  baseUrl: baseUrl ?? BASE_URL,
395
400
  fetch: mppx.fetch.bind(mppx)
396
401
  });
397
- return client.purchases.create(body);
402
+ return client.purchases.create(body, { account });
398
403
  }
399
404
  // Annotate the CommonJS export names for ESM import in node:
400
405
  0 && (module.exports = {
package/dist/index.d.cts CHANGED
@@ -220,7 +220,9 @@ declare class AgentMallPurchases {
220
220
  * Create a purchase. The first call returns a 402 MPP payment challenge.
221
221
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
222
222
  */
223
- create(body: CreatePurchaseRequest): Promise<CreatePurchaseResponse>;
223
+ create(body: CreatePurchaseRequest, options?: {
224
+ account?: WalletAccount;
225
+ }): Promise<CreatePurchaseResponse>;
224
226
  /** Get a single purchase by ID using either operator auth or a buyer token. */
225
227
  get(id: string, options?: {
226
228
  buyerToken?: string;
@@ -273,7 +275,7 @@ type AgentMallManagedAccounts = AgentMallStoreAccounts;
273
275
 
274
276
  type PurchaseConfig = CreatePurchaseRequest & {
275
277
  /** viem Account for MPP payment (from privateKeyToAccount or mppx resolveAccount). */
276
- account: unknown;
278
+ account: WalletAccount;
277
279
  /** API base URL. Defaults to https://api.agentmall.sh */
278
280
  baseUrl?: string;
279
281
  };
package/dist/index.d.ts CHANGED
@@ -220,7 +220,9 @@ declare class AgentMallPurchases {
220
220
  * Create a purchase. The first call returns a 402 MPP payment challenge.
221
221
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
222
222
  */
223
- create(body: CreatePurchaseRequest): Promise<CreatePurchaseResponse>;
223
+ create(body: CreatePurchaseRequest, options?: {
224
+ account?: WalletAccount;
225
+ }): Promise<CreatePurchaseResponse>;
224
226
  /** Get a single purchase by ID using either operator auth or a buyer token. */
225
227
  get(id: string, options?: {
226
228
  buyerToken?: string;
@@ -273,7 +275,7 @@ type AgentMallManagedAccounts = AgentMallStoreAccounts;
273
275
 
274
276
  type PurchaseConfig = CreatePurchaseRequest & {
275
277
  /** viem Account for MPP payment (from privateKeyToAccount or mppx resolveAccount). */
276
- account: unknown;
278
+ account: WalletAccount;
277
279
  /** API base URL. Defaults to https://api.agentmall.sh */
278
280
  baseUrl?: string;
279
281
  };
package/dist/index.js CHANGED
@@ -137,6 +137,25 @@ var AgentMall = class {
137
137
  return await response.json();
138
138
  }
139
139
  };
140
+ async function createStoreAccountHeaders(client, account) {
141
+ const challenge = await client.request(
142
+ "POST",
143
+ "/api/managed-accounts/challenge",
144
+ {
145
+ body: {
146
+ address: account.address
147
+ }
148
+ }
149
+ );
150
+ const signature = await account.signMessage({
151
+ message: challenge.message
152
+ });
153
+ return {
154
+ "X-AgentMall-Wallet-Address": account.address,
155
+ "X-AgentMall-Auth-Challenge": challenge.challenge,
156
+ "X-AgentMall-Auth-Signature": signature
157
+ };
158
+ }
140
159
  var AgentMallProducts = class {
141
160
  constructor(client) {
142
161
  this.client = client;
@@ -156,18 +175,20 @@ var AgentMallPurchases = class {
156
175
  * Create a purchase. The first call returns a 402 MPP payment challenge.
157
176
  * Use mppx-wrapped fetch to handle payment automatically, or catch PaymentRequiredError.
158
177
  */
159
- async create(body) {
178
+ async create(body, options) {
160
179
  const idempotencyKey = body.idempotency_key ?? createIdempotencyKey();
161
180
  const storeAccountId = body.store_account_id ?? body.retailer_credentials_id;
181
+ const headers = {
182
+ ...storeAccountId && options?.account ? await createStoreAccountHeaders(this.client, options.account) : {},
183
+ "X-Idempotency-Key": idempotencyKey
184
+ };
162
185
  return this.client.request("POST", "/api/purchases", {
163
186
  body: {
164
187
  ...body,
165
188
  ...storeAccountId ? { retailer_credentials_id: storeAccountId } : {},
166
189
  idempotency_key: idempotencyKey
167
190
  },
168
- headers: {
169
- "X-Idempotency-Key": idempotencyKey
170
- }
191
+ headers
171
192
  });
172
193
  }
173
194
  /** Get a single purchase by ID using either operator auth or a buyer token. */
@@ -280,23 +301,7 @@ var AgentMallStoreAccounts = class {
280
301
  this.client = client;
281
302
  }
282
303
  async walletHeaders(account) {
283
- const challenge = await this.client.request(
284
- "POST",
285
- "/api/managed-accounts/challenge",
286
- {
287
- body: {
288
- address: account.address
289
- }
290
- }
291
- );
292
- const signature = await account.signMessage({
293
- message: challenge.message
294
- });
295
- return {
296
- "X-AgentMall-Wallet-Address": account.address,
297
- "X-AgentMall-Auth-Challenge": challenge.challenge,
298
- "X-AgentMall-Auth-Signature": signature
299
- };
304
+ return createStoreAccountHeaders(this.client, account);
300
305
  }
301
306
  /** List your store accounts. Requires either apiSecret or a wallet signer. */
302
307
  async list(options) {
@@ -349,7 +354,7 @@ async function purchase(config) {
349
354
  baseUrl: baseUrl ?? BASE_URL,
350
355
  fetch: mppx.fetch.bind(mppx)
351
356
  });
352
- return client.purchases.create(body);
357
+ return client.purchases.create(body, { account });
353
358
  }
354
359
  export {
355
360
  AgentMall,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentmall",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
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",