@heyamiko/amiko-cli 0.12.0-beta.7 → 0.14.0-beta.1

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)));
28502
+ process.exit(1);
28503
+ }
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)));
28467
28509
  process.exit(1);
28468
28510
  }
28469
- const amikoAmount = (usdCost / amikoUsd).toFixed(6);
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();
@@ -28742,7 +28776,7 @@ function registerCreateCommand(create2) {
28742
28776
  }
28743
28777
  });
28744
28778
  });
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) => {
28779
+ 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
28780
  if (!prompt && !opts.lyrics) {
28747
28781
  console.error(error("Provide a prompt or --lyrics."));
28748
28782
  process.exit(1);
@@ -28760,6 +28794,7 @@ function registerCreateCommand(create2) {
28760
28794
  ...opts.lyrics ? { lyrics: opts.lyrics } : {},
28761
28795
  ...opts.duration ? { durationMs: Number(opts.duration) } : {},
28762
28796
  ...opts.instrumental ? { isInstrumental: true } : {},
28797
+ ...opts.lyricsOptimizer ? { lyricsOptimizer: true } : {},
28763
28798
  ...opts.token ? { preferredToken: opts.token } : {}
28764
28799
  }
28765
28800
  });
@@ -28958,6 +28993,10 @@ function detectMime(buffer, name) {
28958
28993
 
28959
28994
  // src/commands/chat.ts
28960
28995
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
28996
+ var CUID_RE = /^c[a-z0-9]{20,}$/i;
28997
+ function looksLikeConversationId(t) {
28998
+ return UUID_RE.test(t) || CUID_RE.test(t);
28999
+ }
28961
29000
  function authOrExit() {
28962
29001
  try {
28963
29002
  return requireAuth();
@@ -28989,7 +29028,7 @@ async function findOrCreateDm(auth, userId) {
28989
29028
  }
28990
29029
  async function resolveConversation(auth, target) {
28991
29030
  const t = target.trim();
28992
- if (UUID_RE.test(t))
29031
+ if (looksLikeConversationId(t))
28993
29032
  return t;
28994
29033
  if (t.startsWith("@")) {
28995
29034
  const handle = t.slice(1);
@@ -29403,24 +29442,29 @@ function registerCardCommand(card) {
29403
29442
  }
29404
29443
 
29405
29444
  // src/commands/amazon.ts
29406
- var TREASURY3 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
29407
- async function payAmiko(auth, wallet, amount2) {
29408
- const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
29409
- method: "POST",
29410
- body: { to: TREASURY3, amount: amount2, token: "AMIKO", chain: "solana" }
29411
- });
29412
- if (!data.txHash)
29413
- throw new Error("No tx hash returned.");
29414
- return data.txHash;
29415
- }
29445
+ var AMAZON_SEARCH_USD = 0.01;
29416
29446
  function registerAmazonCommand(program2) {
29417
29447
  const cmd = program2.command("amazon").description("Search and buy products on Amazon — paid with AMIKO");
29418
- 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) => {
29448
+ 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) => {
29449
+ let payToken;
29450
+ try {
29451
+ payToken = normalizePaymentToken(opts.token);
29452
+ } catch (e5) {
29453
+ console.error(error(e5 instanceof Error ? e5.message : String(e5)));
29454
+ process.exit(1);
29455
+ }
29456
+ let quote;
29457
+ try {
29458
+ quote = await quoteTokenAmount(resolveAuth(), payToken, AMAZON_SEARCH_USD);
29459
+ } catch (e5) {
29460
+ console.error(error(e5 instanceof Error ? e5.message : String(e5)));
29461
+ process.exit(1);
29462
+ }
29419
29463
  await requireApproval({
29420
- cost: "1 AMIKO",
29464
+ cost: `${quote.amount} ${payToken} (≈ $${AMAZON_SEARCH_USD.toFixed(2)})`,
29421
29465
  summary: `Amazon product search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
29422
29466
  yes: opts.yes,
29423
- commandExample: `amiko markets amazon search ${JSON.stringify(query)}`
29467
+ commandExample: `amiko markets amazon search ${JSON.stringify(query)}${payToken !== "AMIKO" ? ` --token ${payToken}` : ""}`
29424
29468
  });
29425
29469
  const spinner = ora("Searching Amazon...").start();
29426
29470
  try {
@@ -29442,14 +29486,14 @@ function registerAmazonCommand(program2) {
29442
29486
  console.error(error(e5 instanceof Error ? e5.message : String(e5)));
29443
29487
  process.exit(1);
29444
29488
  }
29445
- spinner.text = "Paying 1 AMIKO...";
29446
- const txHash = await payAmiko(auth, wallet, "1");
29489
+ spinner.text = `Paying ${quote.amount} ${payToken}...`;
29490
+ const txHash = await payTreasury(auth, { wallet, amount: quote.amount, token: payToken });
29447
29491
  spinner.text = "Searching Amazon...";
29448
29492
  const params = new URLSearchParams({
29449
29493
  q: `site:amazon.com ${query}`
29450
29494
  });
29451
29495
  const res = await fetch(`${mppBase2}/v1/search/google_shopping?${params}`, {
29452
- headers: { "X-Payment-Tx": txHash, "X-Twin-Id": auth.twinId }
29496
+ headers: { "X-Payment-Tx": txHash, "X-Payment-Token": payToken, "X-Twin-Id": auth.twinId }
29453
29497
  });
29454
29498
  spinner.stop();
29455
29499
  if (!res.ok) {
@@ -29546,52 +29590,52 @@ function registerAmazonCommand(program2) {
29546
29590
  }
29547
29591
 
29548
29592
  // src/commands/services.ts
29549
- var TREASURY4 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
29550
29593
  function usdToAmiko(usd2, pricePerAmiko) {
29551
29594
  if (pricePerAmiko <= 0)
29552
29595
  return "1";
29553
29596
  const amount2 = usd2 / pricePerAmiko;
29554
29597
  return Math.ceil(amount2 * 100) / 100 + "";
29555
29598
  }
29556
- async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, body, onProgress, token = "AMIKO") {
29599
+ async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, body, onProgress, rawToken = "AMIKO") {
29600
+ const token = normalizePaymentToken(rawToken);
29557
29601
  if (!path2.startsWith("/")) {
29558
29602
  throw new Error(`path must start with "/" — got: ${path2}`);
29559
29603
  }
29560
29604
  let amount2 = amountOrAuto;
29561
29605
  if (amount2 === "auto") {
29562
29606
  onProgress?.("Fetching price...");
29607
+ let usdPrice = 0;
29563
29608
  try {
29564
29609
  const specRes = await fetchWithTimeout2(`${mppBase2}/openapi.json`, {}, 15000);
29565
29610
  if (specRes.ok) {
29566
29611
  const spec = await specRes.json();
29567
29612
  const pathSpec = spec.paths?.[path2];
29568
29613
  const methodSpec = pathSpec?.[method.toLowerCase()];
29569
- const usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
29570
- if (usdPrice > 0) {
29571
- const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
29572
- amount2 = usdToAmiko(usdPrice, amikoPrice);
29573
- onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
29574
- } else {
29575
- amount2 = "1";
29576
- }
29577
- } else {
29578
- amount2 = "1";
29614
+ usdPrice = parseFloat(methodSpec?.["x-payment-info"]?.price || "0");
29579
29615
  }
29580
29616
  } catch {
29617
+ usdPrice = 0;
29618
+ }
29619
+ if (token === "USDC" || token === "USDT") {
29620
+ if (!(usdPrice > 0)) {
29621
+ throw new Error(`Could not auto-price ${path2} in USD — pass an explicit amount (e.g. --${token.toLowerCase()} 0.01).`);
29622
+ }
29623
+ amount2 = usdPrice.toFixed(6);
29624
+ onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} ${token}`);
29625
+ } else if (usdPrice > 0) {
29626
+ const amikoPrice = await getTokenUsdPrice(auth, "AMIKO") ?? 0;
29627
+ amount2 = usdToAmiko(usdPrice, amikoPrice);
29628
+ onProgress?.(`Price: $${usdPrice.toFixed(4)} = ${amount2} AMIKO`);
29629
+ } else {
29581
29630
  amount2 = "1";
29582
29631
  }
29583
29632
  }
29584
29633
  onProgress?.(`Paying ${amount2} ${token}...`);
29585
- const transferData = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(wallet)}/transfer`, {
29586
- method: "POST",
29587
- body: { to: TREASURY4, amount: amount2, token, chain: "solana" }
29588
- });
29589
- const txHash = transferData.txHash;
29590
- if (!txHash)
29591
- throw new Error("Payment sent but no tx hash returned.");
29634
+ const txHash = await payTreasury(auth, { wallet, amount: amount2, token });
29592
29635
  const headers = {
29593
29636
  "Content-Type": "application/json",
29594
29637
  "X-Payment-Tx": txHash,
29638
+ "X-Payment-Token": token,
29595
29639
  "X-Twin-Id": auth.twinId
29596
29640
  };
29597
29641
  const fetchOpts = { method, headers };
@@ -29687,13 +29731,13 @@ function registerServicesCommand(program2) {
29687
29731
  opts
29688
29732
  });
29689
29733
  });
29690
- 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) => {
29691
- const costLabel = opts.usdc ? `${opts.usdc} USDC` : opts.amiko ? `${opts.amiko} AMIKO` : "auto-priced AMIKO (check /v1/ list for endpoint price first)";
29734
+ 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) => {
29735
+ 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)";
29692
29736
  await requireApproval({
29693
29737
  cost: costLabel,
29694
29738
  summary: `${method.toUpperCase()} ${path2}`,
29695
29739
  yes: opts.yes,
29696
- commandExample: `amiko markets service call ${method.toUpperCase()} ${path2}${bodyStr ? ` ${JSON.stringify(bodyStr)}` : ""}${opts.amiko ? ` --amiko ${opts.amiko}` : ""}${opts.usdc ? ` --usdc ${opts.usdc}` : ""}`
29740
+ 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}` : ""}`
29697
29741
  });
29698
29742
  let body;
29699
29743
  if (bodyStr) {
@@ -29708,8 +29752,9 @@ function registerServicesCommand(program2) {
29708
29752
  method: method.toUpperCase(),
29709
29753
  path: path2,
29710
29754
  body,
29711
- amiko: opts.amiko || (opts.usdc ? undefined : "auto"),
29755
+ amiko: opts.amiko || (opts.usdc || opts.usdt ? undefined : "auto"),
29712
29756
  usdc: opts.usdc,
29757
+ usdt: opts.usdt,
29713
29758
  opts
29714
29759
  });
29715
29760
  });
@@ -29760,8 +29805,8 @@ async function callService(params) {
29760
29805
  console.error(error(e5 instanceof Error ? e5.message : String(e5)));
29761
29806
  process.exit(1);
29762
29807
  }
29763
- const token = params.usdc ? "USDC" : "AMIKO";
29764
- const amount2 = params.usdc || params.amiko || "auto";
29808
+ const token = params.usdt ? "USDT" : params.usdc ? "USDC" : "AMIKO";
29809
+ const amount2 = params.usdt || params.usdc || params.amiko || "auto";
29765
29810
  const { res, txHash, amikoSpent } = await payAndCall(mppBase2, params.method, params.path, auth, wallet, amount2, params.body, (msg) => {
29766
29811
  spinner.text = msg;
29767
29812
  }, token);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.12.0-beta.7",
3
+ "version": "0.14.0-beta.1",
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