@heyamiko/amiko-cli 0.12.0-beta.8 → 0.14.0-beta.2

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @heyamiko/amiko-cli (v0.12.0-beta.7)
1
+ # @heyamiko/amiko-cli (v0.14.0-beta.0)
2
2
 
3
3
  Manage wallets, credits, swaps, MPP marketplace services, and your Amiko twin (identity, documents, voice, avatar, friends, feed) from the terminal. Works for both human users and AI agents running on OpenClaw.
4
4
 
@@ -47,6 +47,7 @@ amiko wallets swap send 1.0 SOL USDC --yes
47
47
  # Call any MPP service
48
48
  amiko markets service list
49
49
  amiko markets service call POST /v1/sfx '{"text":"thunder","duration_seconds":5}'
50
+ amiko markets service call GET /v1/x/search?q=ai --usdt auto # pay any endpoint in USDT
50
51
  ```
51
52
 
52
53
  ## Credits
@@ -93,12 +94,13 @@ amiko credits topup 10000 --wallet Gu2b...QVya # specific wallet (else de
93
94
  ## Search
94
95
 
95
96
  ```bash
96
- amiko markets search "AI agents" # search X/Twitter (1 AMIKO)
97
- amiko markets search "bitcoin" --token USDC # pay with USDC instead
97
+ amiko markets search "AI agents" # search X/Twitter ($0.01 in AMIKO at spot)
98
+ amiko markets search "bitcoin" --token USDT # pay 0.01 USDT directly (no swap)
99
+ amiko markets search "solana" --token USDC # or USDC
98
100
  amiko markets search "news" --raw # raw JSON output
99
101
  ```
100
102
 
101
- Pays 1 AMIKO per search via Crossmint wallet transfer. Tries Solana wallet first, falls back to EVM.
103
+ Each search is anchored at $0.01. The CLI quotes the real token amount before the approval gate — AMIKO converts at the live platform spot; USDC/USDT are pinned at $1.00 so the amount equals the USD price. Payment is a Crossmint wallet transfer from your Solana wallet; the tx rides to MPP as `X-Payment-Tx` + `X-Payment-Token`.
102
104
 
103
105
  ## Image Generation
104
106
 
@@ -253,7 +255,8 @@ amiko card mint <slug> --status # check an existing mint
253
255
  ## Amazon
254
256
 
255
257
  ```bash
256
- amiko markets amazon search "usb c cable" # product search (1 AMIKO)
258
+ amiko markets amazon search "usb c cable" # product search ($0.01)
259
+ amiko markets amazon search "ssd" --token USDT # pay in USDT directly
257
260
  amiko markets amazon quote B01GGKYKQM # price quote (free)
258
261
  ```
259
262
 
@@ -525,6 +528,10 @@ npm publish
525
528
 
526
529
  ## Changelog
527
530
 
531
+ ### 0.14.0-beta.0
532
+
533
+ - **Direct USDC/USDT payment for MPP marketplace services (Bug #9).** `markets search`, `markets image`, and `markets amazon search` gain `--token <AMIKO|USDC|USDT>`; `markets service call` completes its `--usdc` path and adds `--usdt` (both accept `auto` to price from the endpoint's USD anchor). Stables are pinned at $1.00 — no forced swap to AMIKO and no credits conversion. Requires the matching mpp-service deploy: `charge()`/`chargeAmiko()` now read `X-Payment-Token` and verify the on-chain transfer against that token's mint (`verifySplTransfer`), with a ±0.5% tolerance for stables (±5% stays for AMIKO spot drift). Also fixes stale hardcoded amounts: `markets search` / `amazon search` paid a flat "1 AMIKO" regardless of spot — under current prices that under-pays the $0.01 anchor and fails verification; amounts are now quoted live before the approval gate, which shows the real cost instead of "1 AMIKO". Shared payment plumbing extracted to `src/lib/treasury-payment.ts` (one TREASURY constant, one transfer path).
534
+
528
535
  ### 0.11.0-beta.3
529
536
 
530
537
  - **`amiko composio` removed (breaking).** The Composio OAuth-connection commands (`composio connect`, `composio disconnect`) are gone — `src/commands/composio.ts` deleted, `composio` command group dropped from `src/index.ts`. SKILL.md frontmatter, command-groups list, `--yes`-gated list, and the dedicated `## Composio` block are all removed; README's lead paragraph, architecture tree, destructive-ops list, Platform-table row, and Composio code-block example are updated to match. Scripts that called `amiko composio ...` will now error with "unknown command".
