@heyamiko/amiko-cli 0.3.4 → 0.3.6

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.
Files changed (3) hide show
  1. package/README.md +37 -3
  2. package/dist/index.js +361 -127
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,6 +30,9 @@ amiko credits balance
30
30
  # Top up credits from your agent's wallet
31
31
  amiko credits topup 10000 --token AMIKO --yes
32
32
 
33
+ # Swap tokens on Solana
34
+ amiko swap send 1 amiko usdc
35
+
33
36
  # Browse and call marketplace agents
34
37
  amiko browse
35
38
  amiko call titan-research "Summarize the latest AI news"
@@ -45,7 +48,7 @@ amiko connect amk_<key> # API key (shown once at creation)
45
48
  amiko connect # browser sign-in flow
46
49
  ```
47
50
 
48
- The connect code is the simplest — generate one from your Amiko settings page, paste it to the agent or terminal.
51
+ The connect code is the simplest — generate one from your Amiko settings page, paste it to the agent or terminal. Connect auto-detects your Solana wallet for swaps.
49
52
 
50
53
  ```bash
51
54
  amiko whoami # show current auth status
@@ -122,13 +125,41 @@ amiko creator publish <id> # publish a draft
122
125
  amiko creator payout # request payout
123
126
  ```
124
127
 
128
+ ## Swap (Solana)
129
+
130
+ Swap tokens on Solana via Jupiter aggregator. Wallet auto-detected from your connected account — no setup needed.
131
+
132
+ ```bash
133
+ amiko swap send 1 amiko usdc # swap 1 AMIKO → USDC (auto-detect wallet)
134
+ amiko swap send 5 usdt usdc --yes # skip confirmation
135
+ amiko swap send 0.1 sol usdc # SOL → USDC
136
+ amiko swap quote 1 amiko usdc # preview without executing
137
+ amiko swap tokens # list supported tokens
138
+ ```
139
+
140
+ Supports symbols (`sol`, `usdc`, `usdt`, `amiko`, `bonk`, `jup`, `pyusd`, `jitosol`, `ray`, `wbtc`) or raw mint addresses.
141
+
142
+ Swaps execute via Crossmint smart wallets — your agent wallet signs and submits on-chain automatically.
143
+
144
+ ## Bridge (Cross-Chain)
145
+
146
+ Bridge tokens across 26+ chains via Across Protocol:
147
+
148
+ ```bash
149
+ amiko bridge quote 10 # Solana → Tempo (default)
150
+ amiko bridge quote 5 --from base --to tempo
151
+ amiko bridge send 10 --from solana --to tempo --recipient 0x...
152
+ amiko bridge routes # list available routes
153
+ amiko bridge limits # check transfer limits
154
+ amiko bridge status <txHash> # track bridge status
155
+ ```
156
+
125
157
  ## Other Commands
126
158
 
127
159
  ```bash
128
160
  amiko reputation [address] # TARS score
129
161
  amiko discover # MPP service info
130
162
  amiko ping # health check
131
- amiko bridge quote 5 # cross-chain bridge quote
132
163
  amiko config show # view config
133
164
  amiko config set <key> <value> # set config value
134
165
  amiko update # update CLI to latest
@@ -143,9 +174,10 @@ npm install -g @heyamiko/amiko-cli
143
174
  amiko connect <CODE>
144
175
  amiko credits balance
145
176
  amiko credits topup 10000 --token AMIKO --yes
