@heyamiko/amiko-cli 0.9.0-beta.0 → 0.9.0-beta.10

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/index.js CHANGED
@@ -17287,7 +17287,6 @@ function resolveAuth() {
17287
17287
  twinId: asString(parsed.amikoTwinId) ?? asString(parsed.AMIKO_TWIN_ID),
17288
17288
  userId: asString(parsed.amikoUserId) ?? asString(parsed.AMIKO_USER_ID),
17289
17289
  platformUrl: asString(parsed.amikoPlatformUrl) ?? asString(parsed.AMIKO_PLATFORM_URL),
17290
- billingBase: asString(parsed.billingBase),
17291
17290
  apiBase: asString(parsed.apiBase)
17292
17291
  };
17293
17292
  }
@@ -17305,7 +17304,6 @@ function requireAuth() {
17305
17304
  // src/lib/config.ts
17306
17305
  var DEFAULTS = {
17307
17306
  apiBase: "https://mpp.amikomarkets.com",
17308
- billingBase: "https://billing.heyamiko.com",
17309
17307
  platformUrl: "https://platform.heyamiko.com",
17310
17308
  siteBase: "https://platform.heyamiko.com"
17311
17309
  };
@@ -17313,7 +17311,6 @@ function loadConfig() {
17313
17311
  const auth = resolveAuth();
17314
17312
  return {
17315
17313
  apiBase: auth.apiBase ?? DEFAULTS.apiBase,
17316
- billingBase: auth.billingBase ?? DEFAULTS.billingBase,
17317
17314
  platformUrl: auth.platformUrl ?? DEFAULTS.platformUrl,
17318
17315
  siteBase: DEFAULTS.siteBase,
17319
17316
  twinToken: auth.twinToken,
@@ -17385,7 +17382,6 @@ function registerConfigCommand(program2) {
17385
17382
  console.log("");
17386
17383
  console.log(heading("Endpoints"));
17387
17384
  console.log(label(" MPP service", config.apiBase));
17388
- console.log(label(" Billing API", config.billingBase));
17389
17385
  console.log(label(" Platform", config.platformUrl));
17390
17386
  console.log("");
17391
17387
  console.log(heading("Identity"));
@@ -18488,6 +18484,104 @@ async function requireApproval(args) {
18488
18484
  process.exit(2);
18489
18485
  }
18490
18486
 
18487
+ // src/lib/amiko-web.ts
18488
+ var DEFAULT_PLATFORM_URL = "https://platform.heyamiko.com";
18489
+ function baseUrl(auth) {
18490
+ const url = auth.platformUrl || DEFAULT_PLATFORM_URL;
18491
+ return url.replace(/\/$/, "");
18492
+ }
18493
+ function authHeader(auth) {
18494
+ if (auth.twinToken)
18495
+ return `Bearer ${auth.twinToken}`;
18496
+ throw new Error("No twin token found. Place an .amiko.json file with amikoTwinToken in the current directory or any parent.");
18497
+ }
18498
+ async function amikoWebFetch(auth, path, opts = {}) {
18499
+ const url = new URL(`${baseUrl(auth)}${path}`);
18500
+ if (opts.query) {
18501
+ for (const [k, v] of Object.entries(opts.query)) {
18502
+ if (v !== undefined && v !== null)
18503
+ url.searchParams.set(k, String(v));
18504
+ }
18505
+ }
18506
+ const headers = {
18507
+ Authorization: authHeader(auth),
18508
+ ...opts.headers
18509
+ };
18510
+ let body;
18511
+ if (opts.multipart) {
18512
+ body = opts.multipart;
18513
+ } else if (opts.rawBody) {
18514
+ body = opts.rawBody;
18515
+ if (opts.rawContentType)
18516
+ headers["Content-Type"] = opts.rawContentType;
18517
+ } else if (opts.body) {
18518
+ body = JSON.stringify(opts.body);
18519
+ headers["Content-Type"] = "application/json";
18520
+ }
18521
+ const controller = new AbortController;
18522
+ const timeoutMs = opts.timeoutMs ?? 30000;
18523
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
18524
+ let res;
18525
+ try {
18526
+ res = await fetch(url, {
18527
+ method: opts.method ?? "GET",
18528
+ headers,
18529
+ body,
18530
+ signal: controller.signal
18531
+ });
18532
+ } finally {
18533
+ clearTimeout(timer);
18534
+ }
18535
+ if (!res.ok) {
18536
+ const text = await res.text().catch(() => "");
18537
+ const err = new Error(formatError(res.status, text, path));
18538
+ err.status = res.status;
18539
+ err.body = text;
18540
+ throw err;
18541
+ }
18542
+ const ctype = res.headers.get("content-type") || "";
18543
+ if (ctype.includes("application/json")) {
18544
+ return await res.json();
18545
+ }
18546
+ return await res.text();
18547
+ }
18548
+ function formatError(status, body, path) {
18549
+ const prefix = `${status} ${path}`;
18550
+ if (status === 401) {
18551
+ return `${prefix}: unauthorized. The stored token is invalid or expired; re-link this device to Amiko.`;
18552
+ }
18553
+ if (status === 403) {
18554
+ return `${prefix}: forbidden. The current auth context does not have access to this resource.`;
18555
+ }
18556
+ if (!body)
18557
+ return prefix;
18558
+ const snippet = body.length > 500 ? `${body.slice(0, 500)}...` : body;
18559
+ return `${prefix}: ${snippet}`;
18560
+ }
18561
+
18562
+ // src/lib/wallet-default.ts
18563
+ var cache = null;
18564
+ async function loadWallets(auth) {
18565
+ if (cache)
18566
+ return cache;
18567
+ const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets`);
18568
+ cache = data.wallets ?? [];
18569
+ return cache;
18570
+ }
18571
+ async function resolveWalletAddress(auth, explicit, preferredChain = "solana") {
18572
+ if (explicit)
18573
+ return explicit;
18574
+ const wallets = await loadWallets(auth);
18575
+ const match = wallets.find((w) => w.status === "active" && w.chain === preferredChain);
18576
+ if (match)
18577
+ return match.wallet_address;
18578
+ const anyActive = wallets.find((w) => w.status === "active");
18579
+ if (anyActive && !explicit) {
18580
+ throw new Error(`No active ${preferredChain} wallet found. Run \`amiko wallets create --chain ${preferredChain}\` or pass --wallet <address> explicitly.`);
18581
+ }
18582
+ throw new Error(`No wallets found for this agent. Run \`amiko wallets create --chain ${preferredChain}\` first.`);
18583
+ }
18584
+
18491
18585
  // src/lib/across.ts
18492
18586
  function getBridgeBase() {
18493
18587
  const config = loadConfig();
@@ -18639,7 +18733,7 @@ var DEFAULT_ORIGIN = CHAINS.solana;
18639
18733
  var DEFAULT_DESTINATION = CHAINS.tempo;
18640
18734
  function registerBridgeCommand(program2) {
18641
18735
  const cmd = program2.command("bridge").description("Bridge USDC across chains via Across Protocol");
18642
- cmd.command("quote").description("Get a bridge quote for USDC transfer").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain").option("--depositor <address>", "Depositor address on origin chain").action(async (amountStr, opts) => {
18736
+ cmd.command("quote").description("Get a bridge quote for USDC transfer").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain (required for Solana ↔ EVM)").option("--depositor <address>", "Depositor address on origin chain (defaults to your wallet on that chain)").action(async (amountStr, opts) => {
18643
18737
  const spinner = ora("Fetching bridge quote...").start();
18644
18738
  try {
18645
18739
  const originChainId = resolveChainId(opts.from);
@@ -18647,10 +18741,14 @@ function registerBridgeCommand(program2) {
18647
18741
  const inputToken = getUsdcAddress(originChainId);
18648
18742
  const outputToken = getUsdcAddress(destChainId);
18649
18743
  const amount = parseUsdcAmount(amountStr);
18650
- const depositor = opts.depositor;
18651
- if (!depositor) {
18744
+ const preferredChain = originChainId === CHAINS.solana ? "solana" : "base";
18745
+ let depositor;
18746
+ try {
18747
+ const auth = requireAuth();
18748
+ depositor = await resolveWalletAddress(auth, opts.depositor, preferredChain);
18749
+ } catch (e) {
18652
18750
  spinner.stop();
18653
- console.error(error("No depositor address. Provide --depositor <address>."));
18751
+ console.error(error(e instanceof Error ? e.message : String(e)));
18654
18752
  process.exit(1);
18655
18753
  }
18656
18754
  const isCrossEcosystem = originChainId === CHAINS.solana !== (destChainId === CHAINS.solana);
@@ -18698,8 +18796,8 @@ function registerBridgeCommand(program2) {
18698
18796
  }
18699
18797
  if (quote.swapTx) {
18700
18798
  console.log("");
18701
- console.log(dim('Transaction ready. Run "amiko bridge send" to execute.'));
18702
- console.log(dim(` amiko bridge send ${amountStr} --from ${opts.from} --to ${opts.to}`));
18799
+ console.log(dim('Transaction ready. Run "amiko wallets bridge send" to execute.'));
18800
+ console.log(dim(` amiko wallets bridge send ${amountStr} --from ${opts.from} --to ${opts.to}${opts.depositor ? ` --depositor ${opts.depositor}` : ""}${opts.recipient ? ` --recipient ${opts.recipient}` : ""}`));
18703
18801
  }
18704
18802
  } catch (err) {
18705
18803
  spinner.stop();
@@ -18707,7 +18805,7 @@ function registerBridgeCommand(program2) {
18707
18805
  process.exit(1);
18708
18806
  }
18709
18807
  });
18710
- cmd.command("send").description("Execute a USDC bridge transfer via Across").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain").option("--depositor <address>", "Depositor address on origin chain").option("--raw", "Output raw transaction JSON for manual signing").option("--yes", "Skip confirmation (only after the user has approved)").action(async (amountStr, opts) => {
18808
+ cmd.command("send").description("Execute a USDC bridge transfer via Across").argument("<amount>", "Amount of USDC to bridge (e.g., 10, 5.50)").option("--from <chain>", "Origin chain (default: solana)", "solana").option("--to <chain>", "Destination chain (default: tempo)", "tempo").option("--recipient <address>", "Recipient address on destination chain (required for Solana ↔ EVM)").option("--depositor <address>", "Depositor address on origin chain (defaults to your wallet on that chain)").option("--raw", "Output raw transaction JSON for manual signing").option("--yes", "Skip confirmation (only after the user has approved)").action(async (amountStr, opts) => {
18711
18809
  const spinner = ora("Preparing bridge transaction...").start();
18712
18810
  try {
18713
18811
  const originChainId = resolveChainId(opts.from);
@@ -18715,10 +18813,14 @@ function registerBridgeCommand(program2) {
18715
18813
  const inputToken = getUsdcAddress(originChainId);
18716
18814
  const outputToken = getUsdcAddress(destChainId);
18717
18815
  const amount = parseUsdcAmount(amountStr);
18718
- const depositor = opts.depositor;
18719
- if (!depositor) {
18816
+ const preferredChain = originChainId === CHAINS.solana ? "solana" : "base";
18817
+ let depositor;
18818
+ try {
18819
+ const auth = requireAuth();
18820
+ depositor = await resolveWalletAddress(auth, opts.depositor, preferredChain);
18821
+ } catch (e) {
18720
18822
  spinner.stop();
18721
- console.error(error("No depositor address. Provide --depositor <address>."));
18823
+ console.error(error(e instanceof Error ? e.message : String(e)));
18722
18824
  process.exit(1);
18723
18825
  }
18724
18826
  const isCrossEcosystem = originChainId === CHAINS.solana !== (destChainId === CHAINS.solana);
@@ -18752,7 +18854,7 @@ function registerBridgeCommand(program2) {
18752
18854
  cost: quote.fees?.total ? `${formatUsdcAmount(quote.fees.total.amount)} USDC bridge fee + gas` : "gas + Across fee",
18753
18855
  summary: `bridge ${formatUsdcAmount(quote.inputAmount)} USDC ${originName} → ${destName}`,
18754
18856
  yes: opts.yes,
18755
- commandExample: `amiko bridge send ${amountStr} --from ${opts.from} --to ${opts.to}`
18857
+ commandExample: `amiko wallets bridge send ${amountStr} --from ${opts.from} --to ${opts.to}`
18756
18858
  });
18757
18859
  }
18758
18860
  if (opts.raw) {
@@ -18831,7 +18933,7 @@ function registerBridgeCommand(program2) {
18831
18933
  console.log(dim(output.trim()));
18832
18934
  console.log("");
18833
18935
  console.log(dim("Track status:"));
18834
- console.log(dim(` amiko bridge status <txHash> --chain ${originChainId}`));
18936
+ console.log(dim(` amiko wallets bridge status <txHash> --chain ${originChainId}`));
18835
18937
  } catch (err) {
18836
18938
  bridgeSpinner.fail("Bridge transaction failed");
18837
18939
  console.error(error(err instanceof Error ? err.message : "Transaction failed"));
@@ -18978,94 +19080,13 @@ function registerBridgeCommand(program2) {
18978
19080
  // src/commands/credits.ts
18979
19081
  import readline2 from "node:readline";
18980
19082
 
18981
- // src/lib/amiko-web.ts
18982
- var DEFAULT_PLATFORM_URL = "https://platform.heyamiko.com";
18983
- function baseUrl(auth) {
18984
- const url = auth.platformUrl || DEFAULT_PLATFORM_URL;
18985
- return url.replace(/\/$/, "");
18986
- }
18987
- function authHeader(auth) {
18988
- if (auth.twinToken)
18989
- return `Bearer ${auth.twinToken}`;
18990
- throw new Error("No twin token found. Place an .amiko.json file with amikoTwinToken in the current directory or any parent.");
18991
- }
18992
- async function amikoWebFetch(auth, path, opts = {}) {
18993
- const url = new URL(`${baseUrl(auth)}${path}`);
18994
- if (opts.query) {
18995
- for (const [k, v] of Object.entries(opts.query)) {
18996
- if (v !== undefined && v !== null)
18997
- url.searchParams.set(k, String(v));
18998
- }
18999
- }
19000
- const headers = {
19001
- Authorization: authHeader(auth),
19002
- ...opts.headers
19003
- };
19004
- let body;
19005
- if (opts.multipart) {
19006
- body = opts.multipart;
19007
- } else if (opts.rawBody) {
19008
- body = opts.rawBody;
19009
- if (opts.rawContentType)
19010
- headers["Content-Type"] = opts.rawContentType;
19011
- } else if (opts.body) {
19012
- body = JSON.stringify(opts.body);
19013
- headers["Content-Type"] = "application/json";
19014
- }
19015
- const controller = new AbortController;
19016
- const timeoutMs = opts.timeoutMs ?? 30000;
19017
- const timer = setTimeout(() => controller.abort(), timeoutMs);
19018
- let res;
19019
- try {
19020
- res = await fetch(url, {
19021
- method: opts.method ?? "GET",
19022
- headers,
19023
- body,
19024
- signal: controller.signal
19025
- });
19026
- } finally {
19027
- clearTimeout(timer);
19028
- }
19029
- if (!res.ok) {
19030
- const text = await res.text().catch(() => "");
19031
- const err = new Error(formatError(res.status, text, path));
19032
- err.status = res.status;
19033
- err.body = text;
19034
- throw err;
19035
- }
19036
- const ctype = res.headers.get("content-type") || "";
19037
- if (ctype.includes("application/json")) {
19038
- return await res.json();
19039
- }
19040
- return await res.text();
19041
- }
19042
- function formatError(status, body, path) {
19043
- const prefix = `${status} ${path}`;
19044
- if (status === 401) {
19045
- return `${prefix}: unauthorized. The stored token is invalid or expired; re-link this device to Amiko.`;
19046
- }
19047
- if (status === 403) {
19048
- return `${prefix}: forbidden. The current auth context does not have access to this resource.`;
19049
- }
19050
- if (!body)
19051
- return prefix;
19052
- const snippet = body.length > 500 ? `${body.slice(0, 500)}...` : body;
19053
- return `${prefix}: ${snippet}`;
19054
- }
19055
-
19056
19083
  // src/lib/balance.ts
19057
19084
  async function fetchCreditsBalance(auth) {
19058
19085
  if (!auth.twinToken)
19059
19086
  return null;
19060
- const base = (auth.billingBase || "https://billing.heyamiko.com").replace(/\/$/, "");
19061
19087
  try {
19062
- const res = await fetch(`${base}/api/credits`, {
19063
- headers: { Authorization: `Bearer ${auth.twinToken}` }
19064
- });
19065
- if (!res.ok)
19066
- return null;
19067
- const data = await res.json();
19068
- const raw = data.displayBalance ?? Number(data.credits?.balance ?? 0);
19088
+ const data = await amikoWebFetch(auth, `/api/proxy/service/api/credits`);
19089
+ const raw = data.displayBalance;
19069
19090
  return typeof raw === "number" && isFinite(raw) ? Math.round(raw) : null;
19070
19091
  } catch {
19071
19092
  return null;
@@ -19077,8 +19098,7 @@ async function printBalanceRemaining(auth) {
19077
19098
  console.log(dim("Balance: (could not fetch)"));
19078
19099
  return;
19079
19100
  }
19080
- const usd2 = (balance / 1e4).toFixed(2);
19081
- console.log(label("Balance", `${balance.toLocaleString()} credits (~$${usd2})`));
19101
+ console.log(label("Balance", `${balance.toLocaleString()} credits`));
19082
19102
  }
19083
19103
 
19084
19104
  // src/lib/mpp-topup.ts
@@ -19170,8 +19190,7 @@ function registerCreditsCommand(program2) {
19170
19190
  console.log(dim(" Could not fetch credit balance"));
19171
19191
  return;
19172
19192
  }
19173
- const usd2 = (balance / 1e4).toFixed(2);
19174
- console.log(label("Balance", `${source_default.bold(balance.toLocaleString())} credits (~$${usd2})`));
19193
+ console.log(label("Balance", source_default.bold(`${balance.toLocaleString()} credits`)));
19175
19194
  });
19176
19195
  credits.command("topup [amount]").description("Top up credits by spending tokens from your agent wallet. <amount> is in credits (10,000 = $1.00). Use --usd <n> to specify dollars, or --spend <n> to specify a token amount (e.g. 1 SOL worth).").option("--token <TOKEN>", "Token to use for top-up").option("--chain <CHAIN>", "Chain to use for top-up").option("--wallet <ADDR>", "Wallet address to use").option("--method <METHOD>", "Payment method: auto, crossmint, mpp", "auto").option("--key-id <ID>", "Platform API key ID (required for --method mpp)").option("--usd <amount>", "Amount in USD instead of credits (e.g. --usd 1 = 10,000 credits)").option("--spend <amount>", "Spend this many units of --token and convert to credits at current price").option("--yes", "Skip the interactive Proceed? prompt").action(async (amountStr, options) => {
19177
19196
  const inputs = [amountStr, options.usd, options.spend].filter((v) => v != null && v !== "");
@@ -19307,6 +19326,7 @@ function registerCreditsCommand(program2) {
19307
19326
  if (mppResult.credits?.balance)
19308
19327
  console.log(label("Balance", String(mppResult.credits.balance)));
19309
19328
  console.log(success("Credits topped up via MPP."));
19329
+ console.log(dim("Balance may take a moment to reflect on-chain confirmation — re-run `amiko credits balance` if it looks stale."));
19310
19330
  return;
19311
19331
  }
19312
19332
  console.log(heading("Top-up Summary"));
@@ -19351,6 +19371,7 @@ function registerCreditsCommand(program2) {
19351
19371
  }
19352
19372
  console.log("");
19353
19373
  console.log(success("Credits topped up."));
19374
+ console.log(dim("Balance may take a moment to reflect on-chain confirmation — re-run `amiko credits balance` if it looks stale."));
19354
19375
  } catch (e) {
19355
19376
  spinner.stop();
19356
19377
  console.error(error(e instanceof Error ? e.message : "Top-up failed"));
@@ -19465,14 +19486,15 @@ function registerSwapCommand(program2) {
19465
19486
  console.log(label("Route", dim(route)));
19466
19487
  }
19467
19488
  console.log("");
19468
- console.log(dim(`To execute: amiko swap send ${amountStr} ${fromToken} ${toToken}`));
19489
+ console.log(dim(`To execute: amiko wallets swap send ${amountStr} ${fromToken} ${toToken}` + `
19490
+ (uses your default Solana wallet; pass --wallet <address> to override)`));
19469
19491
  } catch (err) {
19470
19492
  spinner.stop();
19471
19493
  console.error(error(err instanceof Error ? err.message : "Failed to get quote"));
19472
19494
  process.exit(1);
19473
19495
  }
19474
19496
  });
19475
- cmd.command("send").description("Execute a token swap on Solana via Crossmint wallet").argument("<amount>", "Amount to swap (e.g., 5, 0.1)").argument("<from>", "Input token (sol, usdc, usdt, amiko, bonk, or mint address)").argument("<to>", "Output token (sol, usdc, usdt, amiko, bonk, or mint address)").option("--wallet <address>", "Wallet address (uses config wallet by default)").option("--slippage <bps>", "Slippage tolerance in basis points", "50").option("--yes", "Skip confirmation").action(async (amountStr, fromToken, toToken, opts) => {
19497
+ cmd.command("send").description("Execute a token swap on Solana via Crossmint wallet").argument("<amount>", "Amount to swap (e.g., 5, 0.1)").argument("<from>", "Input token (sol, usdc, usdt, amiko, bonk, or mint address)").argument("<to>", "Output token (sol, usdc, usdt, amiko, bonk, or mint address)").option("--wallet <address>", "Solana wallet to swap from (defaults to your Solana wallet — run `amiko wallets create --chain solana` if none)").option("--slippage <bps>", "Slippage tolerance in basis points", "50").option("--yes", "Skip confirmation").action(async (amountStr, fromToken, toToken, opts) => {
19476
19498
  try {
19477
19499
  let auth;
19478
19500
  try {
@@ -19481,9 +19503,11 @@ function registerSwapCommand(program2) {
19481
19503
  console.error(error(e instanceof Error ? e.message : "auth failed"));
19482
19504
  process.exit(1);
19483
19505
  }
19484
- const wallet = opts.wallet;
19485
- if (!wallet) {
19486
- console.error(error("No Solana wallet specified. Provide --wallet <address>."));
19506
+ let wallet;
19507
+ try {
19508
+ wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
19509
+ } catch (e) {
19510
+ console.error(error(e instanceof Error ? e.message : String(e)));
19487
19511
  process.exit(1);
19488
19512
  }
19489
19513
  const input = resolveToken(fromToken);
@@ -19514,7 +19538,7 @@ function registerSwapCommand(program2) {
19514
19538
  cost: "Solana gas + 10bps platform fee",
19515
19539
  summary: `swap ${amountStr} ${fromToken.toUpperCase()} → ~${(Number(quote.outAmount) / 10 ** output.decimals).toFixed(6)} ${output.symbol}`,
19516
19540
  yes: opts.yes,
19517
- commandExample: `amiko swap send ${amountStr} ${fromToken} ${toToken}`
19541
+ commandExample: `amiko wallets swap send ${amountStr} ${fromToken} ${toToken}`
19518
19542
  });
19519
19543
  const swapSpinner = ora("Executing swap via platform...").start();
19520
19544
  let swapData;
@@ -19561,7 +19585,7 @@ function registerSwapCommand(program2) {
19561
19585
  console.log(` ${source_default.cyan(t.symbol.padEnd(10))} ${t.name.padEnd(25)} ${dim(truncAddr(t.address))}`);
19562
19586
  }
19563
19587
  console.log("");
19564
- console.log(dim("Use symbol or mint address: amiko swap quote 5 usdt usdc"));
19588
+ console.log(dim("Use symbol or mint address: amiko wallets swap quote 5 usdt usdc"));
19565
19589
  } catch (err) {
19566
19590
  spinner.stop();
19567
19591
  console.error(error(err instanceof Error ? err.message : "Failed to fetch tokens"));
@@ -19582,6 +19606,71 @@ function detectPackageManager() {
19582
19606
  return "npm";
19583
19607
  }
19584
19608
  }
19609
+ function isPrerelease(v) {
19610
+ return v.includes("-");
19611
+ }
19612
+ function compareSemver(a, b) {
19613
+ const [aMain, aPre] = a.split("-", 2);
19614
+ const [bMain, bPre] = b.split("-", 2);
19615
+ const an = aMain.split(".").map(Number);
19616
+ const bn = bMain.split(".").map(Number);
19617
+ for (let i = 0;i < 3; i++) {
19618
+ if ((an[i] ?? 0) !== (bn[i] ?? 0))
19619
+ return (an[i] ?? 0) > (bn[i] ?? 0) ? 1 : -1;
19620
+ }
19621
+ if (!aPre && !bPre)
19622
+ return 0;
19623
+ if (!aPre)
19624
+ return 1;
19625
+ if (!bPre)
19626
+ return -1;
19627
+ const ap = aPre.split(".");
19628
+ const bp = bPre.split(".");
19629
+ for (let i = 0;i < Math.max(ap.length, bp.length); i++) {
19630
+ const x = ap[i];
19631
+ const y = bp[i];
19632
+ if (x === undefined)
19633
+ return -1;
19634
+ if (y === undefined)
19635
+ return 1;
19636
+ const xn = /^\d+$/.test(x) ? Number(x) : NaN;
19637
+ const yn = /^\d+$/.test(y) ? Number(y) : NaN;
19638
+ if (!isNaN(xn) && !isNaN(yn)) {
19639
+ if (xn !== yn)
19640
+ return xn > yn ? 1 : -1;
19641
+ } else if (!isNaN(xn)) {
19642
+ return -1;
19643
+ } else if (!isNaN(yn)) {
19644
+ return 1;
19645
+ } else if (x !== y) {
19646
+ return x > y ? 1 : -1;
19647
+ }
19648
+ }
19649
+ return 0;
19650
+ }
19651
+ async function fetchDistTags(pkg) {
19652
+ const url2 = `https://registry.npmjs.org/-/package/${encodeURIComponent(pkg)}/dist-tags`;
19653
+ const res = await fetch(url2);
19654
+ if (!res.ok)
19655
+ throw new Error(`registry responded ${res.status}`);
19656
+ return await res.json();
19657
+ }
19658
+ async function resolveTargetVersion(pkg, currentVersion) {
19659
+ if (!isPrerelease(currentVersion)) {
19660
+ return { version: "latest", channel: "latest" };
19661
+ }
19662
+ const tags = await fetchDistTags(pkg);
19663
+ const latest = tags.latest;
19664
+ const beta = tags.beta;
19665
+ if (latest && beta) {
19666
+ return compareSemver(latest, beta) >= 0 ? { version: latest, channel: "latest" } : { version: beta, channel: "beta" };
19667
+ }
19668
+ if (beta)
19669
+ return { version: beta, channel: "beta" };
19670
+ if (latest)
19671
+ return { version: latest, channel: "latest" };
19672
+ return { version: "latest", channel: "latest" };
19673
+ }
19585
19674
  var DATA_CLI = "/data/.amiko-cli";
19586
19675
  var NPM_GLOBAL_CANDIDATES = [
19587
19676
  "/usr/local/lib/node_modules/@heyamiko/amiko-cli",
@@ -19627,17 +19716,21 @@ function registerUpdateCommand(program2) {
19627
19716
  console.log(heading("Update"));
19628
19717
  console.log(label("Current", `v${currentVersion}`));
19629
19718
  console.log(label("Package manager", pm));
19719
+ let target;
19720
+ try {
19721
+ target = await resolveTargetVersion(pkg, currentVersion);
19722
+ } catch {
19723
+ target = { version: "latest", channel: "latest" };
19724
+ }
19725
+ const installSpec = `${pkg}@${target.version}`;
19726
+ console.log(label("Target", target.version === "latest" ? "latest" : `${target.version} (${target.channel})`));
19630
19727
  console.log("");
19631
- const spinner = ora("Installing latest version...").start();
19728
+ const spinner = ora(`Installing ${installSpec}...`).start();
19632
19729
  try {
19633
19730
  if (pm === "bun") {
19634
- execFileSync2("bun", ["install", "-g", `${pkg}@latest`], {
19635
- stdio: "pipe"
19636
- });
19731
+ execFileSync2("bun", ["install", "-g", installSpec], { stdio: "pipe" });
19637
19732
  } else {
19638
- execFileSync2("npm", ["install", "-g", `${pkg}@latest`], {
19639
- stdio: "pipe"
19640
- });
19733
+ execFileSync2("npm", ["install", "-g", installSpec], { stdio: "pipe" });
19641
19734
  }
19642
19735
  spinner.stop();
19643
19736
  let syncedTwin = false;
@@ -19667,7 +19760,7 @@ function registerUpdateCommand(program2) {
19667
19760
  console.error(error("Auto-update failed."));
19668
19761
  console.log("");
19669
19762
  console.log(dim("Try manually:"));
19670
- console.log(dim(` ${pm} install -g ${pkg}@latest`));
19763
+ console.log(dim(` ${pm} install -g ${installSpec}`));
19671
19764
  process.exit(1);
19672
19765
  }
19673
19766
  });
@@ -19691,12 +19784,12 @@ async function reportHttpError(res, label2) {
19691
19784
  // src/commands/search.ts
19692
19785
  var TREASURY = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
19693
19786
  function registerSearchCommand(program2) {
19694
- 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>", "Solana wallet address to pay from (required)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
19787
+ 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) => {
19695
19788
  await requireApproval({
19696
19789
  cost: `1 ${opts.token || "AMIKO"}`,
19697
19790
  summary: `X/Twitter search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
19698
19791
  yes: opts.yes,
19699
- commandExample: `amiko search ${JSON.stringify(query)}${opts.token && opts.token !== "AMIKO" ? ` --token ${opts.token}` : ""}`
19792
+ commandExample: `amiko markets search ${JSON.stringify(query)}${opts.token && opts.token !== "AMIKO" ? ` --token ${opts.token}` : ""}`
19700
19793
  });
19701
19794
  const spinner = ora("X Search...").start();
19702
19795
  try {
@@ -19713,10 +19806,12 @@ function registerSearchCommand(program2) {
19713
19806
  const token = opts.token || "AMIKO";
19714
19807
  const chain = "solana";
19715
19808
  const amount2 = token === "AMIKO" ? "1" : "0.01";
19716
- const wallet = opts.wallet;
19717
- if (!wallet) {
19809
+ let wallet;
19810
+ try {
19811
+ wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
19812
+ } catch (e) {
19718
19813
  spinner.stop();
19719
- console.error(error("Provide --wallet <address> to pay from."));
19814
+ console.error(error(e instanceof Error ? e.message : String(e)));
19720
19815
  process.exit(1);
19721
19816
  }
19722
19817
  spinner.text = `Paying ${amount2} ${token} (${chain})...`;
@@ -19790,12 +19885,12 @@ function registerSearchCommand(program2) {
19790
19885
  // src/commands/image.ts
19791
19886
  var TREASURY2 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
19792
19887
  function registerImageCommand(program2) {
19793
- program2.command("image <prompt>").alias("img").description("Generate an image — costs 5 AMIKO").option("--size <size>", "Image size: 1024x1024, 1024x1792, 1792x1024", "1024x1024").option("--quality <q>", "Quality: auto, low, medium, high (gpt-image-1) or standard, hd (dall-e-3)", "auto").option("--model <model>", "Model: gpt-image-1 or dall-e-3", "gpt-image-1").option("--background <bg>", "Background: transparent, opaque, auto (gpt-image-1 only)").option("--format <fmt>", "Output: png, webp, jpeg (gpt-image-1 only)", "png").option("--wallet <address>", "Solana wallet address to pay from (required)").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (prompt, opts) => {
19888
+ program2.command("image <prompt>").alias("img").description("Generate an image — costs 5 AMIKO").option("--size <size>", "Image size: 1024x1024, 1024x1792, 1792x1024", "1024x1024").option("--quality <q>", "Quality: auto, low, medium, high (gpt-image-1) or standard, hd (dall-e-3)", "auto").option("--model <model>", "Model: gpt-image-1 or dall-e-3", "gpt-image-1").option("--background <bg>", "Background: transparent, opaque, auto (gpt-image-1 only)").option("--format <fmt>", "Output: png, webp, jpeg (gpt-image-1 only)", "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) => {
19794
19889
  await requireApproval({
19795
19890
  cost: "5 AMIKO",
19796
19891
  summary: `Generate image: ${prompt.slice(0, 80)}${prompt.length > 80 ? "…" : ""}`,
19797
19892
  yes: opts.yes,
19798
- commandExample: `amiko image ${JSON.stringify(prompt)}`
19893
+ commandExample: `amiko markets image ${JSON.stringify(prompt)}`
19799
19894
  });
19800
19895
  const isTTY = process.stderr.isTTY;
19801
19896
  const spinner = isTTY ? ora("Generating image...").start() : null;
@@ -19836,10 +19931,12 @@ function registerImageCommand(program2) {
19836
19931
  process.exit(1);
19837
19932
  }
19838
19933
  const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
19839
- const wallet = opts.wallet;
19840
- if (!wallet) {
19934
+ let wallet;
19935
+ try {
19936
+ wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
19937
+ } catch (e) {
19841
19938
  stopProgress();
19842
- console.error(error("Provide --wallet <address> to pay from."));
19939
+ console.error(error(e instanceof Error ? e.message : String(e)));
19843
19940
  process.exit(1);
19844
19941
  }
19845
19942
  setStatus("Paying 5 AMIKO...");
@@ -19899,7 +19996,7 @@ function registerImageCommand(program2) {
19899
19996
  console.log(label("URL", img.url));
19900
19997
  } else if (img.b64_json) {
19901
19998
  console.log(label("Format", "base64 (inline image data)"));
19902
- console.log(dim(` ${img.b64_json.length.toLocaleString()} chars — save with: amiko image "..." --raw | jq -r '.images[0].b64_json' | base64 -d > image.png`));
19999
+ console.log(dim(` ${img.b64_json.length.toLocaleString()} chars — save with: amiko markets image "..." --raw | jq -r '.images[0].b64_json' | base64 -d > image.png`));
19903
20000
  }
19904
20001
  if (img.revised_prompt && img.revised_prompt !== prompt) {
19905
20002
  console.log(label("Revised", dim(img.revised_prompt)));
@@ -19929,12 +20026,12 @@ async function payAmiko(auth, wallet, amount2) {
19929
20026
  }
19930
20027
  function registerAmazonCommand(program2) {
19931
20028
  const cmd = program2.command("amazon").description("Search and buy products on Amazon — paid with AMIKO");
19932
- cmd.command("search <query>").description("Search Amazon products — 1 AMIKO").option("--limit <n>", "Number of results", "5").option("--wallet <address>", "Solana wallet address to pay from (required)").option("--raw", "Output raw JSON").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (query, opts) => {
20029
+ 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) => {
19933
20030
  await requireApproval({
19934
20031
  cost: "1 AMIKO",
19935
20032
  summary: `Amazon product search: ${query.slice(0, 80)}${query.length > 80 ? "…" : ""}`,
19936
20033
  yes: opts.yes,
19937
- commandExample: `amiko amazon search ${JSON.stringify(query)}`
20034
+ commandExample: `amiko markets amazon search ${JSON.stringify(query)}`
19938
20035
  });
19939
20036
  const spinner = ora("Searching Amazon...").start();
19940
20037
  try {
@@ -19948,10 +20045,12 @@ function registerAmazonCommand(program2) {
19948
20045
  process.exit(1);
19949
20046
  }
19950
20047
  const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
19951
- const wallet = opts.wallet;
19952
- if (!wallet) {
20048
+ let wallet;
20049
+ try {
20050
+ wallet = await resolveWalletAddress(auth, opts.wallet, "solana");
20051
+ } catch (e) {
19953
20052
  spinner.stop();
19954
- console.error(error("Provide --wallet <address>."));
20053
+ console.error(error(e instanceof Error ? e.message : String(e)));
19955
20054
  process.exit(1);
19956
20055
  }
19957
20056
  spinner.text = "Paying 1 AMIKO...";
@@ -20121,12 +20220,12 @@ async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, b
20121
20220
  }
20122
20221
  function registerServicesCommand(program2) {
20123
20222
  const svc = program2.command("service").alias("svc").description("Call any MPP service — pays with AMIKO tokens");
20124
- svc.command("tts <voiceId> <text>").description("Text-to-speech — 1 AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Solana wallet address to pay from (required)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (voiceId, text, opts) => {
20223
+ svc.command("tts <voiceId> <text>").description("Text-to-speech — 1 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 (voiceId, text, opts) => {
20125
20224
  await requireApproval({
20126
20225
  cost: "1 AMIKO",
20127
20226
  summary: `Text-to-speech (voice ${voiceId}): "${text.slice(0, 60)}${text.length > 60 ? "…" : ""}"`,
20128
20227
  yes: opts.yes,
20129
- commandExample: `amiko service tts ${voiceId} ${JSON.stringify(text)}`
20228
+ commandExample: `amiko markets service tts ${voiceId} ${JSON.stringify(text)}`
20130
20229
  });
20131
20230
  await callService({
20132
20231
  name: "Text-to-Speech",
@@ -20137,12 +20236,12 @@ function registerServicesCommand(program2) {
20137
20236
  opts
20138
20237
  });
20139
20238
  });
20140
- svc.command("stt <audioUrl>").description("Speech-to-text — 1 AMIKO").option("--raw", "Output raw response").option("--wallet <address>", "Solana wallet address to pay from (required)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (audioUrl, opts) => {
20239
+ svc.command("stt <audioUrl>").description("Speech-to-text — 1 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 (audioUrl, opts) => {
20141
20240
  await requireApproval({
20142
20241
  cost: "1 AMIKO",
20143
20242
  summary: `Speech-to-text on ${audioUrl}`,
20144
20243
  yes: opts.yes,
20145
- commandExample: `amiko service stt ${JSON.stringify(audioUrl)}`
20244
+ commandExample: `amiko markets service stt ${JSON.stringify(audioUrl)}`
20146
20245
  });
20147
20246
  await callService({
20148
20247
  name: "Speech-to-Text",
@@ -20171,12 +20270,12 @@ function registerServicesCommand(program2) {
20171
20270
  console.log(JSON.stringify(data, null, 2));
20172
20271
  }
20173
20272
  });
20174
- svc.command("chat <message>").description("AI chat — 1 AMIKO").option("--model <model>", "Model to use").option("--raw", "Output raw response").option("--wallet <address>", "Solana wallet address to pay from (required)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (message, opts) => {
20273
+ svc.command("chat <message>").description("AI chat — 1 AMIKO").option("--model <model>", "Model to use").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 (message, opts) => {
20175
20274
  await requireApproval({
20176
20275
  cost: "1 AMIKO",
20177
20276
  summary: `AI chat${opts.model ? ` (model ${opts.model})` : ""}: "${message.slice(0, 60)}${message.length > 60 ? "…" : ""}"`,
20178
20277
  yes: opts.yes,
20179
- commandExample: `amiko service chat ${JSON.stringify(message)}${opts.model ? ` --model ${opts.model}` : ""}`
20278
+ commandExample: `amiko markets service chat ${JSON.stringify(message)}${opts.model ? ` --model ${opts.model}` : ""}`
20180
20279
  });
20181
20280
  const body = { message };
20182
20281
  if (opts.model)
@@ -20190,13 +20289,13 @@ function registerServicesCommand(program2) {
20190
20289
  opts
20191
20290
  });
20192
20291
  });
20193
- 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>", "Solana wallet address to pay from (required)").option("--yes", "Skip the pre-spend confirmation (required in non-interactive shells)").action(async (method, path2, bodyStr, opts) => {
20292
+ 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) => {
20194
20293
  const costLabel = opts.usdc ? `${opts.usdc} USDC` : opts.amiko ? `${opts.amiko} AMIKO` : "auto-priced AMIKO (check /v1/ list for endpoint price first)";
20195
20294
  await requireApproval({
20196
20295
  cost: costLabel,
20197
20296
  summary: `${method.toUpperCase()} ${path2}`,
20198
20297
  yes: opts.yes,
20199
- commandExample: `amiko service call ${method.toUpperCase()} ${path2}${bodyStr ? ` ${JSON.stringify(bodyStr)}` : ""}${opts.amiko ? ` --amiko ${opts.amiko}` : ""}${opts.usdc ? ` --usdc ${opts.usdc}` : ""}`
20298
+ commandExample: `amiko markets service call ${method.toUpperCase()} ${path2}${bodyStr ? ` ${JSON.stringify(bodyStr)}` : ""}${opts.amiko ? ` --amiko ${opts.amiko}` : ""}${opts.usdc ? ` --usdc ${opts.usdc}` : ""}`
20200
20299
  });
20201
20300
  let body;
20202
20301
  if (bodyStr) {
@@ -20255,10 +20354,12 @@ async function callService(params) {
20255
20354
  process.exit(1);
20256
20355
  }
20257
20356
  const mppBase2 = (config2.apiBase || "https://mpp.amikomarkets.com").replace(/\/$/, "");
20258
- const wallet = params.opts.wallet;
20259
- if (!wallet) {
20357
+ let wallet;
20358
+ try {
20359
+ wallet = await resolveWalletAddress(auth, params.opts.wallet, "solana");
20360
+ } catch (e) {
20260
20361
  spinner.stop();
20261
- console.error(error("Provide --wallet <address>."));
20362
+ console.error(error(e instanceof Error ? e.message : String(e)));
20262
20363
  process.exit(1);
20263
20364
  }
20264
20365
  const token = params.usdc ? "USDC" : "AMIKO";
@@ -20391,7 +20492,7 @@ function parseBool(raw, flag) {
20391
20492
  throw new Error(`${flag} must be true or false, got "${raw}"`);
20392
20493
  }
20393
20494
  function registerTwinCommand(program2) {
20394
- program2.command("twin:update").description("Update twin name, description, or public visibility").option("--name <name>", "Display name").option("--description <text>", "Description").option("--public <bool>", "Public visibility (true/false)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation when changing visibility").option("--json", "Output as JSON").action(async (opts) => {
20495
+ program2.command("update").description("Update twin name, description, or public visibility").option("--name <name>", "Display name").option("--description <text>", "Description").option("--public <bool>", "Public visibility (true/false)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation when changing visibility").option("--json", "Output as JSON").action(async (opts) => {
20395
20496
  if (opts.name === undefined && opts.description === undefined && opts.public === undefined) {
20396
20497
  console.error(error("Pass at least one of --name, --description, --public"));
20397
20498
  process.exit(1);
@@ -20426,7 +20527,7 @@ function registerTwinCommand(program2) {
20426
20527
  action: `Set twin visibility to ${next ? "PUBLIC" : "PRIVATE"}`,
20427
20528
  detail: next ? "Anyone on amiko.com will be able to find and chat with this twin." : "The twin will no longer be discoverable.",
20428
20529
  yes: opts.yes,
20429
- commandExample: `amiko twin:update --public ${next}`
20530
+ commandExample: `amiko twin update --public ${next}`
20430
20531
  });
20431
20532
  }
20432
20533
  const spinner = opts.json ? null : ora("Updating twin...").start();
@@ -20548,6 +20649,18 @@ function detectMime(buffer, name) {
20548
20649
  }
20549
20650
 
20550
20651
  // src/commands/docs.ts
20652
+ function fmtBytes(n) {
20653
+ if (!n || n <= 0)
20654
+ return "-";
20655
+ const units = ["B", "KB", "MB", "GB"];
20656
+ let v = n;
20657
+ let i = 0;
20658
+ while (v >= 1024 && i < units.length - 1) {
20659
+ v /= 1024;
20660
+ i++;
20661
+ }
20662
+ return `${v.toFixed(v >= 10 ? 0 : 1)} ${units[i]}`;
20663
+ }
20551
20664
  function twinFromFlag(opts) {
20552
20665
  const config2 = loadConfig();
20553
20666
  const auth = resolveAuth();
@@ -20561,20 +20674,8 @@ function twinFromFlag(opts) {
20561
20674
  throw e;
20562
20675
  }
20563
20676
  }
20564
- function fmtBytes(n) {
20565
- if (!n || n <= 0)
20566
- return "-";
20567
- const units = ["B", "KB", "MB", "GB"];
20568
- let v = n;
20569
- let i = 0;
20570
- while (v >= 1024 && i < units.length - 1) {
20571
- v /= 1024;
20572
- i++;
20573
- }
20574
- return `${v.toFixed(v >= 10 ? 0 : 1)} ${units[i]}`;
20575
- }
20576
20677
  function registerDocsCommand(program2) {
20577
- program2.command("docs").description("List uploaded documents for a twin").option("--twin <idOrName>", "Target twin id or name").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
20678
+ program2.command("list").description("List uploaded documents for a twin").option("--twin <idOrName>", "Target twin id or name").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
20578
20679
  const { twinId, auth } = twinFromFlag(opts);
20579
20680
  const spinner = opts.json ? null : ora("Loading documents...").start();
20580
20681
  try {
@@ -20590,15 +20691,15 @@ function registerDocsCommand(program2) {
20590
20691
  [
20591
20692
  dim("ID"),
20592
20693
  dim("NAME"),
20593
- dim("SIZE"),
20694
+ dim("TYPE"),
20594
20695
  dim("STATUS"),
20595
20696
  dim("CREATED")
20596
20697
  ],
20597
20698
  ...d.docs.map((doc2) => [
20598
20699
  doc2.id.slice(0, 8),
20599
20700
  doc2.title || doc2.filename,
20600
- fmtBytes(doc2.file_size),
20601
- doc2.status || "-",
20701
+ doc2.file_type || "-",
20702
+ doc2.is_processed ? "processed" : doc2.is_parsed ? "parsed" : "pending",
20602
20703
  doc2.created_at?.slice(0, 10) || "-"
20603
20704
  ])
20604
20705
  ];
@@ -20610,7 +20711,7 @@ function registerDocsCommand(program2) {
20610
20711
  process.exit(1);
20611
20712
  }
20612
20713
  });
20613
- program2.command("docs:upload <source>").description("Upload a document (path | https URL | - for stdin)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--title <title>", "Document title (defaults to filename)").option("--description <text>", "Document description").option("--doc-type <type>", "Document type (e.g. 'other', 'memory')", "other").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
20714
+ program2.command("upload <source>").description("Upload a document (path | https URL | - for stdin)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--title <title>", "Document title (defaults to filename)").option("--description <text>", "Document description").option("--doc-type <type>", "Document type (e.g. 'other', 'memory')", "other").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
20614
20715
  const { twinId, auth } = twinFromFlag(opts);
20615
20716
  const spinner = opts.json ? null : ora("Reading input...").start();
20616
20717
  try {
@@ -20661,13 +20762,13 @@ function registerDocsCommand(program2) {
20661
20762
  process.exit(1);
20662
20763
  }
20663
20764
  });
20664
- program2.command("docs:delete <docId>").description("Delete a document").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (docId, opts) => {
20765
+ program2.command("delete <docId>").description("Delete a document").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (docId, opts) => {
20665
20766
  const { twinId, auth } = twinFromFlag(opts);
20666
20767
  await confirmDestructive({
20667
20768
  action: `Delete document ${docId}`,
20668
20769
  detail: "This will remove the document and its embeddings from the twin's RAG.",
20669
20770
  yes: opts.yes,
20670
- commandExample: `amiko docs:delete ${docId}`
20771
+ commandExample: `amiko docs delete ${docId}`
20671
20772
  });
20672
20773
  const spinner = opts.json ? null : ora("Deleting document...").start();
20673
20774
  try {
@@ -20680,7 +20781,7 @@ function registerDocsCommand(program2) {
20680
20781
  process.exit(1);
20681
20782
  }
20682
20783
  });
20683
- program2.command("docs:presign <source>").description("Get a presigned upload URL for a document (no upload)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename used in the request").option("--json", "Output as JSON").action(async (source, opts) => {
20784
+ program2.command("presign <source>").description("Get a presigned upload URL for a document (no upload)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename used in the request").option("--json", "Output as JSON").action(async (source, opts) => {
20684
20785
  const { twinId, auth } = twinFromFlag(opts);
20685
20786
  const filename = opts.name ?? (source === "-" ? "stdin" : source.split("/").pop() ?? source);
20686
20787
  try {
@@ -20708,7 +20809,7 @@ function twinFromFlag2(opts) {
20708
20809
  }
20709
20810
  }
20710
20811
  function registerVoiceCommand(program2) {
20711
- program2.command("voice:design").description("Design a voice from a text description").requiredOption("--description <text>", "Voice description (min 20 chars)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
20812
+ program2.command("design").description("Design a voice from a text description").requiredOption("--description <text>", "Voice description (min 20 chars)").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
20712
20813
  if (opts.description.trim().length < 20) {
20713
20814
  console.error(error("--description must be at least 20 characters"));
20714
20815
  process.exit(1);
@@ -20728,7 +20829,7 @@ function registerVoiceCommand(program2) {
20728
20829
  console.log(label(`#${i + 1} id`, p.generated_voice_id ?? "?"));
20729
20830
  }
20730
20831
  console.log("");
20731
- console.log(dim("Pick one with: amiko voice:create --sample <id>"));
20832
+ console.log(dim("Pick one with: amiko voice create --sample <id>"));
20732
20833
  }, { json: opts.json });
20733
20834
  } catch (e) {
20734
20835
  spinner?.stop();
@@ -20736,7 +20837,7 @@ function registerVoiceCommand(program2) {
20736
20837
  process.exit(1);
20737
20838
  }
20738
20839
  });
20739
- program2.command("voice:create").description("Create a voice from a previously designed preview").requiredOption("--sample <id>", "generated_voice_id from voice:design").option("--description <text>", "Optional voice description").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
20840
+ program2.command("create").description("Create a voice from a previously designed preview").requiredOption("--sample <id>", "generated_voice_id from voice design").option("--description <text>", "Optional voice description").option("--twin <idOrName>", "Target twin id or name").option("--json", "Output as JSON").action(async (opts) => {
20740
20841
  const { twinId, auth } = twinFromFlag2(opts);
20741
20842
  const spinner = opts.json ? null : ora("Creating voice...").start();
20742
20843
  try {
@@ -20762,7 +20863,7 @@ function registerVoiceCommand(program2) {
20762
20863
  process.exit(1);
20763
20864
  }
20764
20865
  });
20765
- program2.command("voice:clone <source>").description("Clone a voice from an audio file (path | https URL | -)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
20866
+ program2.command("clone <source>").description("Clone a voice from an audio file (path | https URL | -)").option("--twin <idOrName>", "Target twin id or name").option("--name <filename>", "Override filename").option("--stdin", "Read from stdin (source must be -)").option("--json", "Output as JSON").action(async (source, opts) => {
20766
20867
  const { twinId, auth } = twinFromFlag2(opts);
20767
20868
  const spinner = opts.json ? null : ora("Reading audio...").start();
20768
20869
  try {
@@ -20790,13 +20891,13 @@ function registerVoiceCommand(program2) {
20790
20891
  process.exit(1);
20791
20892
  }
20792
20893
  });
20793
- program2.command("voice:reset").description("Reset the twin's voice to default").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
20894
+ program2.command("reset").description("Reset the twin's voice to default").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
20794
20895
  const { twinId, auth } = twinFromFlag2(opts);
20795
20896
  await confirmDestructive({
20796
20897
  action: "Reset voice",
20797
20898
  detail: "This removes the custom voice. You can redesign or clone a new one later.",
20798
20899
  yes: opts.yes,
20799
- commandExample: "amiko voice:reset"
20900
+ commandExample: "amiko voice reset"
20800
20901
  });
20801
20902
  const spinner = opts.json ? null : ora("Resetting voice...").start();
20802
20903
  try {
@@ -20813,7 +20914,7 @@ function registerVoiceCommand(program2) {
20813
20914
 
20814
20915
  // src/commands/avatar.ts
20815
20916
  function registerAvatarCommand(program2) {
20816
- program2.command("avatar:update").description("Update twin avatar image (path | https URL | - for stdin)").requiredOption("--file <source>", "Image source").option("--twin <idOrName>", "Target twin id or name").option("--stdin", "Read from stdin (file must be -)").option("--name <filename>", "Override filename").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
20917
+ program2.command("update").description("Update twin avatar image (path | https URL | - for stdin)").requiredOption("--file <source>", "Image source").option("--twin <idOrName>", "Target twin id or name").option("--stdin", "Read from stdin (file must be -)").option("--name <filename>", "Override filename").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
20817
20918
  const config2 = loadConfig();
20818
20919
  const auth = resolveAuth();
20819
20920
  let twinId;
@@ -20830,7 +20931,7 @@ function registerAvatarCommand(program2) {
20830
20931
  action: "Replace twin avatar",
20831
20932
  detail: "The twin's public avatar will change immediately.",
20832
20933
  yes: opts.yes,
20833
- commandExample: `amiko avatar:update --file ${opts.file}`
20934
+ commandExample: `amiko avatar update --file ${opts.file}`
20834
20935
  });
20835
20936
  const spinner = opts.json ? null : ora("Reading image...").start();
20836
20937
  try {
@@ -20872,27 +20973,27 @@ function registerAvatarCommand(program2) {
20872
20973
 
20873
20974
  // src/commands/friends.ts
20874
20975
  function registerFriendsCommand(program2) {
20875
- program2.command("friends").description("List friends").option("--type <user|twin>", "Filter by friend type").option("--json", "Output as JSON").action(async (opts) => {
20976
+ program2.command("list").description("List friends").option("--type <user|twin>", "Filter by friend type").option("--json", "Output as JSON").action(async (opts) => {
20876
20977
  const auth = resolveAuth();
20877
20978
  const spinner = opts.json ? null : ora("Loading friends...").start();
20878
20979
  try {
20879
20980
  const data = await amikoWebFetch(auth, "/api/friends", { query: { type: opts.type } });
20880
20981
  spinner?.stop();
20881
20982
  renderOutput(data, (d) => {
20882
- const items = d.items ?? [];
20983
+ const items = d.friends ?? [];
20883
20984
  if (items.length === 0) {
20884
20985
  console.log(dim("No friends."));
20885
20986
  return;
20886
20987
  }
20887
20988
  console.log(heading(`Friends (${items.length})`));
20888
20989
  const rows = [
20889
- [dim("ID"), dim("NAME"), dim("HANDLE"), dim("TYPE"), dim("STATUS")],
20990
+ [dim("ID"), dim("NAME"), dim("HANDLE"), dim("TYPE"), dim("SINCE")],
20890
20991
  ...items.map((f) => [
20891
- f.id.slice(0, 8),
20992
+ f.friendship_id.slice(0, 8),
20892
20993
  f.friend.name ?? "-",
20893
20994
  f.friend.handle ?? "-",
20894
20995
  f.friend.type,
20895
- f.status
20996
+ f.since?.slice(0, 10) ?? "-"
20896
20997
  ])
20897
20998
  ];
20898
20999
  console.log(table(rows));
@@ -20903,7 +21004,7 @@ function registerFriendsCommand(program2) {
20903
21004
  process.exit(1);
20904
21005
  }
20905
21006
  });
20906
- program2.command("friends:add").description("Send a friend request to a user by id").requiredOption("--id <userId>", "Target user id").option("--json", "Output as JSON").action(async (opts) => {
21007
+ program2.command("add").description("Send a friend request to a user by id").requiredOption("--id <userId>", "Target user id").option("--json", "Output as JSON").action(async (opts) => {
20907
21008
  const auth = resolveAuth();
20908
21009
  const spinner = opts.json ? null : ora("Sending friend request...").start();
20909
21010
  try {
@@ -20923,7 +21024,7 @@ function registerFriendsCommand(program2) {
20923
21024
  process.exit(1);
20924
21025
  }
20925
21026
  });
20926
- program2.command("friends:accept <friendshipId>").description("Accept a pending friend request").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
21027
+ program2.command("accept <friendshipId>").description("Accept a pending friend request").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
20927
21028
  const auth = resolveAuth();
20928
21029
  const spinner = opts.json ? null : ora("Accepting...").start();
20929
21030
  try {
@@ -20936,11 +21037,11 @@ function registerFriendsCommand(program2) {
20936
21037
  process.exit(1);
20937
21038
  }
20938
21039
  });
20939
- program2.command("friends:remove <friendshipId>").description("Remove a friend").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
21040
+ program2.command("remove <friendshipId>").description("Remove a friend").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
20940
21041
  await confirmDestructive({
20941
21042
  action: `Remove friend ${friendshipId}`,
20942
21043
  yes: opts.yes,
20943
- commandExample: `amiko friends:remove ${friendshipId}`
21044
+ commandExample: `amiko friends remove ${friendshipId}`
20944
21045
  });
20945
21046
  const auth = resolveAuth();
20946
21047
  const spinner = opts.json ? null : ora("Removing...").start();
@@ -20999,7 +21100,9 @@ function registerFeedCommand(program2) {
20999
21100
  process.exit(1);
21000
21101
  }
21001
21102
  });
21002
- program2.command("post:create").description("Create a post on your feed").requiredOption("--content <text>", "Post body (required, non-empty)").option("--visibility <public|private>", "Post visibility", "public").option("--media <url...>", "One or more media URLs").option("--json", "Output as JSON").action(async (opts) => {
21103
+ }
21104
+ function registerPostCommand(program2) {
21105
+ program2.command("create").description("Create a post on your feed").requiredOption("--content <text>", "Post body (required, non-empty)").option("--visibility <public|private>", "Post visibility", "public").option("--media <url...>", "One or more media URLs").option("--json", "Output as JSON").action(async (opts) => {
21003
21106
  const auth = resolveAuth();
21004
21107
  const visibility = opts.visibility === "private" ? "private" : "public";
21005
21108
  const spinner = opts.json ? null : ora("Publishing post...").start();
@@ -21027,7 +21130,7 @@ function registerFeedCommand(program2) {
21027
21130
  process.exit(1);
21028
21131
  }
21029
21132
  });
21030
- program2.command("post:comment").description("Comment on a post").requiredOption("--id <postId>", "Target post id").requiredOption("--comment <text>", "Comment body").option("--media <url...>", "One or more media URLs to attach").option("--twin <idOrName>", "Post as this twin (agent mode)").option("--json", "Output as JSON").action(async (opts) => {
21133
+ program2.command("comment").description("Comment on a post").requiredOption("--id <postId>", "Target post id").requiredOption("--comment <text>", "Comment body").option("--media <url...>", "One or more media URLs to attach").option("--twin <idOrName>", "Post as this twin (agent mode)").option("--json", "Output as JSON").action(async (opts) => {
21031
21134
  const config2 = loadConfig();
21032
21135
  const auth = resolveAuth();
21033
21136
  let twinId;
@@ -21079,7 +21182,7 @@ function twinFromFlag3(opts) {
21079
21182
  }
21080
21183
  }
21081
21184
  function registerComposioCommand(program2) {
21082
- program2.command("composio:connect").description("Start Composio OAuth for an app (gmail, github, ...)").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--redirect <url>", "Override OAuth redirect URL").option("--force", "Force reconnect if already connected").option("--json", "Output as JSON").action(async (opts) => {
21185
+ program2.command("connect").description("Start Composio OAuth for an app (gmail, github, ...)").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--redirect <url>", "Override OAuth redirect URL").option("--force", "Force reconnect if already connected").option("--json", "Output as JSON").action(async (opts) => {
21083
21186
  const { twinId, auth } = twinFromFlag3(opts);
21084
21187
  const spinner = opts.json ? null : ora(`Connecting ${opts.app}...`).start();
21085
21188
  try {
@@ -21115,13 +21218,13 @@ function registerComposioCommand(program2) {
21115
21218
  process.exit(1);
21116
21219
  }
21117
21220
  });
21118
- program2.command("composio:disconnect").description("Disconnect a Composio app for the twin").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
21221
+ program2.command("disconnect").description("Disconnect a Composio app for the twin").requiredOption("--app <slug>", "App name (e.g. gmail)").option("--twin <idOrName>", "Target twin id or name").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (opts) => {
21119
21222
  const { twinId, auth } = twinFromFlag3(opts);
21120
21223
  await confirmDestructive({
21121
21224
  action: `Disconnect ${opts.app}`,
21122
21225
  detail: "The twin will lose access to the app's tools until reconnected.",
21123
21226
  yes: opts.yes,
21124
- commandExample: `amiko composio:disconnect --app ${opts.app}`
21227
+ commandExample: `amiko composio disconnect --app ${opts.app}`
21125
21228
  });
21126
21229
  const spinner = opts.json ? null : ora("Finding connection...").start();
21127
21230
  try {
@@ -21145,6 +21248,141 @@ function registerComposioCommand(program2) {
21145
21248
  });
21146
21249
  }
21147
21250
 
21251
+ // src/commands/wallets.ts
21252
+ function formatBalance(b) {
21253
+ const n = Number(b.amount) / 10 ** b.decimals;
21254
+ if (!isFinite(n))
21255
+ return b.amount;
21256
+ return n.toLocaleString(undefined, { maximumFractionDigits: 6 });
21257
+ }
21258
+ function registerWalletsBalanceCommand(program2) {
21259
+ program2.command("balance <address>").description("Sync and show balance for a single wallet").option("--json", "Output raw JSON").action(async (address2, opts) => {
21260
+ let auth;
21261
+ try {
21262
+ auth = requireAuth();
21263
+ } catch (e) {
21264
+ console.error(error(e instanceof Error ? e.message : String(e)));
21265
+ process.exit(1);
21266
+ }
21267
+ const spinner = opts.json ? null : ora("Syncing wallet balance...").start();
21268
+ try {
21269
+ const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets/${encodeURIComponent(address2)}/balance`);
21270
+ spinner?.stop();
21271
+ if (opts.json) {
21272
+ console.log(JSON.stringify(data, null, 2));
21273
+ return;
21274
+ }
21275
+ console.log(heading("Wallet Balance"));
21276
+ console.log("");
21277
+ console.log(label("Address", data.walletAddress));
21278
+ console.log(label("Chain", data.chain_type));
21279
+ if (data.last_synced_at) {
21280
+ console.log(label("Synced", data.last_synced_at));
21281
+ }
21282
+ const entries = Object.entries(data.balances || {});
21283
+ if (entries.length === 0) {
21284
+ console.log(dim(" No balances."));
21285
+ return;
21286
+ }
21287
+ console.log("");
21288
+ const rows = entries.map(([key, v]) => [` ${key}`, formatBalance(v)]);
21289
+ console.log(table(rows));
21290
+ } catch (e) {
21291
+ spinner?.stop();
21292
+ console.error(error(e instanceof Error ? e.message : "Failed to fetch balance"));
21293
+ process.exit(1);
21294
+ }
21295
+ });
21296
+ }
21297
+ function registerWalletsCreateCommand(program2) {
21298
+ program2.command("create").description("Create a new wallet for the twin on solana or base").requiredOption("--chain <chain>", "Chain to create the wallet on (solana | base)").option("--custodian <custodian>", "Custodian (crossmint | amiko). Defaults to crossmint; amiko is solana-only.", "crossmint").option("--json", "Output raw JSON").action(async (opts) => {
21299
+ const chain = opts.chain.toLowerCase();
21300
+ if (!["solana", "base"].includes(chain)) {
21301
+ console.error(error(`Unsupported chain '${opts.chain}'. Use solana or base.`));
21302
+ process.exit(1);
21303
+ }
21304
+ let auth;
21305
+ try {
21306
+ auth = requireAuth();
21307
+ } catch (e) {
21308
+ console.error(error(e instanceof Error ? e.message : String(e)));
21309
+ process.exit(1);
21310
+ }
21311
+ const spinner = opts.json ? null : ora(`Creating ${chain} wallet...`).start();
21312
+ try {
21313
+ const wallet = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets`, { method: "POST", body: { chain, custodian: opts.custodian } });
21314
+ spinner?.stop();
21315
+ if (opts.json) {
21316
+ console.log(JSON.stringify(wallet, null, 2));
21317
+ return;
21318
+ }
21319
+ console.log(success(`${chain} wallet created`));
21320
+ console.log("");
21321
+ console.log(label("Chain", `${wallet.chain} (${wallet.chain_type})`));
21322
+ if (wallet.name)
21323
+ console.log(label("Name", wallet.name));
21324
+ console.log(label("Address", wallet.wallet_address));
21325
+ console.log(label("Custodian", wallet.custodian ?? "-"));
21326
+ console.log("");
21327
+ console.log(dim(`Tip: paid commands (swap, markets search, image, amazon, services) will now use this wallet by default.`));
21328
+ } catch (e) {
21329
+ spinner?.stop();
21330
+ const msg = e instanceof Error ? e.message : "Failed to create wallet";
21331
+ if (/409|already/i.test(msg)) {
21332
+ console.error(error(`An active ${chain} wallet already exists. Run \`amiko wallets list\` to see it.`));
21333
+ } else {
21334
+ console.error(error(msg));
21335
+ }
21336
+ process.exit(1);
21337
+ }
21338
+ });
21339
+ }
21340
+ function registerWalletsListCommand(program2) {
21341
+ program2.command("list").alias("ls").description("List wallets for the authenticated twin").option("--json", "Output raw JSON").action(async (opts) => {
21342
+ let auth;
21343
+ try {
21344
+ auth = requireAuth();
21345
+ } catch (e) {
21346
+ console.error(error(e instanceof Error ? e.message : String(e)));
21347
+ process.exit(1);
21348
+ }
21349
+ const spinner = opts.json ? null : ora("Fetching wallets...").start();
21350
+ try {
21351
+ const data = await amikoWebFetch(auth, `/api/agents/${encodeURIComponent(auth.twinId)}/wallets`);
21352
+ spinner?.stop();
21353
+ if (opts.json) {
21354
+ console.log(JSON.stringify(data, null, 2));
21355
+ return;
21356
+ }
21357
+ const wallets = data.wallets || [];
21358
+ if (wallets.length === 0) {
21359
+ console.log(dim("No wallets found."));
21360
+ return;
21361
+ }
21362
+ console.log(heading("Wallets"));
21363
+ console.log("");
21364
+ for (const w of wallets) {
21365
+ console.log(label("Chain", `${w.chain} (${w.chain_type})`));
21366
+ if (w.name)
21367
+ console.log(label("Name", w.name));
21368
+ console.log(label("Address", w.wallet_address));
21369
+ console.log(label("Short", truncAddr(w.wallet_address)));
21370
+ const balances = Object.entries(w.balance || {}).filter(([, v]) => Number(v.amount) > 0);
21371
+ if (balances.length > 0) {
21372
+ console.log(dim(" Balances:"));
21373
+ const rows = balances.map(([key, v]) => [` ${key}`, formatBalance(v)]);
21374
+ console.log(table(rows));
21375
+ }
21376
+ console.log("");
21377
+ }
21378
+ } catch (e) {
21379
+ spinner?.stop();
21380
+ console.error(error(e instanceof Error ? e.message : "Failed to list wallets"));
21381
+ process.exit(1);
21382
+ }
21383
+ });
21384
+ }
21385
+
21148
21386
  // src/index.ts
21149
21387
  var require2 = createRequire2(import.meta.url);
21150
21388
  var { version: PKG_VERSION } = require2("../package.json");
@@ -21152,23 +21390,36 @@ var didOnboard = await runOnboarding(process.argv);
21152
21390
  if (didOnboard)
21153
21391
  process.exit(0);
21154
21392
  var program2 = new Command;
21155
- program2.name("amiko").description("Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call MPP services").version(PKG_VERSION);
21393
+ program2.name("amiko").description("Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace services").version(PKG_VERSION);
21394
+ var markets = program2.command("markets").description("MPP marketplace — discover and call paid AMIKO services");
21395
+ var wallets = program2.command("wallets").description("Manage twin wallets — list, swap, bridge");
21396
+ var twin = program2.command("twin").description("Update twin identity (name, description, visibility)");
21397
+ var docs = program2.command("docs").description("Manage twin RAG documents — list, upload, delete, presign");
21398
+ var voice = program2.command("voice").description("Manage twin voice — design, create, clone, reset");
21399
+ var avatar = program2.command("avatar").description("Manage twin avatar image");
21400
+ var friends = program2.command("friends").description("Manage friendships — list, add, accept, remove");
21401
+ var post = program2.command("post").description("Create posts and comments on the feed");
21402
+ var composio = program2.command("composio").description("Manage Composio app connections");
21156
21403
  registerCreditsCommand(program2);
21157
- registerSearchCommand(program2);
21158
- registerImageCommand(program2);
21159
- registerAmazonCommand(program2);
21160
- registerSwapCommand(program2);
21161
- registerBridgeCommand(program2);
21404
+ registerSearchCommand(markets);
21405
+ registerImageCommand(markets);
21406
+ registerAmazonCommand(markets);
21407
+ registerWalletsCreateCommand(wallets);
21408
+ registerWalletsListCommand(wallets);
21409
+ registerWalletsBalanceCommand(wallets);
21410
+ registerSwapCommand(wallets);
21411
+ registerBridgeCommand(wallets);
21162
21412
  registerConfigCommand(program2);
21163
- registerServicesCommand(program2);
21164
- registerDiscoveryCommand(program2);
21413
+ registerServicesCommand(markets);
21414
+ registerDiscoveryCommand(markets);
21165
21415
  registerUpdateCommand(program2);
21166
21416
  registerAccountsCommand(program2);
21167
- registerTwinCommand(program2);
21168
- registerDocsCommand(program2);
21169
- registerVoiceCommand(program2);
21170
- registerAvatarCommand(program2);
21171
- registerFriendsCommand(program2);
21417
+ registerTwinCommand(twin);
21418
+ registerDocsCommand(docs);
21419
+ registerVoiceCommand(voice);
21420
+ registerAvatarCommand(avatar);
21421
+ registerFriendsCommand(friends);
21172
21422
  registerFeedCommand(program2);
21173
- registerComposioCommand(program2);
21423
+ registerPostCommand(post);
21424
+ registerComposioCommand(composio);
21174
21425
  program2.parse();