package/dist/index.js CHANGED
@@ -28306,15 +28306,60 @@ async function reportHttpError(res, label2) {
28306
28306
  console.error(String(pretty).slice(0, 2000));
28307
28307
  }
28308
28308
 
28309
- // src/commands/search.ts
28309
+ // src/lib/treasury-payment.ts
28310
28310
  var TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
28311
+ var PAYMENT_TOKENS = ["AMIKO", "USDC", "USDT"];
28312
+ function normalizePaymentToken(raw) {
28313
+ const sym = (raw ?? "AMIKO").trim().toUpperCase();
28314
+ if (!PAYMENT_TOKENS.includes(sym)) {
28315
+ throw new Error(`Unsupported payment token "${raw}". Supported: ${PAYMENT_TOKENS.join(", ")}.`);
28316
+ }
28317
+ return sym;
28318
+ }
28319
+ async function quoteTokenAmount(auth, token, usd2) {
28320
+ if (token === "USDC" || token === "USDT") {
28321
+ return { amount: usd2.toFixed(6), priceUsd: 1 };
28322
+ }
28323
+ const priceUsd = await getTokenUsdPrice(auth, "AMIKO");
28324
+ if (!priceUsd || priceUsd <= 0) {
28325
+ throw new Error("Could not fetch AMIKO/USD price from the Amiko platform. Try again in a moment, or pay with --token USDT/USDC.");
28326
+ }
28327
+ const amount2 = (usd2 / priceUsd * 1.01).toFixed(6);
28328
+ return { amount: amount2, priceUsd };
28329
+ }
28330
+ async function payTreasury(auth, args) {
28331
+ const transferData = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(args.wallet)}/transfer`, {
28332
+ method: "POST",
28333
+ body: { to: TREASURY, amount: args.amount, token: args.token, chain: "solana" }
28334
+ });
28335
+ if (!transferData.txHash)
28336
+ throw new Error("Payment sent but no tx hash returned.");
28337
+ return transferData.txHash;
28338
+ }
28339
+
28340
+ // src/commands/search.ts
28341
+ var SEARCH_USD = 0.01;
28311
28342
  function registerSearchCommand(program2) {
28312
- program2.command("search <query>").description("X Search — pays 1 AMIKO token per search").option("--raw", "Output raw JSON").option("--token <token>", "Payment token (AMIKO, USDC, SOL)", "AMIKO").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
28343
+ program2.command("search <query>").description("X Search — $0.01 per search, paid in AMIKO, USDC, or USDT").option("--raw", "Output raw JSON").option("--token <token>", "Payment token (AMIKO, USDC, USDT)", "AMIKO").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
28344
+ let token;
28345
+ try {
28346
+ token = normalizePaymentToken(opts.token);
28347
+ } catch (e5) {
28348
+ console.error(error(e5 instanceof Error ? e5.message : String(e5)));
28349
+ process.exit(1);
28350
+ }
28351
+ let quote;
28352
+ try {
28353
+ quote = await quoteTokenAmount(resolveAuth(), token, SEARCH_USD);
28354
+ } catch (e5) {
28355
+ console.error(error(e5 instanceof Error ? e5.message : String(e5)));
28356
+ process.exit(1);
28357
+ }
28313
28358
  await requireApproval({
28314
- cost: `1 ${opts.token || "AMIKO"}`,
28359
+ cost: `${quote.amount} ${token} (≈ $${SEARCH_USD.toFixed(2)})`,
28315
28360
  summary: `X/Twitter search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
28316
28361
  yes: opts.yes,
28317
- commandExample: `amiko markets search ${JSON.stringify(query)}${opts.token && opts.token !== "AMIKO" ? ` --token ${opts.token}` : ""}`
28362
+ commandExample: `amiko markets search ${JSON.stringify(query)}${token !== "AMIKO" ? ` --token ${token}` : ""}`
28318
28363
  });
28319
28364
  const spinner = ora("X Search...").start();