177
+ amiko swap send 1 amiko usdc # swap tokens on Solana
146
178
  ```
147
179
 
148
- The CLI auto-detects `.amiko.json` (synced by the platform) when running on an agent.
180
+ Connect auto-detects the agent's Solana wallet — no manual wallet setup needed. The CLI auto-detects `.amiko.json` (synced by the platform) when running on an agent.
149
181
 
150
182
  ## Configuration
151
183
 
@@ -166,6 +198,8 @@ src/
166
198
  ├── commands/
167
199
  │ ├── connect.ts # connect (code, API key, browser)
168
200
  │ ├── credits.ts # credits balance + topup
201
+ │ ├── swap.ts # Solana token swaps (Jupiter)
202
+ │ ├── bridge.ts # cross-chain bridging (Across)
169
203
  │ ├── browse.ts # marketplace browsing
170
204
  │ ├── call.ts # call agents
171
205
  │ ├── wallet.ts # MPP wallet management
package/dist/index.js CHANGED
@@ -1886,6 +1886,76 @@ var require_commander = __commonJS((exports) => {
1886
1886
  exports.InvalidOptionArgumentError = InvalidArgumentError;
1887
1887
  });
1888
1888
 
1889
+ // src/lib/config.ts
1890
+ var exports_config = {};
1891
+ __export(exports_config, {
1892
+ saveConfig: () => saveConfig,
1893
+ resetConfig: () => resetConfig,
1894
+ loadConfig: () => loadConfig,
1895
+ getPrivyToken: () => getPrivyToken,
1896
+ getConfigPath: () => getConfigPath
1897
+ });
1898
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
1899
+ import { homedir } from "node:os";
1900
+ import { join } from "node:path";
1901
+ function loadConfig() {
1902
+ if (!existsSync(CONFIG_FILE))
1903
+ return { ...DEFAULTS };
1904
+ try {
1905
+ const raw = readFileSync(CONFIG_FILE, "utf-8");
1906
+ const parsed = JSON.parse(raw);
1907
+ return normalizeConfig({ ...DEFAULTS, ...parsed });
1908
+ } catch {
1909
+ return { ...DEFAULTS };
1910
+ }
1911
+ }
1912
+ function saveConfig(config) {
1913
+ const current = loadConfig();
1914
+ const merged = normalizeConfig({ ...current, ...config });
1915
+ if (!existsSync(CONFIG_DIR))
1916
+ mkdirSync(CONFIG_DIR, { recursive: true });
1917
+ writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2) + `
1918
+ `);
1919
+ return merged;
1920
+ }
1921
+ function getConfigPath() {
1922
+ return CONFIG_FILE;
1923
+ }
1924
+ function resetConfig() {
1925
+ if (!existsSync(CONFIG_FILE)) {
1926
+ return false;
1927
+ }
1928
+ unlinkSync(CONFIG_FILE);
1929
+ return true;
1930
+ }
1931
+ function getPrivyToken(config) {
1932
+ return config.privyToken || config.creatorToken;
1933
+ }
1934
+ function normalizeConfig(config) {
1935
+ const normalized = { ...config };
1936
+ if (!normalized.privyToken && normalized.creatorToken) {
1937
+ normalized.privyToken = normalized.creatorToken;
1938
+ }
1939
+ if (LEGACY_SITE_BASES.has(normalized.siteBase)) {
1940
+ normalized.siteBase = DEFAULTS.siteBase;
1941
+ }
1942
+ return normalized;
1943
+ }
1944
+ var CONFIG_DIR, CONFIG_FILE, DEFAULTS, LEGACY_SITE_BASES;
1945
+ var init_config = __esm(() => {
1946
+ CONFIG_DIR = join(homedir(), ".amiko");
1947
+ CONFIG_FILE = join(CONFIG_DIR, "config.json");
1948
+ DEFAULTS = {
1949
+ apiBase: "https://mpp.amikomarkets.com",
1950
+ billingBase: "https://billing.heyamiko.com",
1951
+ siteBase: "https://platform.heyamiko.com"
1952
+ };
1953
+ LEGACY_SITE_BASES = new Set([
1954
+ "https://amiko-markets.vercel.app",
1955
+ "https://amiko-markets.vercel.app/"
1956
+ ]);
1957
+ });
1958
+
1889
1959
  // node_modules/cli-spinners/spinners.json
1890
1960
  var require_spinners = __commonJS((exports, module) => {
1891
1961
  module.exports = {
@@ -3533,6 +3603,80 @@ var require_emoji_regex = __commonJS((exports, module) => {
3533
3603
  };
3534
3604
  });
3535
3605
 
3606
+ // src/lib/agent-config.ts
3607
+ var exports_agent_config = {};
3608
+ __export(exports_agent_config, {
3609
+ resolveAuth: () => resolveAuth,
3610
+ loadAgentConfig: () => loadAgentConfig
3611
+ });
3612
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
3613
+ import { join as join3, resolve } from "node:path";
3614
+ function loadAgentConfig() {
3615
+ let dir = process.cwd();
3616
+ const root = resolve("/");
3617
+ while (dir !== root) {
3618
+ const filePath = join3(dir, ".amiko.json");
3619
+ if (existsSync3(filePath)) {
3620
+ try {
3621
+ const raw = readFileSync2(filePath, "utf-8");
3622
+ const parsed = JSON.parse(raw);
3623
+ const config = {};
3624
+ if (parsed.amikoTwinToken)
3625
+ config.twinToken = parsed.amikoTwinToken;
3626
+ if (parsed.amikoTwinId)
3627
+ config.twinId = parsed.amikoTwinId;
3628
+ if (parsed.amikoUserId)
3629
+ config.userId = parsed.amikoUserId;
3630
+ if (parsed.amikoPlatformUrl)
3631
+ config.platformUrl = parsed.amikoPlatformUrl;
3632
+ return Object.keys(config).length > 0 ? config : null;
3633
+ } catch {
3634
+ return null;
3635
+ }
3636
+ }
3637
+ dir = resolve(dir, "..");
3638
+ }
3639
+ return null;
3640
+ }
3641
+ function resolveAuth(cliConfig) {
3642
+ if (cliConfig.agentWallets?.length) {
3643
+ return {
3644
+ mode: "connected",
3645
+ platformUrl: cliConfig.platformUrl,
3646
+ billingBase: cliConfig.billingBase,
3647
+ twinId: cliConfig.twinId,
3648
+ twinToken: cliConfig.twinToken,
3649
+ platformKey: cliConfig.apiKey,
3650
+ privyToken: cliConfig.privyToken,
3651
+ connectedKeyId: cliConfig.connectedKeyId,
3652
+ userId: cliConfig.userId,
3653
+ agentWallets: cliConfig.agentWallets
3654
+ };
3655
+ }
3656
+ if (cliConfig.privyToken && cliConfig.connectedKeyId) {
3657
+ return {
3658
+ mode: "connected",
3659
+ billingBase: cliConfig.billingBase,
3660
+ platformKey: cliConfig.privyToken,
3661
+ privyToken: cliConfig.privyToken,
3662
+ connectedKeyId: cliConfig.connectedKeyId,
3663
+ userId: cliConfig.userId
3664
+ };
3665
+ }
3666
+ const agentConfig = loadAgentConfig();
3667
+ if (agentConfig?.twinToken) {
3668
+ return {
3669
+ mode: "agent",
3670
+ platformUrl: agentConfig.platformUrl,
3671
+ twinId: agentConfig.twinId,
3672
+ twinToken: agentConfig.twinToken,
3673
+ userId: agentConfig.userId
3674
+ };
3675
+ }
3676
+ return { mode: "none" };
3677
+ }
3678
+ var init_agent_config = () => {};
3679
+
3536
3680
  // node_modules/@solana/errors/dist/index.node.mjs
3537
3681
  function getHumanReadableErrorMessage(code, context = {}) {
3538
3682
  const messageFormatString = SolanaErrorMessages[code];
@@ -23300,67 +23444,9 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
23300
23444
  var source_default = chalk;
23301
23445
 
23302
23446
  // src/onboarding.ts
23447
+ init_config();
23303
23448
  import { createInterface } from "node:readline";
23304
23449
 
23305
- // src/lib/config.ts
23306
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
23307
- import { homedir } from "node:os";
23308
- import { join } from "node:path";
23309
- var CONFIG_DIR = join(homedir(), ".amiko");
23310
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
23311
- var DEFAULTS = {
23312
- apiBase: "https://mpp.amikomarkets.com",
23313
- billingBase: "https://billing.heyamiko.com",
23314
- siteBase: "https://platform.heyamiko.com"
23315
- };
23316
- var LEGACY_SITE_BASES = new Set([
23317
- "https://amiko-markets.vercel.app",
23318
- "https://amiko-markets.vercel.app/"
23319
- ]);
23320
- function loadConfig() {
23321
- if (!existsSync(CONFIG_FILE))
23322
- return { ...DEFAULTS };
23323
- try {
23324
- const raw = readFileSync(CONFIG_FILE, "utf-8");
23325
- const parsed = JSON.parse(raw);
23326
- return normalizeConfig({ ...DEFAULTS, ...parsed });
23327
- } catch {
23328
- return { ...DEFAULTS };
23329
- }
23330
- }
23331
- function saveConfig(config) {
23332
- const current = loadConfig();
23333
- const merged = normalizeConfig({ ...current, ...config });
23334
- if (!existsSync(CONFIG_DIR))
23335
- mkdirSync(CONFIG_DIR, { recursive: true });
23336
- writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2) + `
23337
- `);
23338
- return merged;
23339
- }
23340
- function getConfigPath() {
23341
- return CONFIG_FILE;
23342
- }
23343
- function resetConfig() {
23344
- if (!existsSync(CONFIG_FILE)) {
23345
- return false;
23346
- }
23347
- unlinkSync(CONFIG_FILE);
23348
- return true;
23349
- }
23350
- function getPrivyToken(config) {
23351
- return config.privyToken || config.creatorToken;
23352
- }
23353
- function normalizeConfig(config) {
23354
- const normalized = { ...config };
23355
- if (!normalized.privyToken && normalized.creatorToken) {
23356
- normalized.privyToken = normalized.creatorToken;
23357
- }
23358
- if (LEGACY_SITE_BASES.has(normalized.siteBase)) {
23359
- normalized.siteBase = DEFAULTS.siteBase;
23360
- }
23361
- return normalized;
23362
- }
23363
-
23364
23450
  // src/lib/format.ts
23365
23451
  function table(rows) {
23366
23452
  if (rows.length === 0)
@@ -24372,6 +24458,7 @@ function ora(options) {
24372
24458
  }
24373
24459
 
24374
24460
  // src/lib/api.ts
24461
+ init_config();
24375
24462
  function mppBase() {
24376
24463
  return trimBase(loadConfig().apiBase);
24377
24464
  }
@@ -24480,6 +24567,7 @@ function trimBase(base) {
24480
24567
  }
24481
24568
 
24482
24569
  // src/commands/browse.ts
24570
+ init_config();
24483
24571
  function siteUrl(agentSlug) {
24484
24572
  const config = loadConfig();
24485
24573
  return `${config.siteBase}/agents/${agentSlug}`;
@@ -24615,6 +24703,7 @@ function registerBrowseCommand(program2) {
24615
24703
  }
24616
24704
 
24617
24705
  // src/commands/call.ts
24706
+ init_config();
24618
24707
  import { createInterface as createInterface2 } from "node:readline";
24619
24708
 
24620
24709
  // src/lib/tempo.ts
@@ -24950,6 +25039,7 @@ function registerCallCommand(program2) {
24950
25039
  }
24951
25040
 
24952
25041
  // src/commands/wallet.ts
25042
+ init_config();
24953
25043
  function sanitizeTempoWhoamiOutput(output) {
24954
25044
  return output.replace(/^(\s*key:\s*)"[^"\n]*"$/m, '$1"[redacted]"');
24955
25045
  }
@@ -25148,6 +25238,7 @@ Tip: add --open to auto-launch in browser.`));
25148
25238
  }
