httpcat-cli 0.0.26 → 0.0.27-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.github/workflows/README.md +19 -2
  2. package/.github/workflows/ci.yml +31 -20
  3. package/.github/workflows/homebrew-tap.yml +1 -1
  4. package/.github/workflows/rc-publish.yml +169 -0
  5. package/.github/workflows/release.yml +223 -71
  6. package/README.md +94 -76
  7. package/bun.lock +2933 -0
  8. package/dist/commands/account.d.ts.map +1 -1
  9. package/dist/commands/account.js +14 -7
  10. package/dist/commands/account.js.map +1 -1
  11. package/dist/commands/balances.d.ts.map +1 -0
  12. package/dist/commands/balances.js +171 -0
  13. package/dist/commands/balances.js.map +1 -0
  14. package/dist/commands/buy.d.ts.map +1 -1
  15. package/dist/commands/buy.js +739 -32
  16. package/dist/commands/buy.js.map +1 -1
  17. package/dist/commands/chat.d.ts.map +1 -1
  18. package/dist/commands/chat.js +467 -906
  19. package/dist/commands/chat.js.map +1 -1
  20. package/dist/commands/claim.d.ts.map +1 -0
  21. package/dist/commands/claim.js +65 -0
  22. package/dist/commands/claim.js.map +1 -0
  23. package/dist/commands/create.d.ts.map +1 -1
  24. package/dist/commands/create.js +0 -1
  25. package/dist/commands/create.js.map +1 -1
  26. package/dist/commands/info.d.ts.map +1 -1
  27. package/dist/commands/info.js +128 -26
  28. package/dist/commands/info.js.map +1 -1
  29. package/dist/commands/list.d.ts.map +1 -1
  30. package/dist/commands/list.js +30 -23
  31. package/dist/commands/list.js.map +1 -1
  32. package/dist/commands/positions.d.ts.map +1 -1
  33. package/dist/commands/positions.js +178 -105
  34. package/dist/commands/positions.js.map +1 -1
  35. package/dist/commands/sell.d.ts.map +1 -1
  36. package/dist/commands/sell.js +713 -24
  37. package/dist/commands/sell.js.map +1 -1
  38. package/dist/index.js +315 -99
  39. package/dist/index.js.map +1 -1
  40. package/dist/interactive/shell.d.ts.map +1 -1
  41. package/dist/interactive/shell.js +328 -179
  42. package/dist/interactive/shell.js.map +1 -1
  43. package/dist/mcp/tools.d.ts.map +1 -1
  44. package/dist/mcp/tools.js +8 -8
  45. package/dist/mcp/tools.js.map +1 -1
  46. package/dist/utils/constants.d.ts.map +1 -0
  47. package/dist/utils/constants.js +66 -0
  48. package/dist/utils/constants.js.map +1 -0
  49. package/dist/utils/formatting.d.ts.map +1 -1
  50. package/dist/utils/formatting.js +3 -5
  51. package/dist/utils/formatting.js.map +1 -1
  52. package/dist/utils/token-resolver.d.ts.map +1 -1
  53. package/dist/utils/token-resolver.js +71 -40
  54. package/dist/utils/token-resolver.js.map +1 -1
  55. package/dist/utils/validation.d.ts.map +1 -1
  56. package/dist/utils/validation.js +4 -3
  57. package/dist/utils/validation.js.map +1 -1
  58. package/jest.config.js +1 -1
  59. package/package.json +19 -13
  60. package/.claude/settings.local.json +0 -41
  61. package/dist/commands/balance.d.ts.map +0 -1
  62. package/dist/commands/balance.js +0 -112
  63. package/dist/commands/balance.js.map +0 -1
  64. package/homebrew-httpcat/Formula/httpcat.rb +0 -18
  65. package/homebrew-httpcat/README.md +0 -31
  66. package/homebrew-httpcat/homebrew-httpcat/Formula/httpcat.rb +0 -18
  67. package/homebrew-httpcat/homebrew-httpcat/README.md +0 -31
@@ -1,99 +1,193 @@
1
- import { createInterface } from "readline";
2
1
  import chalk from "chalk";
3
- import inquirer from "inquirer";
2
+ // @ts-ignore - neo-blessed doesn't have types, but @types/blessed provides compatible types
3
+ import blessed from "neo-blessed";
4
4
  import { config } from "../config.js";