28320
28365
  try {
@@ -28328,9 +28373,7 @@ function registerSearchCommand(program2) {
28328
28373
  process.exit(1);
28329
28374
  }
28330
28375
  const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
28331
- const token = opts.token || "AMIKO";
28332
- const chain = "solana";
28333
- const amount2 = token === "AMIKO" ? "1" : "0.01";
28376
+ const amount2 = quote.amount;
28334
28377
  let wallet;
28335
28378
  try {
28336
28379
  wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
@@ -28339,19 +28382,10 @@ function registerSearchCommand(program2) {
28339
28382
  console.error(error(e5 instanceof Error ? e5.message : String(e5)));
28340
28383
  process.exit(1);
28341
28384
  }
28342
- spinner.text = `Paying ${amount2} ${token} (${chain})...`;
28385
+ spinner.text = `Paying ${amount2} ${token} (solana)...`;
28343
28386
  let txHash;
28344
28387
  try {
28345
- const transferData = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
28346
- method: "POST",
28347
- body: { to: TREASURY, amount: amount2, token, chain }
28348
- });
28349
- if (!transferData.txHash) {
28350
- spinner.stop();
28351
- console.error(error("Payment sent but no tx hash returned."));
28352
- process.exit(1);
28353
- }
28354
- txHash = transferData.txHash;
28388
+ txHash = await payTreasury(auth, { wallet, amount: amount2, token });
28355
28389
  } catch (e5) {
28356
28390
  spinner.stop();
28357
28391
  console.error(error(e5 instanceof Error ? e5.message : `${token} payment failed`));
@@ -28408,7 +28442,6 @@ function registerSearchCommand(program2) {
28408
28442
  }
28409
28443
 
28410
28444
  // src/commands/image.ts
28411
- var TREASURY2 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
28412
28445
  var IMAGE_MARKUP = 1.3;
28413
28446
  var IMAGE_PRICING = {
28414
28447
  "gpt-image-2": { low: { sq: 0.006, lg: 0.005 }, medium: { sq: 0.053, lg: 0.041 }, high: { sq: 0.211, lg: 0.165 } },
@@ -28426,7 +28459,7 @@ function computeImageUsd(model, quality, size3, n) {
28426
28459
  return Number((pricing[q][tier] * clamped * IMAGE_MARKUP).toFixed(6));
28427
28460
  }
28428
28461
  function registerImageCommand(program2) {
28429
- program2.command("image <prompt>").alias("img").description("Generate an image — pays in AMIKO at OpenAI pass-through pricing × 1.30 (cost varies by model/quality/size)").option("--size <size>", "Image size: 1024x1024, 1024x1536, 1536x1024", "1024x1024").option("--quality <q>", "Quality: low, medium, high, auto", "high").option("--model <model>", `Model: ${SUPPORTED_MODELS.join(", ")}`, DEFAULT_IMAGE_MODEL).option("--n <count>", "Number of images (1–4)", "1").option("--background <bg>", "Background: transparent, opaque, auto").option("--format <fmt>", "Output: png, webp, jpeg", "png").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
28462
+ program2.command("image <prompt>").alias("img").description("Generate an image — pays in AMIKO at OpenAI pass-through pricing × 1.30 (cost varies by model/quality/size)").option("--size <size>", "Image size: 1024x1024, 1024x1536, 1536x1024", "1024x1024").option("--quality <q>", "Quality: low, medium, high, auto", "high").option("--model <model>", `Model: ${SUPPORTED_MODELS.join(", ")}`, DEFAULT_IMAGE_MODEL).option("--n <count>", "Number of images (1–4)", "1").option("--background <bg>", "Background: transparent, opaque, auto").option("--format <fmt>", "Output: png, webp, jpeg", "png").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--token <token>", "Payment token (AMIKO, USDC, USDT)", "AMIKO").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
28430
28463
  if (!SUPPORTED_MODELS.includes(opts.model)) {
28431
28464
  console.error(error(`Unknown model "${opts.model}". Supported: ${SUPPORTED_MODELS.join(", ")}.`));
28432
28465
  process.exit(1);
@@ -28461,14 +28494,23 @@ function registerImageCommand(program2) {
28461
28494
  console.error(error(e5 instanceof Error ? e5.message : "auth failed"));
28462
28495
  process.exit(1);
28463
28496
  }
28464
- const amikoUsd = await getTokenUsdPrice(auth, "AMIKO");
28465
- if (!amikoUsd || amikoUsd <= 0) {
28466
- console.error(error("Could not fetch AMIKO/USD price from the Amiko platform. Try again in a moment, or top up via `amiko credits topup` and use credit auto-pay instead."));
28497
+ let payToken;
28498
+ try {
28499
+ payToken = normalizePaymentToken(opts.token);
28500
+ } catch (e5) {
28501
+ console.error(error(e5 instanceof Error ? e5.message : String(e5)));
28467
28502
  process.exit(1);
28468
28503
  }
28469
- const amikoAmount = (usdCost / amikoUsd).toFixed(6);
28504
+ let quote;
28505
+ try {
28506
+ quote = await quoteTokenAmount(auth, payToken, usdCost);
28507
+ } catch (e5) {
28508
+ console.error(error(e5 instanceof Error ? e5.message : String(e5)));
28509
+ process.exit(1);
28510
+ }
28511
+ const payAmount = quote.amount;
28470
28512
  await requireApproval({
28471
- cost: `${amikoAmount} AMIKO (≈ $${usdCost.toFixed(4)} @ $${amikoUsd.toFixed(6)}/AMIKO, ${opts.model} ${opts.quality} ${opts.size}${n > 1 ? ` ×${n}` : ""})`,
28513
+ cost: `${payAmount} ${payToken} (≈ $${usdCost.toFixed(4)} @ $${quote.priceUsd.toFixed(6)}/${payToken}, ${opts.model} ${opts.quality} ${opts.size}${n > 1 ? ` ×${n}` : ""})`,
28472
28514
  summary: `Generate image: ${prompt.slice(0, 80)}${prompt.length > 80 ? "…" : ""}`,
28473
28515
  yes: opts.yes,
28474
28516
  commandExample: `amiko markets image ${JSON.stringify(prompt)}`
@@ -28512,19 +28554,10 @@ function registerImageCommand(program2) {
28512
28554
  console.error(error(e5 instanceof Error ? e5.message : String(e5)));
28513
28555
  process.exit(1);
28514
28556
  }
28515
- setStatus(`Paying ${amikoAmount} AMIKO...`);
28557
+ setStatus(`Paying ${payAmount} ${payToken}...`);
28516
28558
  let txHash;
28517
28559
  try {
28518
- const transferData = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
28519
- method: "POST",
28520
- body: { to: TREASURY2, amount: amikoAmount, token: "AMIKO", chain: "solana" }
28521
- });
28522
- if (!transferData.txHash) {
28523
- stopProgress();
28524
- console.error(error("Payment sent but no tx hash returned."));
28525
- process.exit(1);
28526
- }
28527
- txHash = transferData.txHash;
28560
+ txHash = await payTreasury(auth, { wallet, amount: payAmount, token: payToken });
28528
28561
  } catch (e5) {
28529
28562
  stopProgress();
28530
28563
  console.error(error(e5 instanceof Error ? e5.message : "Payment failed"));
@@ -28536,6 +28569,7 @@ function registerImageCommand(program2) {
28536
28569
  headers: {
28537
28570
  "Content-Type": "application/json",
28538
28571
  "X-Payment-Tx": txHash,
28572
+ "X-Payment-Token": payToken,
28539
28573
  "X-Twin-Id": auth.twinId
28540
28574
  },
28541
28575
  body: JSON.stringify({
@@ -28578,7 +28612,7 @@ function registerImageCommand(program2) {
28578
28612
  }
28579
28613
  }
28580
28614
  console.log("");
28581
- console.log(success(`Paid ${amikoAmount} AMIKO (≈ $${usdCost.toFixed(4)})`) + dim(` tx: ${txHash.slice(0, 16)}...`));
28615
+ console.log(success(`Paid ${payAmount} ${payToken} (≈ $${usdCost.toFixed(4)})`) + dim(` tx: ${txHash.slice(0, 16)}...`));
28582
28616
  await printBalanceRemaining(auth);
28583
28617
  } catch (err) {
28584
28618
  stopProgress();
@@ -28610,7 +28644,9 @@ function printCompleted(final, title) {
28610
28644
  console.log(label("Type", final.assetMime));
28611
28645
  console.log("");
28612
28646
  if (final.charged) {
28613
- console.log(success(`Charged ${final.costAmiko ?? "?"} AMIKO`) + (final.chargeTx ? dim(` tx: ${final.chargeTx.slice(0, 16)}…`) : ""));
28647
+ const amt = final.chargeAmount ?? final.costAmiko ?? "?";
28648
+ const tok = final.chargeToken ?? "AMIKO";
28649
+ console.log(success(`Charged ${amt} ${tok}`) + (final.chargeTx ? dim(` tx: ${final.chargeTx.slice(0, 16)}…`) : ""));
28614
28650
  } else {
28615
28651
  console.log(dim("Not charged (settlement pending or waived)."));
28616
28652
  }
@@ -28742,7 +28778,7 @@ function registerCreateCommand(create2) {
28742
28778
  }
28743
28779
  });
28744
28780
  });
28745
- create2.command("music [prompt]").description("Generate music (Create Studio)").option("--model <model>", "music_v1, music-2.6, music-cover", "music-2.6").option("--lyrics <lyrics>", "Lyrics (optional)").option("--duration <ms>", "Length in ms").option("--instrumental", "Instrumental (no vocals)").option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
28781
+ create2.command("music [prompt]").description("Generate music (Create Studio)").option("--model <model>", "music_v1, music-2.6, music-cover", "music-2.6").option("--lyrics <lyrics>", "Lyrics (optional)").option("--duration <ms>", "Length in ms").option("--instrumental", "Instrumental (no vocals)").option("--lyrics-optimizer", "Auto-write lyrics from the prompt (music_v1 only; on by default when no --lyrics/--instrumental)").option("--token <symbol>", "Preferred charge token: AMIKO, USDC, USDT, SOL").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
28746
28782
  if (!prompt && !opts.lyrics) {
28747
28783
  console.error(error("Provide a prompt or --lyrics."));
28748
28784
  process.exit(1);
@@ -28760,6 +28796,7 @@ function registerCreateCommand(create2) {
28760
28796
  ...opts.lyrics ? { lyrics: opts.lyrics } : {},
28761
28797
  ...opts.duration ? { durationMs: Number(opts.duration) } : {},
28762
28798
  ...opts.instrumental ? { isInstrumental: true } : {},
28799
+ ...opts.lyricsOptimizer ? { lyricsOptimizer: true } : {},
28763
28800
  ...opts.token ? { preferredToken: opts.token } : {}
28764
28801
  }
28765
28802
  });
@@ -29407,24 +29444,29 @@ function registerCardCommand(card) {
29407
29444
  }
29408
29445
 
29409
29446
  // src/commands/amazon.ts
29410
- var TREASURY3 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
29411
- async function payAmiko(auth, wallet, amount2) {
29412
- const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
29413
- method: "POST",
29414
- body: { to: TREASURY3, amount: amount2, token: "AMIKO", chain: "solana" }
29415
- });
29416
- if (!data.txHash)
29417
- throw new Error("No tx hash returned.");
29418
- return data.txHash;
29419
- }
29447
+ var AMAZON_SEARCH_USD = 0.01;
29420
29448
  function registerAmazonCommand(program2) {
29421
29449
  const cmd = program2.command("amazon").description("Search and buy products on Amazon — paid with AMIKO");
29422
- cmd.command("search <query>").description("Search Amazon products — 1 AMIKO").option("--limit <n>", "Number of results", "5").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
29450
+ cmd.command("search <query>").description("Search Amazon products — $0.01, paid in AMIKO, USDC, or USDT").option("--limit <n>", "Number of results", "5").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if you don't have one)").option("--token <token>", "Payment token (AMIKO, USDC, USDT)", "AMIKO").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
29451
+ let payToken;
29452
+ try {
29453
+ payToken = normalizePaymentToken(opts.token);
29454
+ } catch (e5) {
29455
+ console.error(error(e5 instanceof Error ? e5.message : String(e5)));
29456
+ process.exit(1);
29457
+ }
29458
+ let quote;
29459
+ try {
29460
+ quote = await quoteTokenAmount(resolveAuth(), payToken, AMAZON_SEARCH_USD);
29461
+ } catch (e5) {
29462
+ console.error(error(e5 instanceof Error ? e5.message : String(e5)));
29463
+ process.exit(1);
29464
+ }
29423
29465
  await requireApproval({
29424
- cost: "1 AMIKO",
29466
+ cost: `${quote.amount} ${payToken} (≈ $${AMAZON_SEARCH_USD.toFixed(2)})`,
29425
29467
  summary: `Amazon product search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
29426
29468
  yes: opts.yes,
29427
- commandExample: `amiko markets amazon search ${JSON.stringify(query)}`
29469
+ commandExample: `amiko markets amazon search ${JSON.stringify(query)}${payToken !== "AMIKO" ? ` --token ${payToken}` : ""}`
29428
29470
  });
29429
29471
  const spinner = ora("Searching Amazon...").start();
29430
29472
  try {
@@ -29446,14 +29488,14 @@ function registerAmazonCommand(program2) {
29446
29488
  console.error(error(e5 instanceof Error ? e5.message : String(e5)));
29447
29489
  process.exit(1);
29448
29490
  }
29449
- spinner.text = "Paying 1 AMIKO...";
29450
- const txHash = await payAmiko(auth, wallet, "1");
29491
+ spinner.text = `Paying ${quote.amount} ${payToken}...`;
29492
+ const txHash = await payTreasury(auth, { wallet, amount: quote.amount, token: payToken });
29451
29493
  spinner.text = "Searching Amazon...";
29452
29494
  const params = new URLSearchParams({
29453
29495
  q: `site:amazon.com ${query}`
29454
29496
  });
29455
29497
  const res = await fetch(`${mppBase2}/v1/search/google_shopping?${params}`, {
29456
- headers: { "X-Payment-Tx": txHash, "X-Twin-Id": auth.twinId }
29498
+ headers: { "X-Payment-Tx": txHash, "X-Payment-Token": payToken, "X-Twin-Id": auth.twinId }
29457
29499
  });
29458
29500
  spinner.stop();
29459
29501
  if (!res.ok) {
@@ -29550,52 +29592,52 @@ function registerAmazonCommand(program2) {
29550
29592
  }
29551
29593
 
29552
29594
  // src/commands/services.ts
29553
- var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
29554
29595
  function usdToAmiko(usd2, pricePerAmiko) {
29555
29596
  if (pricePerAmiko <= 0)
29556
29597
  return "1";
29557
29598
  const amount2 = usd2 / pricePerAmiko;
29558
29599
  return Math.ceil(amount2 * 100) / 100 + "";
29559
29600
  }
29560
- async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, body, onProgress, token = "AMIKO") {
29601
+ async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, body, onProgress, rawToken = "AMIKO") {
29602
+ const token = normalizePaymentToken(rawToken);
29561
29603
  if (!path2.startsWith("/")) {
29562
29604
  throw new Error(`path must start with "/" — got: ${path2}`);
29563
29605
  }
29564
29606
  let amount2 = amountOrAuto;
29565
29607
  if (amount2 === "auto") {
29566
29608
  onProgress?.("Fetching price...");
29609
+ let usdPrice = 0;
29567
29610
  try {
29568
29611
  const specRes = await fetchWithTimeout2(`${mppBase2}/openapi.json`, {}, 15000);
29569
29612
  if (specRes.ok) {
29570
29613
  const spec = await specRes.json();
29571
29614
  const pathSpec = spec.paths?.[path2];
29572
29615
  const methodSpec = pathSpec?.[method.toLowerCase()];
29573
- const usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
29574
- if (usdPrice > 0) {
29575
- const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
29576
- amount2 = usdToAmiko(usdPrice, amikoPrice);
29577
- onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
29578
- } else {
29579
- amount2 = "1";
29580
- }
29581
- } else {
29582
- amount2 = "1";
29616
+ usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
29583
29617
  }
29584
29618
  } catch {
29619
+ usdPrice = 0;
29620
+ }
29621
+ if (token === "USDC" || token === "USDT") {
29622
+ if (!(usdPrice > 0)) {
29623
+ throw new Error(`Could not auto-price ${path2} in USD — pass an explicit amount (e.g. --${token.toLowerCase()} 0.01).`);
29624
+ }
29625
+ amount2 = usdPrice.toFixed(6);
29626
+ onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} ${token}`);
29627
+ } else if (usdPrice > 0) {
29628
+ const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
29629
+ amount2 = usdToAmiko(usdPrice, amikoPrice);
29630
+ onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
29631
+ } else {
29585
29632
  amount2 = "1";
29586
29633
  }
29587
29634
  }
29588
29635
  onProgress?.(`Paying ${amount2} ${token}...`);
29589
- const transferData = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
29590
- method: "POST",
29591
- body: { to: TREASURY4, amount: amount2, token, chain: "solana" }
29592
- });
29593
- const txHash = transferData.txHash;
29594
- if (!txHash)
29595
- throw new Error("Payment sent but no tx hash returned.");
29636
+ const txHash = await payTreasury(auth, { wallet, amount: amount2, token });
29596
29637
  const headers = {
29597
29638
  "Content-Type": "application/json",
29598
29639
  "X-Payment-Tx": txHash,
29640
+ "X-Payment-Token": token,
29599
29641
  "X-Twin-Id": auth.twinId
29600
29642
  };
