fuego-cli 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,11 +69,13 @@ fuego dashboard
69
69
  |---------|-------------|
70
70
  | `fuego create` | Create a new Solana wallet |
71
71
  | `fuego address` | Show your wallet address |
72
- | `fuego balance` | Check SOL, USDC, USDT balances |
72
+ | `fuego balance` | Check all token balances (SOL + SPL tokens) |
73
73
  | `fuego rpc` | Show or configure your Solana RPC endpoint |
74
74
  | `fuego contacts` | Manage your contacts |
75
75
  | `fuego fund` | Show funding options: MoonPay link + QR code |
76
76
  | `fuego send` | Send SOL, USDC, or USDT to an address or contact |
77
+ | `fuego jupiter quote` | Get swap quotes from Jupiter |
78
+ | `fuego jupiter swap` | Execute token swaps via Jupiter |
77
79
  | `fuego purch` | Purchase products via x402 (Amazon, Shopify, etc.) |
78
80
 
79
81
  ### Project Management
@@ -115,7 +117,7 @@ fuego create --name prod-wallet
115
117
 
116
118
  ### `fuego balance`
117
119
 
118
- Check balances via the Fuego server.
120
+ Check all token balances via the Fuego server. Shows SOL plus all SPL tokens in your wallet.
119
121
 
120
122
  ```bash
121
123
  fuego balance
@@ -129,9 +131,9 @@ fuego bal
129
131
 
130
132
  Address: DmFy...eUZF
131
133
 
132
- - SOL: 1.234567890
133
- - USDC: $0.00
134
- - USDT: $0.00
134
+ - SOL: 0.105113976
135
+ - USDC: 28.85
136
+ - BONK: 712,687.68
135
137
  ```
136
138
 
137
139
  ---
@@ -306,6 +308,55 @@ fuego send melanie 5 --token USDT --yes
306
308
 
307
309
  ---
308
310
 
311
+ ### `fuego jupiter quote <amount>`
312
+
313
+ Get a swap quote from Jupiter for any supported token pair.
314
+
315
+ ```bash
316
+ # Quote SOL to USDC
317
+ fuego jupiter quote 0.5 --input SOL --output USDC
318
+
319
+ # Quote USDC to BONK
320
+ fuego jupiter quote 100 --input USDC --output BONK
321
+
322
+ # Quote using full mint addresses
323
+ fuego jupiter quote 1.0 --input SOL --output DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263
324
+ ```
325
+
326
+ **Options:**
327
+ - `-i, --input <token>` — **Required.** Input token symbol (SOL, USDC, USDT, BONK, JUP) or full mint address
328
+ - `-o, --output <token>` — **Required.** Output token symbol or full mint address
329
+
330
+ **Output:** Shows expected output amount, price impact, and routing information.
331
+
332
+ ---
333
+
334
+ ### `fuego jupiter swap <amount>`
335
+
336
+ Execute a token swap via Jupiter.
337
+
338
+ ```bash
339
+ # Swap SOL to USDC (preview only)
340
+ fuego jupiter swap 0.5 --input SOL --output USDC
341
+
342
+ # Swap SOL to BONK (with confirmation)
343
+ fuego jupiter swap 0.05 --input SOL --output BONK --yes
344
+
345
+ # Swap USDC to random token by mint address
346
+ fuego jupiter swap 10 --input USDC --output <mint-address> --yes
347
+ ```
348
+
349
+ **Options:**
350
+ - `-i, --input <token>` — **Required.** Input token symbol or full mint address
351
+ - `-o, --output <token>` — **Required.** Output token symbol or full mint address
352
+ - `-y, --yes` — Skip confirmation and execute immediately
353
+
354
+ **Supported Tokens:** SOL, USDC, USDT, BONK, JUP (or any token by full mint address)
355
+
356
+ **Note:** For unknown tokens, provide the full 44-character mint address. The dashboard caches the top 100 tokens for symbol lookups.
357
+
358
+ ---
359
+
309
360
  ### `fuego purch <product-url>`