25149
25239
 
25150
25240
  // src/commands/creator.ts
25241
+ init_config();
25151
25242
  function requireCreatorToken() {
25152
25243
  const config = loadConfig();
25153
25244
  const token = getPrivyToken(config);
@@ -25277,6 +25368,7 @@ function registerCreatorCommand(program2) {
25277
25368
  }
25278
25369
 
25279
25370
  // src/commands/reputation.ts
25371
+ init_config();
25280
25372
  function registerReputationCommand(program2) {
25281
25373
  program2.command("reputation").alias("rep").description("Look up TARS reputation for a wallet address").argument("[address]", "Wallet address (uses configured wallet if omitted)").action(async (address) => {
25282
25374
  const addr = address || loadConfig().wallet;
@@ -25333,6 +25425,7 @@ function scoreTier(score) {
25333
25425
  }
25334
25426
 
25335
25427
  // src/commands/config.ts
25428
+ init_config();
25336
25429
  var VALID_KEYS = [
25337
25430
  "apiBase",
25338
25431
  "billingBase",
@@ -25402,6 +25495,7 @@ function maskKey(key) {
25402
25495
  }
25403
25496
 
25404
25497
  // src/commands/discovery.ts
25498
+ init_config();
25405
25499
  function registerDiscoveryCommand(program2) {
25406
25500
  program2.command("discover").alias("status").description("Show MPP service info, endpoints, and pricing").action(async () => {
25407
25501
  const config = loadConfig();
@@ -25489,6 +25583,7 @@ function registerDiscoveryCommand(program2) {
25489
25583
  }
25490
25584
 
25491
25585
  // src/commands/marketplace.ts
25586
+ init_config();
25492
25587
  function timeAgo(timestamp) {
25493
25588
  const seconds = Math.floor((Date.now() - new Date(timestamp).getTime()) / 1000);
25494
25589
  if (seconds < 60)
@@ -25620,6 +25715,7 @@ function registerMarketplaceCommand(program2) {
25620
25715
  }
25621
25716
 
25622
25717
  // src/commands/login.ts
25718
+ init_config();
25623
25719
  import { createServer } from "node:http";
25624
25720
  import { randomBytes } from "node:crypto";
25625
25721
  import { execSync } from "node:child_process";
@@ -25886,6 +25982,7 @@ function startCallbackServer(expectedState, timeoutMs = 120000, acknowledgementT
25886
25982
  }
25887
25983
 
25888
25984
  // src/lib/across.ts
25985
+ init_config();
25889
25986
  function getBridgeBase() {
25890
25987
  const config = loadConfig();
25891
25988
  return `${config.apiBase}/v1/bridge`;
@@ -26035,6 +26132,7 @@ function getUsdcAddress(chainId) {
26035
26132
  }
26036
26133
 
26037
26134
  // src/commands/bridge.ts
26135
+ init_config();
26038
26136
  var DEFAULT_ORIGIN = CHAINS.solana;
26039
26137
  var DEFAULT_DESTINATION = CHAINS.tempo;
26040
26138
  function registerBridgeCommand(program2) {
@@ -26367,9 +26465,200 @@ function registerBridgeCommand(program2) {
26367
26465
  });
26368
26466
  }
26369
26467
 
26468
+ // src/commands/swap.ts
26469
+ var KNOWN_TOKENS = {
26470
+ sol: { mint: "So11111111111111111111111111111111111111112", decimals: 9, name: "SOL" },
26471
+ wsol: { mint: "So11111111111111111111111111111111111111112", decimals: 9, name: "Wrapped SOL" },
26472
+ usdc: { mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", decimals: 6, name: "USDC" },
26473
+ usdt: { mint: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", decimals: 6, name: "USDT" },
26474
+ pyusd: { mint: "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", decimals: 6, name: "PYUSD" },
26475
+ bonk: { mint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263", decimals: 5, name: "BONK" },
26476
+ jup: { mint: "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", decimals: 6, name: "JUP" },
26477
+ jitosol: { mint: "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", decimals: 9, name: "JitoSOL" },
26478
+ ray: { mint: "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R", decimals: 6, name: "RAY" },
26479
+ amiko: { mint: "AY1Ww6MxwC3cCiyrandHqLrp4FXvzgcwQTZpJ2FEpump", decimals: 6, name: "AMIKO" }
26480
+ };
26481
+ function resolveToken(input) {
26482
+ const lower = input.toLowerCase();
26483
+ const known = KNOWN_TOKENS[lower];
26484
+ if (known)
26485
+ return { mint: known.mint, decimals: known.decimals, symbol: known.name };
26486
+ return { mint: input, decimals: 6, symbol: truncAddr(input) };
26487
+ }
26488
+ function formatAmount(raw, decimals) {
26489
+ const n = typeof raw === "string" ? parseInt(raw, 10) : raw;
26490
+ return (n / 10 ** decimals).toFixed(decimals > 6 ? 4 : decimals);
26491
+ }
26492
+ function parseAmount(amount, decimals) {
26493
+ const num = parseFloat(amount);
26494
+ if (isNaN(num) || num <= 0)
26495
+ throw new Error(`Invalid amount: ${amount}`);
26496
+ return Math.round(num * 10 ** decimals).toString();
26497
+ }
26498
+ function registerSwapCommand(program2) {
26499
+ const cmd = program2.command("swap").description("Swap tokens on Solana via Jupiter");
26500
+ cmd.command("quote").description("Get a swap quote for Solana tokens").argument("<amount>", "Amount to swap (e.g., 5, 0.1)").argument("<from>", "Input token (sol, usdc, usdt, bonk, or mint address)").argument("<to>", "Output token (sol, usdc, usdt, bonk, or mint address)").option("--slippage <bps>", "Slippage tolerance in basis points", "50").action(async (amountStr, fromToken, toToken, opts) => {
26501
+ const spinner = ora("Fetching swap quote...").start();
26502
+ try {
26503
+ const input = resolveToken(fromToken);
26504
+ const output = resolveToken(toToken);
26505
+ const amount = parseAmount(amountStr, input.decimals);
26506
+ const params = new URLSearchParams({
26507
+ inputMint: input.mint,
26508
+ outputMint: output.mint,
26509
+ amount,
26510
+ slippageBps: opts.slippage
26511
+ });
26512
+ const quote = await get(`/v1/swap/quote?${params}`);
26513
+ spinner.stop();
26514
+ console.log(heading("Swap Quote"));
26515
+ console.log("");
26516
+ console.log(label("Input", `${formatAmount(quote.inAmount, input.decimals)} ${input.symbol}`));
26517
+ console.log(label("Output", `${formatAmount(quote.outAmount, output.decimals)} ${output.symbol}`));
26518
+ console.log(label("Min Output", `${formatAmount(quote.otherAmountThreshold, output.decimals)} ${output.symbol}`));
26519
+ if (quote.platformFee && parseInt(quote.platformFee.amount) > 0) {
26520
+ console.log(label("Platform Fee", `${formatAmount(quote.platformFee.amount, output.decimals)} ${output.symbol} (${quote.platformFee.feeBps} bps)`));
26521
+ }
26522
+ const impact = parseFloat(quote.priceImpactPct);
26523
+ if (impact > 0.1) {
26524
+ console.log(label("Price Impact", source_default.yellow(`${impact.toFixed(2)}%`)));
26525
+ } else {
26526
+ console.log(label("Price Impact", `${impact.toFixed(4)}%`));
26527
+ }
26528
+ console.log(label("Slippage", `${quote.slippageBps} bps`));
26529
+ if (quote.routePlan.length > 0) {
26530
+ const route = quote.routePlan.map((r) => r.swapInfo.label).join(" → ");
26531
+ console.log(label("Route", dim(route)));
26532
+ }
26533
+ console.log("");
26534
+ console.log(dim(`To execute: amiko swap send ${amountStr} ${fromToken} ${toToken}`));
26535
+ } catch (err) {
26536
+ spinner.stop();
26537
+ console.error(error(err instanceof Error ? err.message : "Failed to get quote"));
26538
+ process.exit(1);
26539
+ }
26540
+ });
26541
+ 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) => {
26542
+ try {
26543
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
26544
+ const { resolveAuth: resolveAuth2 } = await Promise.resolve().then(() => (init_agent_config(), exports_agent_config));
26545
+ const config = loadConfig2();
26546
+ const auth = resolveAuth2(config);
26547
+ const solanaAgentWallet = config.agentWallets?.find((w) => w.chain === "solana" && w.source === "agent")?.address;
26548
+ const wallet = opts.wallet || config.wallet || solanaAgentWallet;
26549
+ if (!wallet) {
26550
+ console.error(error("No Solana wallet found. Provide --wallet or run: amiko connect"));
26551
+ process.exit(1);
26552
+ }
26553
+ if (auth.mode === "none") {
26554
+ console.error(error("Not connected. Run: amiko connect"));
26555
+ process.exit(1);
26556
+ }
26557
+ const input = resolveToken(fromToken);
26558
+ const output = resolveToken(toToken);
26559
+ const amount = parseAmount(amountStr, input.decimals);
26560
+ const quoteSpinner = ora("Fetching swap quote...").start();
26561
+ const quoteParams = new URLSearchParams({
26562
+ inputMint: input.mint,
26563
+ outputMint: output.mint,
26564
+ amount,
26565
+ slippageBps: opts.slippage
26566
+ });
26567
+ const quote = await get(`/v1/swap/quote?${quoteParams}`);
26568
+ quoteSpinner.stop();
26569
+ console.log(heading("Swap"));
26570
+ console.log("");
26571
+ console.log(label("Send", `${formatAmount(quote.inAmount, input.decimals)} ${input.symbol}`));
26572
+ console.log(label("Receive", `~${formatAmount(quote.outAmount, output.decimals)} ${output.symbol}`));
26573
+ if (quote.platformFee && parseInt(quote.platformFee.amount) > 0) {
26574
+ console.log(label("Fee", `${formatAmount(quote.platformFee.amount, output.decimals)} ${output.symbol} (${quote.platformFee.feeBps} bps)`));
26575
+ }
26576
+ const route = quote.routePlan.map((r) => r.swapInfo.label).join(" → ");
26577
+ if (route)
26578
+ console.log(label("Route", dim(route)));
26579
+ console.log(label("Wallet", truncAddr(wallet)));
26580
+ console.log("");
26581
+ if (!opts.yes) {
26582
+ const readline = await import("readline");
26583
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
26584
+ const answer = await new Promise((resolve2) => rl.question("Confirm swap? (y/N) ", resolve2));
26585
+ rl.close();
26586
+ if (answer.toLowerCase() !== "y") {
26587
+ console.log(dim("Cancelled."));
26588
+ process.exit(0);
26589
+ }
26590
+ }
26591
+ const swapSpinner = ora("Executing swap via Crossmint...").start();
26592
+ const billingToken = auth.platformKey || auth.privyToken;
26593
+ const billingBase2 = auth.billingBase;
26594
+ if (!billingBase2 || !billingToken) {
26595
+ swapSpinner.stop();
26596
+ console.error(error("No billing credentials. Run: amiko connect"));
26597
+ process.exit(1);
26598
+ }
26599
+ const swapRes = await fetch(`${billingBase2}/api/crossmint/wallets/${encodeURIComponent(wallet)}/swap`, {
26600
+ method: "POST",
26601
+ headers: {
26602
+ "Content-Type": "application/json",
26603
+ Authorization: `Bearer ${billingToken}`
26604
+ },
26605
+ body: JSON.stringify({
26606
+ inputMint: input.mint,
26607
+ outputMint: output.mint,
26608
+ amount,
26609
+ slippageBps: parseInt(opts.slippage)
26610
+ })
26611
+ });
26612
+ if (!swapRes.ok) {
26613
+ swapSpinner.stop();
26614
+ const err = await swapRes.json().catch(() => ({ error: "Swap failed" }));
26615
+ console.error(error(err.error || `Swap failed: ${swapRes.status}`));
26616
+ if (err.details)
26617
+ console.error(dim(typeof err.details === "string" ? err.details : JSON.stringify(err.details)));
26618
+ process.exit(1);
26619
+ }
26620
+ const swapData = await swapRes.json();
26621
+ swapSpinner.stop();
26622
+ console.log(success("Swap executed"));
26623
+ console.log("");
26624
+ if (swapData.txHash) {
26625
+ console.log(label("Tx Hash", dim(swapData.txHash)));
26626
+ console.log(label("Explorer", dim(swapData.explorerLink || `https://solscan.io/tx/${swapData.txHash}`)));
26627
+ }
26628
+ if (swapData.quote) {
26629
+ console.log(label("Input", `${formatAmount(swapData.quote.inAmount, input.decimals)} ${input.symbol}`));
26630
+ console.log(label("Output", `${formatAmount(swapData.quote.outAmount, output.decimals)} ${output.symbol}`));
26631
+ }
26632
+ } catch (err) {
26633
+ console.error(error(err instanceof Error ? err.message : "Swap failed"));
26634
+ process.exit(1);
26635
+ }
26636
+ });
26637
+ cmd.command("tokens").description("List supported Solana tokens for swapping").action(async () => {
26638
+ const spinner = ora("Fetching tokens...").start();
26639
+ try {
26640
+ const data = await get("/v1/swap/tokens");
26641
+ spinner.stop();
26642
+ console.log(heading("Solana Tokens"));
26643
+ console.log(dim(`${data.tokens.length} tokens (source: ${data.provider})
26644
+ `));
26645
+ for (const t of data.tokens) {
26646
+ console.log(` ${source_default.cyan(t.symbol.padEnd(10))} ${t.name.padEnd(25)} ${dim(truncAddr(t.address))}`);
26647
+ }
26648
+ console.log("");
26649
+ console.log(dim("Use symbol or mint address: amiko swap quote 5 usdt usdc"));
26650
+ } catch (err) {
26651
+ spinner.stop();
26652
+ console.error(error(err instanceof Error ? err.message : "Failed to fetch tokens"));
26653
+ process.exit(1);
26654
+ }
26655
+ });
26656
+ }
26657
+
26370
26658
  // src/commands/connect.ts
26371
26659
  import { randomBytes as randomBytes2 } from "node:crypto";
26372
26660
  import { execFileSync as execFileSync2 } from "node:child_process";
26661
+ init_config();
26373
26662
  function openBrowser2(url) {
26374
26663
  try {
26375
26664
  const platform = process.platform;
@@ -26426,8 +26715,15 @@ function saveCredentials(data) {
26426
26715
  update.connectedKeyPreview = data.connectedKeyPreview;
26427
26716
  if (data.connectedKeyName)
26428
26717
  update.connectedKeyName = data.connectedKeyName;
26429
- if (data.wallets)
26718
+ if (data.wallets) {
26430
26719
  update.agentWallets = data.wallets;
26720
+ const currentConfig = loadConfig();
26721
+ if (!currentConfig.wallet) {
26722
+ const solWallet = data.wallets.find((w) => w.chain === "solana" && w.source === "agent");
26723
+ if (solWallet)
26724
+ update.wallet = solWallet.address;
26725
+ }
26726
+ }
26431
26727
  saveConfig(update);
26432
26728
  }
26433
26729
  function registerConnectCommand(program2) {
@@ -26554,75 +26850,12 @@ Tip: you can also generate a connect link from your Amiko settings`));
26554
26850
  });
26555
26851
  }
26556
26852
 
26557
- // src/lib/agent-config.ts
26558
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
26559
- import { join as join3, resolve } from "node:path";
26560
- function loadAgentConfig() {
26561
- let dir = process.cwd();
26562
- const root = resolve("/");
26563
- while (dir !== root) {
26564
- const filePath = join3(dir, ".amiko.json");
26565
- if (existsSync3(filePath)) {
26566
- try {
26567
- const raw = readFileSync2(filePath, "utf-8");
26568
- const parsed = JSON.parse(raw);
26569
- const config = {};
26570
- if (parsed.amikoTwinToken)
26571
- config.twinToken = parsed.amikoTwinToken;
26572
- if (parsed.amikoTwinId)
26573
- config.twinId = parsed.amikoTwinId;
26574
- if (parsed.amikoUserId)
26575
- config.userId = parsed.amikoUserId;
26576
- if (parsed.amikoPlatformUrl)
26577
- config.platformUrl = parsed.amikoPlatformUrl;
26578
- return Object.keys(config).length > 0 ? config : null;
26579
- } catch {
26580
- return null;
26581
- }
26582
- }
26583
- dir = resolve(dir, "..");
26584
- }
26585
- return null;
26586
- }
26587
- function resolveAuth(cliConfig) {
26588
- if (cliConfig.agentWallets?.length) {
26589
- return {
26590
- mode: "connected",
26591
- platformUrl: cliConfig.platformUrl,
26592
- billingBase: cliConfig.billingBase,
26593
- twinId: cliConfig.twinId,
26594
- twinToken: cliConfig.twinToken,
26595
- platformKey: cliConfig.apiKey,
26596
- privyToken: cliConfig.privyToken,
26597
- connectedKeyId: cliConfig.connectedKeyId,
26598
- userId: cliConfig.userId,
26599
- agentWallets: cliConfig.agentWallets
26600
- };
26601
- }
26602
- if (cliConfig.privyToken && cliConfig.connectedKeyId) {
26603
- return {
26604
- mode: "connected",
26605
- billingBase: cliConfig.billingBase,
26606
- platformKey: cliConfig.privyToken,
26607
- privyToken: cliConfig.privyToken,
26608
- connectedKeyId: cliConfig.connectedKeyId,
26609
- userId: cliConfig.userId
26610
- };
26611
- }
26612
- const agentConfig = loadAgentConfig();
26613
- if (agentConfig?.twinToken) {
26614
- return {
26615
- mode: "agent",
26616
- platformUrl: agentConfig.platformUrl,
26617
- twinId: agentConfig.twinId,
26618
- twinToken: agentConfig.twinToken,
26619
- userId: agentConfig.userId
26620
- };
26621
- }
26622
- return { mode: "none" };
26623
- }
26853
+ // src/commands/credits.ts
26854
+ init_config();
26855
+ init_agent_config();
26624
26856
 
26625
26857
  // src/lib/mpp-topup.ts
26858
+ init_config();
26626
26859
  async function mppCreditsTopup(params) {
26627
26860
  const config2 = loadConfig();
26628
26861
  const mppBase2 = config2.apiBase.replace(/\/$/, "");
@@ -27011,6 +27244,7 @@ registerDiscoveryCommand(program2);
27011
27244
  registerMarketplaceCommand(program2);
27012
27245
  registerLoginCommand(program2);
27013
27246
  registerBridgeCommand(program2);
27247
+ registerSwapCommand(program2);
27014
27248
  registerConnectCommand(program2);
27015
27249
  registerCreditsCommand(program2);
27016
27250
  registerUpdateCommand(program2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Amiko CLI — manage agents, wallets, credits, and marketplace on the MPP payment rail",
5
5
  "type": "module",
6
6
  "bin": {