29601
29643
  const fetchOpts = { method, headers };
@@ -29691,13 +29733,13 @@ function registerServicesCommand(program2) {
29691
29733
  opts
29692
29734
  });
29693
29735
  });
29694
- svc.command("call <method> <path> [body]").description("Call any MPP endpoint — pays in AMIKO or USDC on Solana").option("--amiko <amount>", "AMIKO tokens to pay (default: auto-price from USD)").option("--usdc <amount>", "Pay in USDC instead of AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (method, path2, bodyStr, opts) => {
29695
- const costLabel = opts.usdc ? `${opts.usdc} USDC` : opts.amiko ? `${opts.amiko} AMIKO` : "auto-priced AMIKO (check /v1/ list for endpoint price first)";
29736
+ svc.command("call <method> <path> [body]").description("Call any MPP endpoint — pays in AMIKO, USDC, or USDT on Solana").option("--amiko <amount>", "AMIKO tokens to pay (default: auto-price from USD)").option("--usdc <amount>", 'Pay in USDC instead of AMIKO ("auto" to price from USD)').option("--usdt <amount>", 'Pay in USDT instead of AMIKO ("auto" to price from USD)').option("--raw", "Output raw response").option("--wallet <address>", "Wallet to pay from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (method, path2, bodyStr, opts) => {
29737
+ const costLabel = opts.usdt ? `${opts.usdt} USDT` : opts.usdc ? `${opts.usdc} USDC` : opts.amiko ? `${opts.amiko} AMIKO` : "auto-priced AMIKO (check /v1/ list for endpoint price first)";
29696
29738
  await requireApproval({
29697
29739
  cost: costLabel,
29698
29740
  summary: `${method.toUpperCase()} ${path2}`,
29699
29741
  yes: opts.yes,
29700
- commandExample: `amiko markets service call ${method.toUpperCase()} ${path2}${bodyStr ? ` ${JSON.stringify(bodyStr)}` : ""}${opts.amiko ? ` --amiko ${opts.amiko}` : ""}${opts.usdc ? ` --usdc ${opts.usdc}` : ""}`
29742
+ commandExample: `amiko markets service call ${method.toUpperCase()} ${path2}${bodyStr ? ` ${JSON.stringify(bodyStr)}` : ""}${opts.amiko ? ` --amiko ${opts.amiko}` : ""}${opts.usdc ? ` --usdc ${opts.usdc}` : ""}${opts.usdt ? ` --usdt ${opts.usdt}` : ""}`
29701
29743
  });
29702
29744
  let body;
29703
29745
  if (bodyStr) {
@@ -29712,8 +29754,9 @@ function registerServicesCommand(program2) {
29712
29754
  method: method.toUpperCase(),
29713
29755
  path: path2,
29714
29756
  body,
29715
- amiko: opts.amiko || (opts.usdc ? undefined : "auto"),
29757
+ amiko: opts.amiko || (opts.usdc || opts.usdt ? undefined : "auto"),
29716
29758
  usdc: opts.usdc,
29759
+ usdt: opts.usdt,
29717
29760
  opts
29718
29761
  });
29719
29762
  });