5
- import { printWelcome, printCat } from "./art.js";
6
- import { handleError } from "../utils/errors.js";
7
- import { withLoading } from "../utils/loading.js";
5
+ import { printCat } from "./art.js";
8
6
  import { validateAmount } from "../utils/validation.js";
9
7
  // Import commands
10
- import { createToken, displayCreateResult } from "../commands/create.js";
11
- import { buyToken, displayBuyResult, TEST_AMOUNTS, PROD_AMOUNTS } from "../commands/buy.js";
12
- import { sellToken, displaySellResult, parseTokenAmount, } from "../commands/sell.js";
13
- import { getTokenInfo, displayTokenInfo } from "../commands/info.js";
14
- import { listTokens, displayTokenList } from "../commands/list.js";
15
- import { getPositions, displayPositions } from "../commands/positions.js";
8
+ import { createToken } from "../commands/create.js";
9
+ import { buyToken, TEST_AMOUNTS, PROD_AMOUNTS } from "../commands/buy.js";
10
+ import { sellToken, parseTokenAmount } from "../commands/sell.js";
11
+ import { getTokenInfo } from "../commands/info.js";
12
+ import { listTokens } from "../commands/list.js";
13
+ import { getPositions } from "../commands/positions.js";
16
14
  import { privateKeyToAccount } from "viem/accounts";
17
- import { checkHealth, displayHealthStatus } from "../commands/health.js";
15
+ import { checkHealth } from "../commands/health.js";
18
16
  import { startChatStream } from "../commands/chat.js";