310
361
 
311
362
  Purchase products via x402/Purch.xyz (Amazon, Shopify, etc.) using USDC on Solana.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fuego-cli",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Command-line interface for Fuego - the sovereign Solana wallet for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -5,17 +5,24 @@ import ora from 'ora';
5
5
 
6
6
  const FUEGO_SERVER_URL = 'http://127.0.0.1:8080';
7
7
 
8
- interface BalanceResponse {
9
- success: boolean;
10
- data?: {
11
- sol: number;
12
- };
8
+ interface Token {
9
+ symbol: string;
10
+ ui_amount: number;
11
+ decimals: number;
12
+ mint: string;
13
+ token_account: string;
14
+ amount: string;
13
15
  }
14
16
 
15
- interface TokenBalanceResponse {
17
+ interface TokensResponse {
16
18
  success: boolean;
17
19
  data?: {
18
- ui_amount: string;
20
+ wallet: string;
21
+ network: string;
22
+ sol_balance: number;
23
+ sol_lamports: number;
24
+ token_count: number;
25
+ tokens: Token[];
19
26
  };
20
27
  }
21
28
 
@@ -35,48 +42,58 @@ export async function balanceCommand(): Promise<void> {
35
42
  const publicKey = config.publicKey;
36
43
  const network = 'mainnet-beta';
37
44
 
38
- // Query SOL balance
39
- const solResponse = await fetch(`${FUEGO_SERVER_URL}/sol-balance`, {
45
+ // Query all tokens via /tokens endpoint
46
+ const tokensResponse = await fetch(`${FUEGO_SERVER_URL}/tokens`, {
40
47
  method: 'POST',
41
48
  headers: { 'Content-Type': 'application/json' },
42
49
  body: JSON.stringify({ network, address: publicKey })
43
50
  });
44
- const solData = await solResponse.json() as BalanceResponse;
45
- const solBalance = solData.success ? solData.data!.sol : 0;
51
+ const tokensData = await tokensResponse.json() as TokensResponse;
46
52
 
47
- // Query USDC balance
48
- const usdcResponse = await fetch(`${FUEGO_SERVER_URL}/usdc-balance`, {
49
- method: 'POST',
50
- headers: { 'Content-Type': 'application/json' },
51
- body: JSON.stringify({ network, address: publicKey })
52
- });
53
- const usdcData = await usdcResponse.json() as TokenBalanceResponse;
54
- const usdcBalance = usdcData.success ? parseFloat(usdcData.data!.ui_amount) : 0;
53
+ spinner.stop();
55
54
 
56
- // Query USDT balance
57
- const usdtResponse = await fetch(`${FUEGO_SERVER_URL}/usdt-balance`, {
58
- method: 'POST',
59
- headers: { 'Content-Type': 'application/json' },
60
- body: JSON.stringify({ network, address: publicKey })
61
- });
62
- const usdtData = await usdtResponse.json() as TokenBalanceResponse;
63
- const usdtBalance = usdtData.success ? parseFloat(usdtData.data!.ui_amount) : 0;
55
+ if (!tokensData.success || !tokensData.data) {
56
+ console.log(chalk.red('❌ Failed to fetch balances from server'));
57
+ process.exit(1);
58
+ }
64
59
 
65
- spinner.stop();
60
+ const { sol_balance, tokens } = tokensData.data;
66
61
 
67
- showInfo('💰 Your Balances', [
62
+ // Build balance lines
63
+ const balanceLines: string[] = [
68
64
  `Address: ${formatPublicKey(publicKey)}`,
69
65
  '',
70
- `${chalk.yellow('- SOL:')} ${chalk.white(solBalance.toFixed(9))}`,
71
- `${chalk.green('- USDC:')} ${chalk.white('$' + usdcBalance.toFixed(2))}`,
72
- `${chalk.cyan('- USDT:')} ${chalk.white('$' + usdtBalance.toFixed(2))}`,
73
- ]);
66
+ `${chalk.yellow('- SOL:')} ${chalk.white(sol_balance.toFixed(9))}`,
67
+ ];
68
+
69
+ // Add each token balance
70
+ for (const token of tokens) {
71
+ const formattedAmount = token.ui_amount.toLocaleString('en-US', {
72
+ minimumFractionDigits: 2,
73
+ maximumFractionDigits: token.decimals > 6 ? 6 : token.decimals
74
+ });
75
+
76
+ // Color-code common tokens
77
+ let tokenColor = chalk.white;
78
+ if (token.symbol === 'USDC') tokenColor = chalk.green;
79
+ else if (token.symbol === 'USDT') tokenColor = chalk.cyan;
80
+ else if (token.symbol === 'BONK') tokenColor = chalk.magenta;
81
+
82
+ balanceLines.push(`${tokenColor(`- ${token.symbol}:`)} ${chalk.white(formattedAmount)}`);
83
+ }
84
+
85
+ // If no tokens found, note that
86
+ if (tokens.length === 0) {
87
+ balanceLines.push(chalk.gray('(No SPL tokens found)'));
88
+ }
89
+
90
+ showInfo('💰 Your Balances', balanceLines);
74
91
 
75
92
  flameDivider();
76
93
  } catch (error) {
77
94
  spinner.stop();
78
95
  console.log(chalk.red(`\n❌ Failed to fetch balances: ${error instanceof Error ? error.message : 'Unknown error'}`));
79
- console.log(chalk.gray('\nMake sure the Fuego server is running: cd fuego/server && cargo run'));
96
+ console.log(chalk.gray('\nMake sure the Fuego server is running: fuego serve'));
80
97
  process.exit(1);
81
98
  }
82
99
  }
@@ -53,7 +53,8 @@ export async function createCommand(options: CreateOptions): Promise<void> {
53
53
  fs.writeJsonSync(configPath, {
54
54
  ...existingConfig,
55
55
  network: existingConfig.network || 'mainnet',
56
- rpcUrl: existingConfig.rpcUrl || 'https://api.mainnet-beta.solana.com'
56
+ rpcUrl: existingConfig.rpcUrl || 'https://api.mainnet-beta.solana.com',
57
+ jupiterKey: existingConfig.jupiterKey || ''
57
58
  }, { spaces: 2 });
58
59
 
59
60
  // Create address book directory and empty file
@@ -0,0 +1,122 @@
1
+ import chalk from 'chalk';
2
+ import { spawn } from 'child_process';
3
+ import path from 'path';
4
+ import { loadWalletConfig, findFuegoPath } from '../lib/config.js';
5
+ import { showSuccess, showInfo, showError, flameDivider } from '../lib/ascii.js';
6
+
7
+ interface QuoteOptions {
8
+ input?: string;
9
+ output?: string;
10
+ }
11
+
12
+ const TOKEN_MINTS: { [key: string]: string } = {
13
+ 'SOL': 'So11111111111111111111111111111111111111112',
14
+ 'USDC': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
15
+ 'USDT': 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
16
+ 'BONK': 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
17
+ 'JUP': 'JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN',
18
+ };
19
+
20
+ function resolveToken(token: string): string {
21
+ const upper = token.toUpperCase();
22
+ if (TOKEN_MINTS[upper]) {
23
+ return TOKEN_MINTS[upper];
24
+ }
25
+ // If it's a full mint address, use it directly
26
+ if (token.length === 44) {
27
+ return token;
28
+ }
29
+ return '';
30
+ }
31
+
32
+ export async function jupiterQuoteCommand(amount: string, options: QuoteOptions): Promise<void> {
33
+ console.log();
34
+
35
+ // Validate amount
36
+ if (!amount) {
37
+ console.log(chalk.red('❌ Usage: fuego jupiter quote <amount> --input SOL --output USDC'));
38
+ console.log(chalk.gray('\nExamples:'));
39
+ console.log(chalk.gray(' fuego jupiter quote 0.5 --input SOL --output USDC'));
40
+ console.log(chalk.gray(' fuego jupiter quote 10 --input USDC --output BONK'));
41
+ console.log(chalk.gray('\nSupported tokens: SOL, USDC, USDT, BONK, JUP'));
42
+ process.exit(1);
43
+ }
44
+
45
+ const amountNum = parseFloat(amount);
46
+ if (isNaN(amountNum) || amountNum <= 0) {
47
+ console.log(chalk.red('❌ Invalid amount. Please provide a positive number.'));
48
+ process.exit(1);
49
+ }
50
+
51
+ // Validate tokens
52
+ if (!options.input || !options.output) {
53
+ console.log(chalk.red('❌ Both --input and --output tokens are required.'));
54
+ console.log(chalk.gray('Example: --input SOL --output USDC'));
55
+ process.exit(1);
56
+ }
57
+
58
+ const inputMint = resolveToken(options.input);
59
+ const outputMint = resolveToken(options.output);
60
+
61
+ if (!inputMint) {
62
+ console.log(chalk.red(`❌ Unknown input token: ${options.input}`));
63
+ console.log(chalk.gray('Supported: SOL, USDC, USDT, BONK, JUP'));
64
+ process.exit(1);
65
+ }
66
+
67
+ if (!outputMint) {
68
+ console.log(chalk.red(`❌ Unknown output token: ${options.output}`));
69
+ console.log(chalk.gray('Supported: SOL, USDC, USDT, BONK, JUP'));
70
+ process.exit(1);
71
+ }
72
+
73
+ // Load wallet
74
+ const walletConfig = loadWalletConfig();
75
+ if (!walletConfig) {
76
+ console.log(chalk.red('❌ No wallet found. Run "fuego create" first.'));
77
+ process.exit(1);
78
+ }
79
+
80
+ showInfo('🪐 Jupiter Quote', [
81
+ `From: ${chalk.cyan(options.input.toUpperCase())}`,
82
+ `To: ${chalk.cyan(options.output.toUpperCase())}`,
83
+ `Amount: ${chalk.yellow(amount)}`,
84
+ `Network: ${chalk.gray('mainnet-beta')}`
85
+ ]);
86
+
87
+ console.log();
88
+ console.log(chalk.blue('⏳ Fetching quote from Jupiter...'));
89
+
90
+ // Find fuego path and call script
91
+ const fuegoPath = findFuegoPath();
92
+ if (!fuegoPath) {
93
+ showError('Fuego installation not found. Run "fuego install" first.');
94
+ process.exit(1);
95
+ }
96
+
97
+ const scriptPath = path.join(fuegoPath, 'scripts', 'jupiter', 'jupiter_price.mjs');
98
+
99
+ const nodeProcess = spawn('node', [
100
+ scriptPath,
101
+ '--input', options.input,
102
+ '--output', options.output,
103
+ '--amount', amount
104
+ ]);
105
+
106
+ nodeProcess.stdout.on('data', (data) => {
107
+ process.stdout.write(data);
108
+ });
109
+
110
+ nodeProcess.stderr.on('data', (data) => {
111
+ process.stderr.write(data);
112
+ });
113
+
114
+ nodeProcess.on('close', (code) => {
115
+ if (code === 0) {
116
+ flameDivider();
117
+ } else {
118
+ showError('Failed to fetch quote');
119
+ process.exit(1);
120
+ }
121
+ });
122
+ }
@@ -0,0 +1,146 @@
1
+ import chalk from 'chalk';
2
+ import { spawn } from 'child_process';
3
+ import path from 'path';
4
+ import { loadWalletConfig, findFuegoPath } from '../lib/config.js';
5
+ import { showSuccess, showInfo, showError, flameDivider } from '../lib/ascii.js';
6
+
7
+ interface SwapOptions {
8
+ input?: string;
9
+ output?: string;
10
+ yes?: boolean;
11
+ }
12
+
13
+ const TOKEN_MINTS: { [key: string]: string } = {
14
+ 'SOL': 'So11111111111111111111111111111111111111112',
15
+ 'USDC': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
16
+ 'USDT': 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
17
+ 'BONK': 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
18
+ 'JUP': 'JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN',
19
+ };
20
+
21
+ function resolveToken(token: string): string {
22
+ const upper = token.toUpperCase();
23
+ if (TOKEN_MINTS[upper]) {
24
+ return TOKEN_MINTS[upper];
25
+ }
26
+ if (token.length === 44) {
27
+ return token;
28
+ }
29
+ return '';
30
+ }
31
+
32
+ export async function jupiterSwapCommand(amount: string, options: SwapOptions): Promise<void> {
33
+ console.log();
34
+
35
+ // Validate amount
36
+ if (!amount) {
37
+ console.log(chalk.red('❌ Usage: fuego jupiter swap <amount> --input SOL --output USDC [--yes]'));
38
+ console.log(chalk.gray('\nExamples:'));
39
+ console.log(chalk.gray(' fuego jupiter swap 0.5 --input SOL --output USDC'));
40
+ console.log(chalk.gray(' fuego jupiter swap 10 --input USDC --output BONK --yes'));
41
+ console.log(chalk.gray('\nSupported tokens: SOL, USDC, USDT, BONK, JUP'));
42
+ process.exit(1);
43
+ }
44
+
45
+ const amountNum = parseFloat(amount);
46
+ if (isNaN(amountNum) || amountNum <= 0) {
47
+ console.log(chalk.red('❌ Invalid amount. Please provide a positive number.'));
48
+ process.exit(1);
49
+ }
50
+
51
+ // Validate tokens
52
+ if (!options.input || !options.output) {
53
+ console.log(chalk.red('❌ Both --input and --output tokens are required.'));
54
+ console.log(chalk.gray('Example: --input SOL --output USDC'));
55
+ process.exit(1);
56
+ }
57
+
58
+ const inputMint = resolveToken(options.input);
59
+ const outputMint = resolveToken(options.output);
60
+
61
+ if (!inputMint) {
62
+ console.log(chalk.red(`❌ Unknown input token: ${options.input}`));
63
+ console.log(chalk.gray('Supported: SOL, USDC, USDT, BONK, JUP'));
64
+ process.exit(1);
65
+ }
66
+
67
+ if (!outputMint) {
68
+ console.log(chalk.red(`❌ Unknown output token: ${options.output}`));
69
+ console.log(chalk.gray('Supported: SOL, USDC, USDT, BONK, JUP'));
70
+ process.exit(1);
71
+ }
72
+
73
+ // Load wallet
74
+ const walletConfig = loadWalletConfig();
75
+ if (!walletConfig) {
76
+ console.log(chalk.red('❌ No wallet found. Run "fuego create" first.'));
77
+ process.exit(1);
78
+ }
79
+
80
+ // Show transaction preview
81
+ showInfo('🪐 Jupiter Swap Preview', [
82
+ `From: ${chalk.yellow(amount)} ${chalk.cyan(options.input.toUpperCase())}`,
83
+ `To: ${chalk.cyan(options.output.toUpperCase())}`,
84
+ `Wallet: ${chalk.gray(walletConfig.publicKey.substring(0, 16))}...`,
85
+ `Network: ${chalk.gray('mainnet-beta')}`
86
+ ]);
87
+
88
+ // Confirm unless --yes
89
+ if (!options.yes) {
90
+ console.log();
91
+ console.log(chalk.yellow('⚠️ Add --yes to confirm and execute swap'));
92
+ console.log(chalk.gray('Or run first: fuego jupiter quote ' + amount + ' --input ' + options.input + ' --output ' + options.output));
93
+ flameDivider();
94
+ return;
95
+ }
96
+
97
+ console.log();
98
+ console.log(chalk.blue('⏳ Executing Jupiter swap...'));
99
+ console.log(chalk.gray('This may take a moment...'));
100
+
101
+ // Find fuego path and call script
102
+ const fuegoPath = findFuegoPath();
103
+ if (!fuegoPath) {
104
+ showError('Fuego installation not found. Run "fuego install" first.');
105
+ process.exit(1);
106
+ }
107
+
108
+ const scriptPath = path.join(fuegoPath, 'scripts', 'jupiter', 'jupiter_swap_regular.mjs');
109
+
110
+ const nodeProcess = spawn('node', [
111
+ scriptPath,
112
+ '--input', options.input,
113
+ '--output', options.output,
114
+ '--amount', amount
115
+ ]);
116
+
117
+ let output = '';
118
+
119
+ nodeProcess.stdout.on('data', (data) => {
120
+ output += data.toString();
121
+ process.stdout.write(data);
122
+ });
123
+
124
+ nodeProcess.stderr.on('data', (data) => {
125
+ process.stderr.write(data);
126
+ });
127
+
128
+ nodeProcess.on('close', (code) => {
129
+ if (code === 0) {
130
+ // Extract signature from output if present
131
+ const sigMatch = output.match(/Signature: ([A-Za-z0-9]+)/);
132
+ if (sigMatch) {
133
+ showSuccess(
134
+ '✅ Swap Complete!',
135
+ `${chalk.yellow(amount)} ${chalk.cyan(options.input.toUpperCase())} → ${chalk.cyan(options.output.toUpperCase())}`
136
+ );
137
+ } else {
138
+ showSuccess('✅ Swap Executed!', '');
139
+ }
140
+ flameDivider();
141
+ } else {
142
+ showError('Swap failed. Check output above for details.');
143
+ process.exit(1);
144
+ }
145
+ });
146
+ }
package/src/index.ts CHANGED
@@ -14,6 +14,8 @@ import { contactsAddCommand, contactsListCommand, contactsShowCommand, contactsR
14
14
  import { sendCommand } from './commands/send.js';
15
15
  import { fundCommand } from './commands/fund.js';
16
16
  import { purchCommand } from './commands/purch.js';
17
+ import { jupiterQuoteCommand } from './commands/jupiter-quote.js';
18
+ import { jupiterSwapCommand } from './commands/jupiter-swap.js';
17
19
  import { showBanner } from './lib/ascii.js';
18
20
  import { getFuegoCliVersion } from './lib/config.js';
19
21
 
@@ -136,6 +138,25 @@ async function main() {
136
138
  .option('--country <code>', 'Country code (default: US)', 'US')
137
139
  .action(purchCommand);
138
140
 
141
+ const jupiter = program
142
+ .command('jupiter')
143
+ .description('🪐 Jupiter swap commands');
144
+
145
+ jupiter
146
+ .command('quote <amount>')
147
+ .description('Get a swap quote from Jupiter')
148
+ .requiredOption('-i, --input <token>', 'Input token (SOL, USDC, USDT, BONK, JUP)')
149
+ .requiredOption('-o, --output <token>', 'Output token (SOL, USDC, USDT, BONK, JUP)')
150
+ .action(jupiterQuoteCommand);
151
+
152
+ jupiter
153
+ .command('swap <amount>')
154
+ .description('Execute a swap via Jupiter')
155
+ .requiredOption('-i, --input <token>', 'Input token (SOL, USDC, USDT, BONK, JUP)')
156
+ .requiredOption('-o, --output <token>', 'Output token (SOL, USDC, USDT, BONK, JUP)')
157
+ .option('-y, --yes', 'Skip confirmation and execute immediately')
158
+ .action(jupiterSwapCommand);
159
+
139
160
  await program.parseAsync(process.argv);
140
161
  }
141
162
 
Binary file