@@ -29764,8 +29807,8 @@ async function callService(params) {
29764
29807
  console.error(error(e5 instanceof Error ? e5.message : String(e5)));
29765
29808
  process.exit(1);
29766
29809
  }
29767
- const token = params.usdc ? "USDC" : "AMIKO";
29768
- const amount2 = params.usdc || params.amiko || "auto";
29810
+ const token = params.usdt ? "USDT" : params.usdc ? "USDC" : "AMIKO";
29811
+ const amount2 = params.usdt || params.usdc || params.amiko || "auto";
29769
29812
  const { res, txHash, amikoSpent } = await payAndCall(mppBase2, params.method, params.path, auth, wallet, amount2, params.body, (msg) => {
29770
29813
  spinner.text = msg;
29771
29814
  }, token);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.12.0-beta.8",
3
+ "version": "0.14.0-beta.2",
4
4
  "description": "Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace agents",
5
5
  "type": "module",
6
6
  "bin": {
package/skills/SKILL.md CHANGED
@@ -92,9 +92,11 @@ All paid markets commands auto-select the twin's active Solana wallet (see `--wa
92
92
 
93
93
  **Prefer `amiko create` for anything it supports.** For media generation — image, video, speech (TTS), music, SFX — use `amiko create` (async, charge-on-success, never loses money on a timeout), NOT `markets`. Reach for `markets` **only for what `create` does not cover**: X/Twitter search, Amazon, AI chat, speech-to-text/transcription, and arbitrary MPP endpoints via `markets service call`.
94
94
 
95
+ **Payment token:** `markets search`, `markets image`, and `markets amazon search` accept `--token <AMIKO|USDC|USDT>` (default AMIKO); `markets service call` uses `--usdc`/`--usdt` (pass `auto` to price from USD). Stables are pinned at $1.00 — the amount equals the endpoint's USD price — so there is no forced swap through AMIKO. When the owner asks to pay in USDT/USDC, pass the flag; do NOT swap first.
96
+
95
97
  ## Create Studio — behavior notes
96
98
 
97
- `amiko create <image|video|tts|music|sfx>` is the CLI half of the platform Create Studio. It generates through Amiko's own authenticated endpoints (not raw MPP), runs **async**, and is **charged on success** from the twin's custodial wallet — a failed or timed-out generation is **never billed**, and there is no pre-pay. **The command returns immediately** with a `jobId` and `status: PENDING` — it does NOT block for the whole generation (so you're not held for 60s–9min). It prints how to check + a rough ETA. **Do NOT sit in a tight polling loop** (it burns turns/tokens). After submitting, **tell the user roughly how long to wait and stop** — image ~15–60s, video ~2–9min, music ~30–120s, tts/sfx ~5–20s (e.g. "your video's generating — check back in a few minutes"). Then retrieve it **once** later — when the user next asks, or after the ETA — with `amiko create status <jobId>` (re-query that job, ~24h) or `amiko create media` (list recent generations; `--service`/`--limit`/`--raw`). Result is a permanent Supabase Storage URL. **Do NOT use `--wait`** — there's no such option; `create` is always non-blocking, so submit then check later with `amiko create status <jobId>`. `--token <AMIKO|USDC|USDT|SOL>` selects the charge token (default auto, AMIKO-first). Prefer `create` over `markets image` for media generation — it doesn't lose money on timeouts. `markets image` remains the raw MPP pre-pay path.
99
+ `amiko create <image|video|tts|music|sfx>` is the CLI half of the platform Create Studio. It generates through Amiko's own authenticated endpoints (not raw MPP), runs **async**, and is **charged on success** from the twin's custodial wallet — a failed or timed-out generation is **never billed**, and there is no pre-pay. **The command returns immediately** with a `jobId` and `status: PENDING` — it does NOT block for the whole generation (so you're not held for 60s–9min). It prints how to check + a rough ETA. **Do NOT sit in a tight polling loop** (it burns turns/tokens). After submitting, **tell the user roughly how long to wait and stop** — image ~15–60s, video ~2–9min, music ~30–120s, tts/sfx ~5–20s (e.g. "your video's generating — check back in a few minutes"). Then retrieve it **once** later — when the user next asks, or after the ETA — with `amiko create status <jobId>` (re-query that job, ~24h) or `amiko create media` (list recent generations; `--service`/`--limit`/`--raw`). Result is a permanent Supabase Storage URL. **Do NOT use `--wait`** — there's no such option; `create` is always non-blocking, so submit then check later with `amiko create status <jobId>`. `--token <AMIKO|USDC|USDT|SOL>` selects the charge token (default auto, AMIKO-first). Prefer `create` over `markets image` for media generation — it doesn't lose money on timeouts. `markets image` remains the raw MPP pre-pay path. For **music**, a plain prompt is enough — `create music "a triumphant orchestral ballad"` sings from the prompt (lyrics auto-written); add `--lyrics "…"` to set exact words, or `--instrumental` for no vocals. Don't paste long lyrics into the prompt itself.
98
100
 
99
101
  ## Chat — behavior notes
100
102