19
- import { checkBalance, displayBalance } from "../commands/balance.js";
17
+ import { checkBalance } from "../commands/balances.js";
20
18
  export async function startInteractiveShell(client) {
21
- // Display welcome message
22
- console.clear();
19
+ // Create blessed screen
20
+ const screen = blessed.screen({
21
+ smartCSR: true,
22
+ title: "httpcat Interactive Shell",
23
+ fullUnicode: true,
24
+ });
23
25
  const network = client.getNetwork();
24
- printWelcome(undefined, network);
25
- const rl = createInterface({
26
- input: process.stdin,
27
- output: process.stdout,
28
- prompt: chalk.cyan("httpcat> "),
26
+ // Create header box
27
+ const headerBox = blessed.box({
28
+ top: 0,
29
+ left: 0,
30
+ width: "100%",
31
+ height: 8,
32
+ content: "",
33
+ tags: false,
34
+ style: {
35
+ fg: "white",
36
+ bg: "black",
37
+ },
38
+ padding: {
39
+ left: 1,
40
+ right: 1,
41
+ },
42
+ });
43
+ // Build welcome content
44
+ const welcomeLines = [];
45
+ welcomeLines.push(" /\\_/\\");
46
+ welcomeLines.push(" ( ^.^ )");
47
+ welcomeLines.push(" > ^ <");
48
+ welcomeLines.push("");
49
+ welcomeLines.push("Welcome to httpcat!");
50
+ welcomeLines.push(`Connected to: ${network}`);
51
+ welcomeLines.push('Type "help" for available commands or "exit" to quit');
52
+ headerBox.setContent(welcomeLines.join("\n"));
53
+ // Create output log box (scrollable)
54
+ const outputBox = blessed.log({
55
+ top: 8,
56
+ left: 0,
57
+ width: "100%",
58
+ height: "100%-11",
59
+ tags: true,
60
+ scrollable: true,
61
+ alwaysScroll: true,
62
+ scrollbar: {
63
+ ch: " ",
64
+ inverse: true,
65
+ },
66
+ style: {
67
+ fg: "white",
68
+ bg: "black",
69
+ },
70
+ padding: {
71
+ left: 1,
72
+ right: 1,
73
+ },
74
+ });
75
+ // Create input box at bottom
76
+ const inputBox = blessed.textbox({
77
+ bottom: 0,
78
+ left: 0,
79
+ width: "100%",
80
+ height: 3,
81
+ inputOnFocus: true,
82
+ keys: true,
83
+ style: {
84
+ fg: "cyan",
85
+ bg: "black",
86
+ focus: {
87
+ fg: "white",
88
+ bg: "blue",
89
+ },
90
+ },
91
+ padding: {
92
+ left: 1,
93
+ right: 1,
94
+ },
29
95
  });
30
- console.log();
31
- console.log(chalk.dim('Type "help" for available commands or "exit" to quit'));
32
- console.log();
33
- rl.prompt();
34
- rl.on("line", async (line) => {
35
- const trimmed = line.trim();
96
+ // Create a prompt label
97
+ const promptLabel = blessed.text({
98
+ bottom: 1,
99
+ left: 1,
100
+ content: "httpcat> ",
101
+ style: {
102
+ fg: "cyan",
103
+ bg: "black",
104
+ },
105
+ });
106
+ // Append all widgets
107
+ screen.append(headerBox);
108
+ screen.append(outputBox);
109
+ screen.append(promptLabel);
110
+ screen.append(inputBox);
111
+ // Helper to log output
112
+ const log = (text) => {
113
+ outputBox.log(text);
114
+ outputBox.setScrollPerc(100);
115
+ screen.render();
116
+ };
117
+ // Helper to log multiple lines
118
+ const logLines = (lines) => {
119
+ lines.forEach((line) => outputBox.log(line));
120
+ outputBox.setScrollPerc(100);
121
+ screen.render();
122
+ };
123
+ // Handle input submission
124
+ inputBox.on("submit", async (value) => {
125
+ const trimmed = value.trim();
126
+ inputBox.clearValue();
36
127
  if (!trimmed) {
37
- rl.prompt();
128
+ inputBox.focus();
129
+ screen.render();
38
130
  return;
39
131
  }
132
+ log(`httpcat> ${trimmed}`);
40
133
  const [command, ...args] = trimmed.split(/\s+/);
41
- // Pause readline to prevent input during command execution
42
- rl.pause();
43
134
  try {
44
- await handleCommand(client, command.toLowerCase(), args, rl);
135
+ await handleCommand(client, command.toLowerCase(), args, log, logLines, screen, inputBox);
45
136
  }
46
137
  catch (error) {
47
- handleError(error, config.get("preferences")?.verboseLogging);
48
- }
49
- finally {
50
- // Always resume readline after command completes
51
- rl.resume();
138
+ log(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
52
139
  }
53
- console.log();
54
- rl.prompt();
140
+ log(""); // Empty line after command output
141
+ inputBox.focus();
142
+ screen.render();
55
143
  });
56
- rl.on("close", () => {
57
- console.log();
144
+ // Handle Ctrl+C
145
+ screen.key(["C-c"], () => {
146
+ screen.destroy();
58
147
  printCat("sleeping");
59
148
  console.log(chalk.cyan("Goodbye! 👋"));
60
149
  process.exit(0);
61
150
  });
151
+ // Handle escape to clear input
152
+ inputBox.key(["escape"], () => {
153
+ inputBox.clearValue();
154
+ screen.render();
155
+ });
156
+ // Focus input and render
157
+ inputBox.focus();
158
+ screen.render();
62
159
  }
63
- async function handleCommand(client, command, args, rl) {
160
+ async function handleCommand(client, command, args, log, logLines, screen, inputBox) {
64
161
  switch (command) {
65
162
  case "help":
66
- displayHelp();
163
+ displayHelp(log, logLines);
67
164
  break;
68
165
  case "create": {
69
166
  if (args.length < 2) {
70
- console.log(chalk.red("Usage: create <name> <symbol> [--photo URL] [--banner URL] [--website URL]"));
167
+ log(chalk.red("Usage: create <name> <symbol> [--photo URL] [--banner URL] [--website URL]"));
71
168
  return;
72
169
  }
73
170
  const [name, symbol] = args;
74
171
  const photoUrl = extractFlag(args, "--photo");
75
172
  const bannerUrl = extractFlag(args, "--banner");
76
173
  const websiteUrl = extractFlag(args, "--website");
77
- const result = await withLoading(() => createToken(client, {
174
+ log(chalk.dim("Creating token..."));
175
+ screen.render();
176
+ const result = await createToken(client, {
78
177
  name,
79
178
  symbol,
80
179
  photoUrl,
81
180
  bannerUrl,
82
181
  websiteUrl,
83
- }), {
84
- message: "Creating token...",
85
- json: false,
86
- quiet: false,
87
- spinner: "cat",
88
182
  });
89
- displayCreateResult(result);
183
+ displayCreateResultToLog(result, log, logLines);
90
184
  break;
91
185
  }
92
186
  case "buy": {
93
187
  if (args.length < 2) {
94
- console.log(chalk.red("Usage: buy <tokenId|name|symbol> <amount>"));
95
- console.log(chalk.dim(" amount: 0.05, 0.10, or 0.20 (test mode) or 50, 100, 200 (production)"));
96
- console.log(chalk.dim(' Examples: buy abc123-... 0.20, buy "My Token" 0.10, buy MTK 0.05'));
188
+ log(chalk.red("Usage: buy <address|name|symbol> <amount>"));
189
+ log(chalk.dim(" amount: 0.05, 0.10, or 0.20 (test mode) or 50, 100, 200 (production)"));
190
+ log(chalk.dim(' Examples: buy 0x1234... 0.20, buy "My Token" 0.10, buy MTK 0.05'));
97
191
  return;
98
192
  }
99
193
  const [identifier, amountInput] = args;
@@ -105,183 +199,158 @@ async function handleCommand(client, command, args, rl) {
105
199
  amount = validateAmount(amountInput, validAmounts, isTestMode);
106
200
  }
107
201
  catch (error) {
108
- // Validation failed - offer interactive selection
109
- console.log(chalk.yellow(`⚠️ Invalid amount: ${amountInput}`));
110
- console.log();
111
- const answer = await inquirer.prompt([
112
- {
113
- type: 'list',
114
- name: 'amount',
115
- message: 'Please select a valid amount:',
116
- choices: validAmounts.map(amt => ({
117
- name: `$${amt} USDC`,
118
- value: amt
119
- }))
120
- }
121
- ]);
122
- amount = answer.amount;
202
+ // Validation failed - show error and return
203
+ log(chalk.yellow(`Invalid amount: ${amountInput}`));
204
+ log(chalk.dim(`Valid amounts: ${validAmounts.join(", ")}`));
205
+ return;
123
206
  }
124
- const result = await withLoading(() => buyToken(client, identifier, amount, isTestMode, false), {
125
- message: "Buying tokens...",
126
- json: false,
127
- quiet: false,
128
- spinner: "cat",
129
- });
130
- displayBuyResult(result);
207
+ log(chalk.dim("Buying tokens..."));
208
+ screen.render();
209
+ const result = await buyToken(client, identifier, amount, isTestMode, false);
210
+ displayBuyResultToLog(result, log, logLines);
131
211
  break;
132
212
  }
133
213
  case "sell": {
134
214
  if (args.length < 2) {
135
- console.log(chalk.red("Usage: sell <tokenId|name|symbol> <amount|percentage|all>"));
136
- console.log(chalk.dim(' Examples: sell abc123 1000, sell "My Token" 50%, sell MTK all'));
215
+ log(chalk.red("Usage: sell <address|name|symbol> <amount|percentage|all>"));
216
+ log(chalk.dim(' Examples: sell 0x1234... 1000, sell "My Token" 50%, sell MTK all'));
137
217
  return;
138
218
  }
139
219
  const [identifier, amountInput] = args;
140
- // Get current holdings first
141
- const info = await withLoading(() => getTokenInfo(client, identifier), {
142
- message: "Checking token info...",
143
- json: false,
144
- quiet: false,
145
- spinner: "cat",
146
- });
220
+ log(chalk.dim("Checking token info..."));
221
+ screen.render();
222
+ const info = await getTokenInfo(client, identifier);
147
223
  if (!info.userPosition || info.userPosition.tokensOwned === "0") {
148
- console.log(chalk.yellow("You do not own any of this token."));
224
+ log(chalk.yellow("You do not own any of this token."));
149
225
  return;
150
226
  }
151
227
  const tokenAmount = parseTokenAmount(amountInput, info.userPosition.tokensOwned);
152
- const result = await withLoading(() => sellToken(client, identifier, tokenAmount, false), {
153
- message: "Selling tokens...",
154
- json: false,
155
- quiet: false,
156
- spinner: "cat",
157
- });
158
- displaySellResult(result);
228
+ log(chalk.dim("Selling tokens..."));
229
+ screen.render();
230
+ const result = await sellToken(client, identifier, tokenAmount, false);
231
+ displaySellResultToLog(result, log, logLines);
159
232
  break;
160
233
  }
161
234
  case "info": {
162
235
  if (args.length < 1) {
163
- console.log(chalk.red("Usage: info <tokenId|name|symbol>"));
164
- console.log(chalk.dim(' Examples: info abc123-..., info "My Token", info MTK'));
236
+ log(chalk.red("Usage: info <address|name|symbol>"));
237
+ log(chalk.dim(' Examples: info 0x1234..., info "My Token", info MTK'));
165
238
  return;
166
239
  }
167
240
  const [identifier] = args;
168
- const info = await withLoading(() => getTokenInfo(client, identifier), {
169
- message: "Fetching token info...",
170
- json: false,
171
- quiet: false,
172
- spinner: "cat",
173
- });
174
- displayTokenInfo(info);
241
+ // Get user address from private key for balance checking
242
+ const privateKey = config.getPrivateKey();
243
+ const account = privateKeyToAccount(privateKey);
244
+ const userAddress = account.address;
245
+ log(chalk.dim("Fetching token info..."));
246
+ screen.render();
247
+ const info = await getTokenInfo(client, identifier, userAddress);
248
+ displayTokenInfoToLog(info, log, logLines);
175
249
  break;
176
250
  }
177
251
  case "list": {
178
252
  const page = parseInt(extractFlag(args, "--page") || "1");
179
253
  const limit = parseInt(extractFlag(args, "--limit") || "20");
180
254
  const sortBy = (extractFlag(args, "--sort") || "mcap");
181
- const result = await withLoading(() => listTokens(client, page, limit, sortBy), {
182
- message: "Fetching token list...",
183
- json: false,
184
- quiet: false,
185
- spinner: "cat",
186
- });
187
- displayTokenList(result);
255
+ log(chalk.dim("Fetching token list..."));
256
+ screen.render();
257
+ const result = await listTokens(client, page, limit, sortBy);
258
+ displayTokenListToLog(result, log, logLines);
188
259
  break;
189
260
  }
190
261
  case "positions": {
191
- // Get user address from private key
192
262
  const privateKey = config.getPrivateKey();
193
263
  const account = privateKeyToAccount(privateKey);
194
264
  const userAddress = account.address;
195
- const result = await withLoading(() => getPositions(client, userAddress), {
196
- message: "Fetching positions...",
197
- json: false,
198
- quiet: false,
199
- spinner: "cat",
200
- });
201
- displayPositions(result);
265
+ log(chalk.dim("Fetching positions..."));
266
+ screen.render();
267
+ const result = await getPositions(client, userAddress);
268
+ displayPositionsToLog(result, log, logLines);
202
269
  break;
203
270
  }
204
271
  case "health": {
205
- const health = await withLoading(() => checkHealth(client), {
206
- message: "Checking health...",
207
- json: false,
208
- quiet: false,
209
- spinner: "cat",
210
- });
211
- displayHealthStatus(health);
272
+ log(chalk.dim("Checking health..."));
273
+ screen.render();
274
+ const health = await checkHealth(client);
275
+ displayHealthStatusToLog(health, log, logLines);
212
276
  break;
213
277
  }
214
- case "balance": {
278
+ case "balances": {
215
279
  try {
216
280
  const privateKey = config.getPrivateKey();
217
- const balance = await withLoading(() => checkBalance(privateKey), {
218
- message: "Checking balance...",
219
- json: false,
220
- quiet: false,
221
- spinner: "cat",
222
- });
223
- displayBalance(balance);
281
+ log(chalk.dim("Checking balance..."));
282
+ screen.render();
283
+ const balance = await checkBalance(privateKey);
284
+ displayBalanceToLog(balance, log, logLines);
224
285
  }
225
286
  catch (error) {
226
- // If private key is not configured, checkBalance will prompt for it
227
- const balance = await withLoading(() => checkBalance(), {
228
- message: "Checking balance...",
229
- json: false,
230
- quiet: false,
231
- spinner: "cat",
232
- });
233
- displayBalance(balance);
287
+ log(chalk.dim("Checking balance..."));
288
+ screen.render();
289
+ const balance = await checkBalance();
290
+ displayBalanceToLog(balance, log, logLines);
234
291
  }
235
292
  break;
236
293
  }
237
294
  case "config": {
238
295
  if (args[0] === "--show") {
239
- displayConfig();
296
+ displayConfigToLog(log, logLines);
240
297
  }
241
298
  else if (args[0] === "--set" && args[1]) {
242
299
  const [key, value] = args[1].split("=");
243
300
  config.set(key, value);
244
- console.log(chalk.green(`✅ ${key} set to ${value}`));
301
+ log(chalk.green(`${key} set to ${value}`));
245
302
  }
246
303
  else if (args[0] === "--reset") {
247
- await config.runSetupWizard();
304
+ log(chalk.yellow("Config reset requires exiting the shell. Use 'httpcat config' from command line."));
248
305
  }
249
306
  else {
250
- console.log(chalk.red("Usage: config [--show|--set key=value|--reset]"));
307
+ log(chalk.red("Usage: config [--show|--set key=value|--reset]"));
251
308
  }
252
309
  break;
253
310
  }
254
311
  case "network": {
255
- console.log(chalk.yellow("Current network:"), chalk.cyan(config.get("network")));
256
- console.log(chalk.dim("Note: Only base-sepolia (testnet) is currently supported"));
312
+ log(chalk.yellow("Current network: ") + chalk.cyan(config.get("network")));
313
+ log(chalk.dim("Note: Only base-sepolia (testnet) is currently supported"));
257
314
  break;
258
315
  }
259
316
  case "chat": {
317
+ // For chat, we need to exit the blessed shell and start chat stream
318
+ log(chalk.yellow("Starting chat mode..."));
319
+ screen.destroy();
260
320
  const tokenIdentifier = args.length > 0 ? args[0] : undefined;
261
321
  await startChatStream(client, false, tokenIdentifier);
322
+ process.exit(0);
262
323
  break;
263
324
  }
264
325
  case "exit":
265
326
  case "quit":
327
+ screen.destroy();
328
+ printCat("sleeping");
329
+ console.log(chalk.cyan("Goodbye! 👋"));
266
330
  process.exit(0);
267
331
  break;
332
+ case "clear":
333
+ // Clear the output box
334
+ screen.children[1].setContent("");
335
+ screen.render();
336
+ break;
268
337
  default:
269
- console.log(chalk.red(`Unknown command: ${command}`));
270
- console.log(chalk.dim('Type "help" for available commands'));
338
+ log(chalk.red(`Unknown command: ${command}`));
339
+ log(chalk.dim('Type "help" for available commands'));
271
340
  }
272
341
  }
273
- function displayHelp() {
274
- console.log();
275
- console.log(chalk.cyan.bold("Available Commands:"));
276
- console.log();
342
+ function displayHelp(log, logLines) {
343
+ log("");
344
+ log(chalk.cyan.bold("Available Commands:"));
345
+ log("");
277
346
  const commands = [
278
347
  ["create <name> <symbol>", "Create a new token"],
279
- ["buy <id|name|symbol> <amount>", "Buy tokens ($0.05, $0.10, or $0.20)"],
280
- ["sell <id|name|symbol> <amount|all>", "Sell tokens"],
281
- ["info <id|name|symbol>", "Get token information"],
348
+ ["buy <address|name|symbol> <amount>", "Buy tokens ($0.05, $0.10, or $0.20)"],
349
+ ["sell <address|name|symbol> <amount|all>", "Sell tokens"],
350
+ ["info <address|name|symbol>", "Get token information"],
282
351
  ["list [--sort mcap|created]", "List all tokens"],
283
352
  ["positions", "Get all your positions with comprehensive info"],
284
- ["balance", "Check wallet balance (ETH and USDC)"],
353
+ ["balances", "Check wallet balances (ETH and USDC)"],
285
354
  [
286
355
  "chat [token]",
287
356
  "Start streaming chat (optional: token symbol/name/address)",
@@ -289,38 +358,118 @@ function displayHelp() {
289
358
  ["health", "Check agent health"],
290
359
  ["config [--show|--set|--reset]", "Manage configuration"],
291
360
  ["network", "Show current network"],
361
+ ["clear", "Clear the output"],
292
362
  ["help", "Show this help message"],
293
363
  ["exit", "Exit the shell"],
294
364
  ];
295
365
  for (const [cmd, desc] of commands) {
296
- console.log(` ${chalk.cyan(cmd.padEnd(30))} ${chalk.dim(desc)}`);
366
+ log(` ${chalk.cyan(cmd.padEnd(30))} ${chalk.dim(desc)}`);
297
367
  }
298
- console.log();
299
- console.log(chalk.dim("Examples:"));
300
- console.log(chalk.dim(' create "My Token" "MTK"'));
301
- console.log(chalk.dim(" buy abc123-... 5"));
302
- console.log(chalk.dim(' buy "My Token" 2'));
303
- console.log(chalk.dim(" buy MTK 1"));
304
- console.log(chalk.dim(" sell abc123-... 50%"));
305
- console.log(chalk.dim(' info "My Token"'));
306
- console.log(chalk.dim(" list --sort mcap --limit 10"));
307
- console.log(chalk.dim(" positions"));
308
- console.log();
368
+ log("");
369
+ log(chalk.dim("Examples:"));
370
+ log(chalk.dim(' create "My Token" "MTK"'));
371
+ log(chalk.dim(" buy 0x1234... 5"));
372
+ log(chalk.dim(' buy "My Token" 2'));
373
+ log(chalk.dim(" buy MTK 1"));
374
+ log(chalk.dim(" sell 0x1234... 50%"));
375
+ log(chalk.dim(' info "My Token"'));
376
+ log(chalk.dim(" list --sort mcap --limit 10"));
377
+ log(chalk.dim(" positions"));
309
378
  }
310
- function displayConfig() {
379
+ function displayConfigToLog(log, logLines) {
311
380
  const cfg = config.getAll();
312
- console.log();
313
- console.log(chalk.cyan.bold("Current Configuration:"));
314
- console.log();
315
- console.log(chalk.dim("Network:"), chalk.cyan(cfg.network));
316
- console.log(chalk.dim("Agent URL:"), chalk.cyan(cfg.agentUrl));
317
- console.log(chalk.dim("Facilitator:"), chalk.cyan(cfg.facilitatorUrl));
318
- console.log(chalk.dim("Max Payment:"), chalk.cyan("$" + cfg.defaultMaxPayment));
319
- console.log(chalk.dim("ASCII Art:"), chalk.cyan(cfg.preferences.enableAsciiArt ? "enabled" : "disabled"));
320
- console.log(chalk.dim("Private Key:"), chalk.dim(cfg.privateKey ? "configured" : "not configured"));
321
- console.log();
322
- console.log(chalk.dim(`Config file: ${config.getConfigPath()}`));
323
- console.log();
381
+ log("");
382
+ log(chalk.cyan.bold("Current Configuration:"));
383
+ log("");
384
+ log(chalk.dim("Network: ") + chalk.cyan(cfg.network));
385
+ log(chalk.dim("Agent URL: ") + chalk.cyan(cfg.agentUrl));
386
+ log(chalk.dim("Facilitator: ") + chalk.cyan(cfg.facilitatorUrl));
387
+ log(chalk.dim("Max Payment: ") + chalk.cyan("$" + cfg.defaultMaxPayment));
388
+ log(chalk.dim("ASCII Art: ") +
389
+ chalk.cyan(cfg.preferences.enableAsciiArt ? "enabled" : "disabled"));
390
+ log(chalk.dim("Private Key: ") +
391
+ chalk.dim(cfg.privateKey ? "configured" : "not configured"));
392
+ log("");
393
+ log(chalk.dim(`Config file: ${config.getConfigPath()}`));
394
+ }
395
+ // Simplified display functions that log to blessed output
396
+ function displayCreateResultToLog(result, log, logLines) {
397
+ log(chalk.green("Token created successfully!"));
398
+ log(chalk.dim("Name: ") + result.name);
399
+ log(chalk.dim("Symbol: ") + result.symbol);
400
+ if (result.tokenAddress) {
401
+ log(chalk.dim("Address: ") + chalk.cyan(result.tokenAddress));
402
+ }
403
+ }
404
+ function displayBuyResultToLog(result, log, logLines) {
405
+ log(chalk.green("Purchase successful!"));
406
+ log(chalk.dim("Tokens received: ") + chalk.cyan(result.tokensReceived));
407
+ log(chalk.dim("Amount spent: ") + chalk.yellow(result.amountSpent));
408
+ log(chalk.dim("New price: ") + chalk.cyan(result.newPrice));
409
+ log(chalk.dim("Graduation: ") +
410
+ chalk.magenta(result.graduationProgress.toFixed(2) + "%"));
411
+ }
412
+ function displaySellResultToLog(result, log, logLines) {
413
+ log(chalk.green("Sale successful!"));
414
+ log(chalk.dim("Tokens sold: ") + chalk.cyan(result.tokensSold));
415
+ log(chalk.dim("USDC received: ") + chalk.yellow(result.usdcReceived));
416
+ log(chalk.dim("New price: ") + chalk.cyan(result.newPrice));
417
+ log(chalk.dim("Graduation: ") +
418
+ chalk.magenta(result.graduationProgress.toFixed(2) + "%"));
419
+ }
420
+ function displayTokenInfoToLog(info, log, logLines) {
421
+ log(chalk.cyan.bold(info.name) + ` (${info.symbol})`);
422
+ if (info.tokenAddress) {
423
+ log(chalk.dim("Address: ") + info.tokenAddress);
424
+ }
425
+ log(chalk.dim("Price: ") + chalk.cyan(info.currentPrice));
426
+ log(chalk.dim("Market Cap: ") + chalk.yellow(info.marketCap));
427
+ log(chalk.dim("Supply: ") + info.totalSupply);
428
+ if (info.graduationProgress !== undefined) {
429
+ log(chalk.dim("Graduation: ") +
430
+ chalk.magenta(info.graduationProgress.toFixed(2) + "%"));
431
+ }
432
+ if (info.userPosition && info.userPosition.tokensOwned !== "0") {
433
+ log(chalk.dim("Your position: ") +
434
+ chalk.green(info.userPosition.tokensOwned + " tokens"));
435
+ }
436
+ }
437
+ function displayTokenListToLog(result, log, logLines) {
438
+ log(chalk.cyan.bold(`Tokens (${result.tokens.length} total):`));
439
+ log("");
440
+ for (const token of result.tokens.slice(0, 10)) {
441
+ log(` ${chalk.cyan(token.symbol.padEnd(8))} ${token.name.padEnd(20)} ${chalk.yellow(token.marketCap || "N/A")}`);
442
+ }
443
+ if (result.tokens.length > 10) {
444
+ log(chalk.dim(` ... and ${result.tokens.length - 10} more`));
445
+ }
446
+ }
447
+ function displayPositionsToLog(result, log, logLines) {
448
+ if (!result.positions || result.positions.length === 0) {
449
+ log(chalk.yellow("No positions found."));
450
+ return;
451
+ }
452
+ log(chalk.cyan.bold(`Your Positions (${result.positions.length}):`));
453
+ log("");
454
+ for (const pos of result.positions) {
455
+ log(` ${chalk.cyan(pos.symbol || "???")} - ${chalk.green(pos.tokensOwned + " tokens")} @ ${chalk.yellow(pos.currentValue || "N/A")}`);
456
+ }
457
+ }
458
+ function displayHealthStatusToLog(health, log, logLines) {
459
+ const statusColor = health.status === "healthy" ? chalk.green : chalk.red;
460
+ log(statusColor(`Agent Status: ${health.status}`));
461
+ if (health.version) {
462
+ log(chalk.dim("Version: ") + health.version);
463
+ }
464
+ if (health.uptime) {
465
+ log(chalk.dim("Uptime: ") + health.uptime);
466
+ }
467
+ }
468
+ function displayBalanceToLog(balance, log, logLines) {
469
+ log(chalk.cyan.bold("Wallet Balance:"));
470
+ log(chalk.dim("Address: ") + chalk.cyan(balance.address));
471
+ log(chalk.dim("ETH: ") + chalk.yellow(balance.ethBalance));
472
+ log(chalk.dim("USDC: ") + chalk.green(balance.usdcBalance));
324
473
  }
325
474
  function extractFlag(args, flag) {
326
475
  const index = args.findIndex((arg) => arg === flag);