httpcat-cli 0.2.10-rc.1 → 0.2.10-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.
- package/.github/workflows/sync-version.yml +19 -4
- package/bun.lock +3 -0
- package/dist/commands/balances.d.ts.map +1 -1
- package/dist/commands/balances.js +43 -41
- package/dist/commands/balances.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +213 -20
- package/dist/config.js.map +1 -1
- package/dist/interactive/shell.d.ts.map +1 -1
- package/dist/interactive/shell.js +872 -146
- package/dist/interactive/shell.js.map +1 -1
- package/dist/utils/privateKeyPrompt.d.ts.map +1 -1
- package/dist/utils/privateKeyPrompt.js +13 -9
- package/dist/utils/privateKeyPrompt.js.map +1 -1
- package/package.json +2 -1
|
@@ -10,69 +10,258 @@ import { buyToken, TEST_AMOUNTS, PROD_AMOUNTS } from "../commands/buy.js";
|
|
|
10
10
|
import { sellToken, parseTokenAmount } from "../commands/sell.js";
|
|
11
11
|
import { getTokenInfo } from "../commands/info.js";
|
|
12
12
|
import { listTokens } from "../commands/list.js";
|
|
13
|
+
import { formatCurrency } from "../utils/formatting.js";
|
|
13
14
|
import { getPositions } from "../commands/positions.js";
|
|
14
15
|
import { privateKeyToAccount } from "viem/accounts";
|
|
15
16
|
import { checkHealth } from "../commands/health.js";
|
|
16
17
|
import { startChatStream } from "../commands/chat.js";
|
|
17
18
|
import { checkBalance } from "../commands/balances.js";
|
|
19
|
+
import { viewFees, claimFees } from "../commands/claim.js";
|
|
20
|
+
import { getTransactions } from "../commands/transactions.js";
|
|
21
|
+
import { getAccountInfo, switchAccount, addAccount } from "../commands/account.js";
|
|
22
|
+
import { HttpcatError } from "../client.js";
|
|
23
|
+
// Detect terminal background color
|
|
24
|
+
function detectTerminalBackground() {
|
|
25
|
+
// Check COLORFGBG (format: "foreground;background")
|
|
26
|
+
// Background values: 0-7 are dark, 8-15 are light
|
|
27
|
+
const colorfgbg = process.env.COLORFGBG;
|
|
28
|
+
if (colorfgbg) {
|
|
29
|
+
const parts = colorfgbg.split(";");
|
|
30
|
+
if (parts.length >= 2) {
|
|
31
|
+
const bg = parseInt(parts[1], 10);
|
|
32
|
+
if (!isNaN(bg)) {
|
|
33
|
+
// 0-7 are dark backgrounds, 8-15 are light
|
|
34
|
+
return bg < 8 ? "dark" : "light";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Check TERM_PROGRAM for common terminals
|
|
39
|
+
const termProgram = process.env.TERM_PROGRAM?.toLowerCase();
|
|
40
|
+
if (termProgram) {
|
|
41
|
+
// These terminals often have dark backgrounds by default
|
|
42
|
+
if (["iterm2", "vscode", "hyper", "alacritty", "kitty"].includes(termProgram)) {
|
|
43
|
+
return "dark";
|
|
44
|
+
}
|
|
45
|
+
// These often have light backgrounds
|
|
46
|
+
if (["apple_terminal"].includes(termProgram)) {
|
|
47
|
+
return "light";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Check for common dark terminal indicators
|
|
51
|
+
const term = process.env.TERM?.toLowerCase() || "";
|
|
52
|
+
if (term.includes("256") || term.includes("xterm")) {
|
|
53
|
+
// Most modern terminals default to dark
|
|
54
|
+
return "dark";
|
|
55
|
+
}
|
|
56
|
+
// Default to dark (safer assumption for modern terminals)
|
|
57
|
+
return "dark";
|
|
58
|
+
}
|
|
18
59
|
export async function startInteractiveShell(client) {
|
|
19
|
-
//
|
|
60
|
+
// Auto-detect terminal background and set default theme
|
|
61
|
+
const detectedBg = detectTerminalBackground();
|
|
62
|
+
let currentTheme = detectedBg === "dark" ? "dark" : "win95";
|
|
63
|
+
// Create blessed screen with optimized settings
|
|
20
64
|
const screen = blessed.screen({
|
|
21
65
|
smartCSR: true,
|
|
22
66
|
title: "httpcat Interactive Shell",
|
|
23
67
|
fullUnicode: true,
|
|
68
|
+
fastCSR: false, // Disable fast CSR to prevent rendering issues
|
|
69
|
+
cursor: {
|
|
70
|
+
artificial: true,
|
|
71
|
+
shape: "line",
|
|
72
|
+
blink: true,
|
|
73
|
+
color: "green",
|
|
74
|
+
},
|
|
75
|
+
// Ensure cursor is always visible
|
|
76
|
+
forceUnicode: false,
|
|
24
77
|
});
|
|
25
78
|
const network = client.getNetwork();
|
|
26
|
-
//
|
|
79
|
+
// Theme colors - no backgrounds, just borders
|
|
80
|
+
const getThemeColors = (theme) => {
|
|
81
|
+
switch (theme) {
|
|
82
|
+
case "light":
|
|
83
|
+
return {
|
|
84
|
+
bg: "default", // Transparent/default
|
|
85
|
+
fg: "black",
|
|
86
|
+
border: "black",
|
|
87
|
+
inputBg: "default",
|
|
88
|
+
inputFg: "black", // Explicit black for visibility
|
|
89
|
+
inputFocusBg: "default",
|
|
90
|
+
inputFocusFg: "black",
|
|
91
|
+
};
|
|
92
|
+
case "win95":
|
|
93
|
+
return {
|
|
94
|
+
bg: "default",
|
|
95
|
+
fg: "black",
|
|
96
|
+
border: "black",
|
|
97
|
+
inputBg: "default",
|
|
98
|
+
inputFg: "black", // Explicit black for visibility
|
|
99
|
+
inputFocusBg: "default",
|
|
100
|
+
inputFocusFg: "black",
|
|
101
|
+
};
|
|
102
|
+
default: // dark
|
|
103
|
+
return {
|
|
104
|
+
bg: "default",
|
|
105
|
+
fg: "green",
|
|
106
|
+
border: "green",
|
|
107
|
+
inputBg: "default",
|
|
108
|
+
inputFg: "green", // Explicit green for visibility
|
|
109
|
+
inputFocusBg: "default",
|
|
110
|
+
inputFocusFg: "green",
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
let themeColors = getThemeColors(currentTheme);
|
|
115
|
+
// Create header box with thick borders, transparent background
|
|
27
116
|
const headerBox = blessed.box({
|
|
28
117
|
top: 0,
|
|
29
118
|
left: 0,
|
|
30
119
|
width: "100%",
|
|
31
|
-
height:
|
|
120
|
+
height: 16,
|
|
32
121
|
content: "",
|
|
33
|
-
tags:
|
|
122
|
+
tags: true,
|
|
34
123
|
style: {
|
|
35
|
-
fg:
|
|
36
|
-
bg: "
|
|
124
|
+
fg: themeColors.fg,
|
|
125
|
+
bg: "default", // Transparent
|
|
126
|
+
bold: false,
|
|
127
|
+
border: {
|
|
128
|
+
fg: themeColors.border,
|
|
129
|
+
bold: true,
|
|
130
|
+
},
|
|
37
131
|
},
|
|
38
132
|
padding: {
|
|
39
|
-
left:
|
|
133
|
+
left: 0,
|
|
40
134
|
right: 1,
|
|
135
|
+
top: 1,
|
|
136
|
+
bottom: 1,
|
|
41
137
|
},
|
|
138
|
+
border: {
|
|
139
|
+
type: "line",
|
|
140
|
+
fg: themeColors.border,
|
|
141
|
+
ch: "═", // Double line for thicker border
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
// Helper function to build welcome content with account info
|
|
145
|
+
const buildWelcomeContent = async (theme) => {
|
|
146
|
+
const welcomeLines = [];
|
|
147
|
+
const colorTag = theme === "dark" ? "green-fg" : "black-fg";
|
|
148
|
+
// Old-school pattern in top bar
|
|
149
|
+
const pattern = "═" + "─".repeat(78) + "═";
|
|
150
|
+
welcomeLines.push(`{${colorTag}}${pattern}{/${colorTag}}`);
|
|
151
|
+
// Single cat ASCII art
|
|
152
|
+
welcomeLines.push(`{${colorTag}} /\\_/\\{/${colorTag}}`);
|
|
153
|
+
welcomeLines.push(`{${colorTag}} ( ^.^ ){/${colorTag}}`);
|
|
154
|
+
welcomeLines.push(`{${colorTag}} > ^ <{/${colorTag}}`);
|
|
155
|
+
welcomeLines.push(`{${colorTag}} / \\{/${colorTag}}`);
|
|
156
|
+
welcomeLines.push("");
|
|
157
|
+
// Get account info
|
|
158
|
+
let accountInfo = null;
|
|
159
|
+
try {
|
|
160
|
+
const accounts = config.getAllAccounts();
|
|
161
|
+
const activeIndex = config.getActiveAccountIndex();
|
|
162
|
+
const account = accounts.find((acc) => acc.index === activeIndex);
|
|
163
|
+
if (account) {
|
|
164
|
+
// Get balance info
|
|
165
|
+
try {
|
|
166
|
+
const privateKey = config.getAccountPrivateKey(activeIndex);
|
|
167
|
+
const balance = await checkBalance(privateKey, true); // silent mode
|
|
168
|
+
accountInfo = {
|
|
169
|
+
account,
|
|
170
|
+
balance,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// If balance check fails, just show account info without balance
|
|
175
|
+
accountInfo = { account };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
// If account info fails, continue without it
|
|
181
|
+
}
|
|
182
|
+
// Playful cat-like greetings with account info
|
|
183
|
+
const greetings = [
|
|
184
|
+
`{green-fg}Meow! Welcome to httpcat!{/green-fg}`,
|
|
185
|
+
`{green-fg}*purrs* Ready to play with some tokens?{/green-fg}`,
|
|
186
|
+
`{green-fg}Connected to: {cyan-fg}${network}{/cyan-fg}{/green-fg}`,
|
|
187
|
+
];
|
|
188
|
+
// Add account information
|
|
189
|
+
if (accountInfo) {
|
|
190
|
+
const { account, balance } = accountInfo;
|
|
191
|
+
const accountType = account.type === "custom" ? "Custom" : "Seed-Derived";
|
|
192
|
+
const accountLabel = account.label ? ` (${account.label})` : "";
|
|
193
|
+
greetings.push("");
|
|
194
|
+
greetings.push(`{cyan-fg}Account #{green-fg}${account.index}{/green-fg} | {green-fg}${accountType}${accountLabel}{/green-fg}{/cyan-fg}`);
|
|
195
|
+
if (balance) {
|
|
196
|
+
const ethDisplay = balance.ethFormatted || balance.ethBalance || "0 ETH";
|
|
197
|
+
const usdcDisplay = balance.usdcFormatted || balance.usdcBalance || "$0.00";
|
|
198
|
+
greetings.push(`{cyan-fg}Balance: {yellow-fg}${ethDisplay}{/yellow-fg} | {green-fg}${usdcDisplay}{/green-fg}{/cyan-fg}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
greetings.push("");
|
|
202
|
+
greetings.push(`{yellow-fg}Pssst... type {bold}help{/bold} if you want to see what I can do!{/yellow-fg}`);
|
|
203
|
+
greetings.push(`{yellow-fg}Or just start playing - I'm curious like a cat!{/yellow-fg}`);
|
|
204
|
+
welcomeLines.push(...greetings);
|
|
205
|
+
return welcomeLines.join("\n");
|
|
206
|
+
};
|
|
207
|
+
// Set initial header content
|
|
208
|
+
buildWelcomeContent(currentTheme).then((content) => {
|
|
209
|
+
headerBox.setContent(content);
|
|
210
|
+
screen.render();
|
|
42
211
|
});
|
|
43
|
-
//
|
|
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)
|
|
212
|
+
// Create output log box (scrollable) with thick borders, transparent background
|
|
54
213
|
const outputBox = blessed.log({
|
|
55
|
-
top:
|
|
214
|
+
top: 16,
|
|
56
215
|
left: 0,
|
|
57
216
|
width: "100%",
|
|
58
|
-
|
|
217
|
+
bottom: 4, // Leave space for input box at bottom
|
|
59
218
|
tags: true,
|
|
60
219
|
scrollable: true,
|
|
61
220
|
alwaysScroll: true,
|
|
62
221
|
scrollbar: {
|
|
63
222
|
ch: " ",
|
|
64
|
-
inverse:
|
|
223
|
+
inverse: currentTheme !== "dark",
|
|
65
224
|
},
|
|
66
225
|
style: {
|
|
67
|
-
fg:
|
|
68
|
-
bg: "
|
|
226
|
+
fg: themeColors.fg,
|
|
227
|
+
bg: "default", // Transparent
|
|
228
|
+
border: {
|
|
229
|
+
fg: themeColors.border,
|
|
230
|
+
bold: true,
|
|
231
|
+
},
|
|
69
232
|
},
|
|
70
233
|
padding: {
|
|
71
|
-
left:
|
|
234
|
+
left: 0,
|
|
72
235
|
right: 1,
|
|
73
236
|
},
|
|
237
|
+
mouse: true, // Enable mouse scrolling
|
|
238
|
+
border: {
|
|
239
|
+
type: "line",
|
|
240
|
+
fg: themeColors.border,
|
|
241
|
+
ch: "═", // Double line for thicker border
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
// Create prompt label with bold font (appears larger) - positioned inside input box
|
|
245
|
+
const promptLabel = blessed.text({
|
|
246
|
+
bottom: 1,
|
|
247
|
+
left: 2,
|
|
248
|
+
width: 8, // Exactly "httpcat>" (8 characters)
|
|
249
|
+
height: 1,
|
|
250
|
+
content: "",
|
|
251
|
+
tags: true,
|
|
252
|
+
style: {
|
|
253
|
+
fg: themeColors.fg,
|
|
254
|
+
bg: "default", // Transparent
|
|
255
|
+
bold: true,
|
|
256
|
+
},
|
|
74
257
|
});
|
|
75
|
-
//
|
|
258
|
+
// Helper to update prompt label content
|
|
259
|
+
const updatePromptLabel = (theme) => {
|
|
260
|
+
const colorTag = theme === "dark" ? "green-fg" : "black-fg";
|
|
261
|
+
promptLabel.content = `{${colorTag}}{bold}httpcat>{/bold}{/${colorTag}}`;
|
|
262
|
+
};
|
|
263
|
+
updatePromptLabel(currentTheme);
|
|
264
|
+
// Create input box with visible cursor and stylish border
|
|
76
265
|
const inputBox = blessed.textbox({
|
|
77
266
|
bottom: 0,
|
|
78
267
|
left: 0,
|
|
@@ -80,35 +269,82 @@ export async function startInteractiveShell(client) {
|
|
|
80
269
|
height: 3,
|
|
81
270
|
inputOnFocus: true,
|
|
82
271
|
keys: true,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
fg: "white",
|
|
88
|
-
bg: "blue",
|
|
89
|
-
},
|
|
90
|
-
},
|
|
272
|
+
secret: false,
|
|
273
|
+
tags: true,
|
|
274
|
+
alwaysScroll: false,
|
|
275
|
+
scrollable: false,
|
|
91
276
|
padding: {
|
|
92
|
-
left:
|
|
277
|
+
left: 10, // Space for "httpcat>" prompt (8 chars) + 2 for spacing
|
|
93
278
|
right: 1,
|
|
279
|
+
top: 0,
|
|
280
|
+
bottom: 0,
|
|
281
|
+
},
|
|
282
|
+
cursor: {
|
|
283
|
+
artificial: true,
|
|
284
|
+
shape: "block", // Block cursor is more visible than line
|
|
285
|
+
blink: true,
|
|
286
|
+
color: currentTheme === "dark" ? "green" : "black",
|
|
94
287
|
},
|
|
95
|
-
});
|
|
96
|
-
// Create a prompt label
|
|
97
|
-
const promptLabel = blessed.text({
|
|
98
|
-
bottom: 1,
|
|
99
|
-
left: 1,
|
|
100
|
-
content: "httpcat> ",
|
|
101
288
|
style: {
|
|
102
|
-
fg:
|
|
103
|
-
bg: "
|
|
289
|
+
fg: themeColors.fg,
|
|
290
|
+
bg: "default",
|
|
291
|
+
border: {
|
|
292
|
+
fg: themeColors.border,
|
|
293
|
+
bold: true,
|
|
294
|
+
},
|
|
295
|
+
focus: {
|
|
296
|
+
fg: themeColors.fg,
|
|
297
|
+
bg: "default",
|
|
298
|
+
border: {
|
|
299
|
+
fg: themeColors.border,
|
|
300
|
+
bold: true,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
border: {
|
|
305
|
+
type: "line",
|
|
306
|
+
fg: themeColors.border,
|
|
307
|
+
ch: "─", // Single line border
|
|
104
308
|
},
|
|
105
309
|
});
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
310
|
+
// Helper to update theme
|
|
311
|
+
const updateTheme = (newTheme) => {
|
|
312
|
+
currentTheme = newTheme;
|
|
313
|
+
themeColors = getThemeColors(currentTheme);
|
|
314
|
+
// Update screen cursor color
|
|
315
|
+
screen.cursor.color = currentTheme === "dark" ? "green" : "black";
|
|
316
|
+
// Update header content with new theme colors
|
|
317
|
+
buildWelcomeContent(currentTheme).then((content) => {
|
|
318
|
+
headerBox.setContent(content);
|
|
319
|
+
screen.render();
|
|
320
|
+
});
|
|
321
|
+
// Update all widget styles
|
|
322
|
+
headerBox.style.fg = themeColors.fg;
|
|
323
|
+
headerBox.style.bg = "default"; // Transparent
|
|
324
|
+
headerBox.border = { type: "line", fg: themeColors.border, ch: "═" };
|
|
325
|
+
outputBox.style.fg = themeColors.fg;
|
|
326
|
+
outputBox.style.bg = "default"; // Transparent
|
|
327
|
+
outputBox.scrollbar.inverse = currentTheme !== "dark";
|
|
328
|
+
outputBox.border = { type: "line", fg: themeColors.border, ch: "═" };
|
|
329
|
+
updatePromptLabel(currentTheme);
|
|
330
|
+
promptLabel.style.fg = themeColors.fg;
|
|
331
|
+
promptLabel.style.bg = "default"; // Transparent
|
|
332
|
+
promptLabel.style.bold = true;
|
|
333
|
+
// Update input box cursor, style, and border
|
|
334
|
+
inputBox.style.fg = themeColors.fg;
|
|
335
|
+
inputBox.style.bg = "default";
|
|
336
|
+
inputBox.style.focus.fg = themeColors.fg;
|
|
337
|
+
inputBox.style.focus.bg = "default";
|
|
338
|
+
inputBox.style.border.fg = themeColors.border;
|
|
339
|
+
inputBox.style.focus.border.fg = themeColors.border;
|
|
340
|
+
inputBox.border = { type: "line", fg: themeColors.border, ch: "─" };
|
|
341
|
+
if (inputBox.cursor) {
|
|
342
|
+
inputBox.cursor.color = currentTheme === "dark" ? "green" : "black";
|
|
343
|
+
}
|
|
344
|
+
screen.cursor.color = currentTheme === "dark" ? "green" : "black";
|
|
345
|
+
screen.render();
|
|
346
|
+
};
|
|
347
|
+
// Helper to log output (define before use)
|
|
112
348
|
const log = (text) => {
|
|
113
349
|
outputBox.log(text);
|
|
114
350
|
outputBox.setScrollPerc(100);
|
|
@@ -120,19 +356,41 @@ export async function startInteractiveShell(client) {
|
|
|
120
356
|
outputBox.setScrollPerc(100);
|
|
121
357
|
screen.render();
|
|
122
358
|
};
|
|
359
|
+
// Wrap toggleTheme to also log
|
|
360
|
+
const toggleThemeWithLog = () => {
|
|
361
|
+
const themes = ["win95", "dark", "light"];
|
|
362
|
+
const currentIndex = themes.indexOf(currentTheme);
|
|
363
|
+
const nextTheme = themes[(currentIndex + 1) % themes.length];
|
|
364
|
+
updateTheme(nextTheme);
|
|
365
|
+
log(chalk.blue(`Theme switched to: ${nextTheme}`));
|
|
366
|
+
};
|
|
367
|
+
// Append all widgets in correct z-order (last appended is on top)
|
|
368
|
+
screen.append(headerBox);
|
|
369
|
+
screen.append(outputBox);
|
|
370
|
+
screen.append(inputBox);
|
|
371
|
+
screen.append(promptLabel); // Prompt label on top so it's always visible
|
|
372
|
+
// Handle F1 for theme toggle
|
|
373
|
+
screen.key(["f1"], () => {
|
|
374
|
+
toggleThemeWithLog();
|
|
375
|
+
});
|
|
376
|
+
// Store toggle function for command handler
|
|
377
|
+
screen.toggleTheme = toggleThemeWithLog;
|
|
378
|
+
screen.updateTheme = updateTheme;
|
|
123
379
|
// Handle input submission
|
|
124
380
|
inputBox.on("submit", async (value) => {
|
|
125
381
|
const trimmed = value.trim();
|
|
126
382
|
inputBox.clearValue();
|
|
383
|
+
screen.render(); // Clear input immediately
|
|
127
384
|
if (!trimmed) {
|
|
128
385
|
inputBox.focus();
|
|
129
386
|
screen.render();
|
|
130
387
|
return;
|
|
131
388
|
}
|
|
132
|
-
|
|
389
|
+
// Log the command with prompt
|
|
390
|
+
log(`{green-fg}httpcat>{/green-fg} ${trimmed}`);
|
|
133
391
|
const [command, ...args] = trimmed.split(/\s+/);
|
|
134
392
|
try {
|
|
135
|
-
await handleCommand(client, command.toLowerCase(), args, log, logLines, screen, inputBox);
|
|
393
|
+
await handleCommand(client, command.toLowerCase(), args, log, logLines, screen, inputBox, currentTheme, buildWelcomeContent, headerBox);
|
|
136
394
|
}
|
|
137
395
|
catch (error) {
|
|
138
396
|
log(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
@@ -153,11 +411,20 @@ export async function startInteractiveShell(client) {
|
|
|
153
411
|
inputBox.clearValue();
|
|
154
412
|
screen.render();
|
|
155
413
|
});
|
|
414
|
+
// Handle Ctrl+C to quit (also works from input box)
|
|
415
|
+
inputBox.key(["C-c"], () => {
|
|
416
|
+
screen.destroy();
|
|
417
|
+
printCat("sleeping");
|
|
418
|
+
console.log(chalk.cyan("Goodbye! 👋"));
|
|
419
|
+
process.exit(0);
|
|
420
|
+
});
|
|
156
421
|
// Focus input and render
|
|
157
422
|
inputBox.focus();
|
|
158
423
|
screen.render();
|
|
424
|
+
// Show available commands on load
|
|
425
|
+
displayHelp(log, logLines);
|
|
159
426
|
}
|
|
160
|
-
async function handleCommand(client, command, args, log, logLines, screen, inputBox) {
|
|
427
|
+
async function handleCommand(client, command, args, log, logLines, screen, inputBox, currentTheme, buildWelcomeContent, headerBox) {
|
|
161
428
|
switch (command) {
|
|
162
429
|
case "help":
|
|
163
430
|
displayHelp(log, logLines);
|
|
@@ -171,7 +438,7 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
171
438
|
const photoUrl = extractFlag(args, "--photo");
|
|
172
439
|
const bannerUrl = extractFlag(args, "--banner");
|
|
173
440
|
const websiteUrl = extractFlag(args, "--website");
|
|
174
|
-
log(chalk.
|
|
441
|
+
log(chalk.blue("Creating token..."));
|
|
175
442
|
screen.render();
|
|
176
443
|
const result = await createToken(client, {
|
|
177
444
|
name,
|
|
@@ -185,14 +452,21 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
185
452
|
}
|
|
186
453
|
case "buy": {
|
|
187
454
|
if (args.length < 2) {
|
|
188
|
-
log(chalk.red("Usage: buy <address|name|symbol> <amount>"));
|
|
455
|
+
log(chalk.red("Usage: buy <address|name|symbol> <amount> [--repeat <count>] [--delay <ms>]"));
|
|
189
456
|
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'));
|
|
457
|
+
log(chalk.dim(' Examples: buy 0x1234... 0.20, buy "My Token" 0.10, buy MTK 0.05 --repeat 10'));
|
|
191
458
|
return;
|
|
192
459
|
}
|
|
193
460
|
const [identifier, amountInput] = args;
|
|
194
461
|
const isTestMode = client.getNetwork().includes("sepolia");
|
|
195
462
|
const validAmounts = isTestMode ? TEST_AMOUNTS : PROD_AMOUNTS;
|
|
463
|
+
// Parse flags
|
|
464
|
+
const repeatCount = extractFlag(args, "--repeat")
|
|
465
|
+
? parseInt(extractFlag(args, "--repeat") || "1", 10)
|
|
466
|
+
: undefined;
|
|
467
|
+
const delayMs = extractFlag(args, "--delay")
|
|
468
|
+
? parseInt(extractFlag(args, "--delay") || "0", 10)
|
|
469
|
+
: 0;
|
|
196
470
|
// Try to validate and normalize the amount
|
|
197
471
|
let amount;
|
|
198
472
|
try {
|
|
@@ -204,10 +478,72 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
204
478
|
log(chalk.dim(`Valid amounts: ${validAmounts.join(", ")}`));
|
|
205
479
|
return;
|
|
206
480
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
481
|
+
const privateKey = config.getPrivateKey();
|
|
482
|
+
// Handle repeat mode
|
|
483
|
+
if (repeatCount && repeatCount > 0) {
|
|
484
|
+
const results = [];
|
|
485
|
+
let totalSpent = 0;
|
|
486
|
+
let stoppedEarly = false;
|
|
487
|
+
let stopReason = "";
|
|
488
|
+
for (let i = 1; i <= repeatCount; i++) {
|
|
489
|
+
try {
|
|
490
|
+
log(chalk.blue(`Buy ${i}/${repeatCount}...`));
|
|
491
|
+
screen.render();
|
|
492
|
+
const result = await buyToken(client, identifier, amount, isTestMode, i > 1, // Silent after first buy
|
|
493
|
+
privateKey);
|
|
494
|
+
results.push(result);
|
|
495
|
+
totalSpent += parseFloat(result.amountSpent);
|
|
496
|
+
// Display compact result
|
|
497
|
+
displayBuyResultToLog(result, log, logLines);
|
|
498
|
+
// Check if token graduated
|
|
499
|
+
if (result.graduationReached) {
|
|
500
|
+
stoppedEarly = true;
|
|
501
|
+
stopReason = "Token graduated";
|
|
502
|
+
log(chalk.green("🎓 Token has graduated! Stopping buy loop."));
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
// Apply delay between iterations (except after the last one)
|
|
506
|
+
if (i < repeatCount && delayMs > 0) {
|
|
507
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
catch (error) {
|
|
511
|
+
// Handle insufficient funds (402) gracefully
|
|
512
|
+
if (error instanceof HttpcatError && error.status === 402) {
|
|
513
|
+
stoppedEarly = true;
|
|
514
|
+
stopReason = "Insufficient funds";
|
|
515
|
+
log(chalk.yellow("💡 Insufficient funds. Stopping buy loop."));
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
// For other errors, re-throw to be handled by outer catch
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// Show final summary
|
|
523
|
+
if (results.length > 0) {
|
|
524
|
+
const lastResult = results[results.length - 1];
|
|
525
|
+
const graduationStatus = lastResult.graduationReached
|
|
526
|
+
? "✅ GRADUATED!"
|
|
527
|
+
: `${(lastResult.graduationProgress || 0).toFixed(2)}%`;
|
|
528
|
+
log("");
|
|
529
|
+
log(chalk.cyan.bold("📊 Repeat Buy Summary"));
|
|
530
|
+
log(chalk.dim("─".repeat(50)));
|
|
531
|
+
log(`Total buys completed: ${chalk.bold(results.length.toString())}${repeatCount ? `/${repeatCount}` : ""}`);
|
|
532
|
+
log(`Total amount spent: ${chalk.bold(`$${totalSpent.toFixed(2)}`)}`);
|
|
533
|
+
log(`Final token price: ${chalk.bold(lastResult.newPrice)}`);
|
|
534
|
+
log(`Final graduation: ${chalk.bold(graduationStatus)}`);
|
|
535
|
+
if (stoppedEarly) {
|
|
536
|
+
log(`Stopped early: ${chalk.yellow(stopReason)}`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
// Normal single buy execution
|
|
542
|
+
log(chalk.blue("Buying tokens..."));
|
|
543
|
+
screen.render();
|
|
544
|
+
const result = await buyToken(client, identifier, amount, isTestMode, false, privateKey);
|
|
545
|
+
displayBuyResultToLog(result, log, logLines);
|
|
546
|
+
}
|
|
211
547
|
break;
|
|
212
548
|
}
|
|
213
549
|
case "sell": {
|
|
@@ -217,7 +553,7 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
217
553
|
return;
|
|
218
554
|
}
|
|
219
555
|
const [identifier, amountInput] = args;
|
|
220
|
-
log(chalk.
|
|
556
|
+
log(chalk.blue("Checking token info..."));
|
|
221
557
|
screen.render();
|
|
222
558
|
const info = await getTokenInfo(client, identifier);
|
|
223
559
|
if (!info.userPosition || info.userPosition.tokensOwned === "0") {
|
|
@@ -225,7 +561,7 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
225
561
|
return;
|
|
226
562
|
}
|
|
227
563
|
const tokenAmount = parseTokenAmount(amountInput, info.userPosition.tokensOwned);
|
|
228
|
-
log(chalk.
|
|
564
|
+
log(chalk.blue("Selling tokens..."));
|
|
229
565
|
screen.render();
|
|
230
566
|
const result = await sellToken(client, identifier, tokenAmount, false);
|
|
231
567
|
displaySellResultToLog(result, log, logLines);
|
|
@@ -242,7 +578,7 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
242
578
|
const privateKey = config.getPrivateKey();
|
|
243
579
|
const account = privateKeyToAccount(privateKey);
|
|
244
580
|
const userAddress = account.address;
|
|
245
|
-
log(chalk.
|
|
581
|
+
log(chalk.blue("Fetching token info..."));
|
|
246
582
|
screen.render();
|
|
247
583
|
const info = await getTokenInfo(client, identifier, userAddress);
|
|
248
584
|
displayTokenInfoToLog(info, log, logLines);
|
|
@@ -252,7 +588,7 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
252
588
|
const page = parseInt(extractFlag(args, "--page") || "1");
|
|
253
589
|
const limit = parseInt(extractFlag(args, "--limit") || "20");
|
|
254
590
|
const sortBy = (extractFlag(args, "--sort") || "mcap");
|
|
255
|
-
log(chalk.
|
|
591
|
+
log(chalk.blue("Fetching token list..."));
|
|
256
592
|
screen.render();
|
|
257
593
|
const result = await listTokens(client, page, limit, sortBy);
|
|
258
594
|
displayTokenListToLog(result, log, logLines);
|
|
@@ -262,10 +598,24 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
262
598
|
const privateKey = config.getPrivateKey();
|
|
263
599
|
const account = privateKeyToAccount(privateKey);
|
|
264
600
|
const userAddress = account.address;
|
|
265
|
-
|
|
601
|
+
// Parse flags
|
|
602
|
+
const activeOnly = args.includes("--active");
|
|
603
|
+
const graduatedOnly = args.includes("--graduated");
|
|
604
|
+
log(chalk.blue("Fetching positions..."));
|
|
266
605
|
screen.render();
|
|
267
606
|
const result = await getPositions(client, userAddress);
|
|
268
|
-
|
|
607
|
+
// Filter positions if flags are set
|
|
608
|
+
let filteredResult = result;
|
|
609
|
+
if (activeOnly || graduatedOnly) {
|
|
610
|
+
filteredResult = {
|
|
611
|
+
...result,
|
|
612
|
+
positions: result.positions.filter((p) => activeOnly
|
|
613
|
+
? p.token.status !== "graduated"
|
|
614
|
+
: p.token.status === "graduated"),
|
|
615
|
+
};
|
|
616
|
+
filteredResult.total = filteredResult.positions.length;
|
|
617
|
+
}
|
|
618
|
+
displayPositionsToLog(filteredResult, log, logLines);
|
|
269
619
|
break;
|
|
270
620
|
}
|
|
271
621
|
case "health": {
|
|
@@ -278,15 +628,15 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
278
628
|
case "balances": {
|
|
279
629
|
try {
|
|
280
630
|
const privateKey = config.getPrivateKey();
|
|
281
|
-
log(chalk.
|
|
631
|
+
log(chalk.blue("Checking balance..."));
|
|
282
632
|
screen.render();
|
|
283
|
-
const balance = await checkBalance(privateKey);
|
|
633
|
+
const balance = await checkBalance(privateKey, true); // silent mode
|
|
284
634
|
displayBalanceToLog(balance, log, logLines);
|
|
285
635
|
}
|
|
286
636
|
catch (error) {
|
|
287
|
-
log(chalk.
|
|
637
|
+
log(chalk.blue("Checking balance..."));
|
|
288
638
|
screen.render();
|
|
289
|
-
const balance = await checkBalance();
|
|
639
|
+
const balance = await checkBalance(undefined, true); // silent mode
|
|
290
640
|
displayBalanceToLog(balance, log, logLines);
|
|
291
641
|
}
|
|
292
642
|
break;
|
|
@@ -309,16 +659,18 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
309
659
|
break;
|
|
310
660
|
}
|
|
311
661
|
case "network": {
|
|
312
|
-
log(chalk.yellow("Current network: ") + chalk.
|
|
313
|
-
log(chalk.
|
|
662
|
+
log(chalk.yellow("Current network: ") + chalk.green(config.get("network")));
|
|
663
|
+
log(chalk.blue("Note: Only base-sepolia (testnet) is currently supported"));
|
|
314
664
|
break;
|
|
315
665
|
}
|
|
316
666
|
case "chat": {
|
|
317
667
|
// For chat, we need to exit the blessed shell and start chat stream
|
|
318
668
|
log(chalk.yellow("Starting chat mode..."));
|
|
319
669
|
screen.destroy();
|
|
320
|
-
const tokenIdentifier = args.length > 0 ? args[0] : undefined;
|
|
321
|
-
|
|
670
|
+
const tokenIdentifier = args.length > 0 && !args[0].startsWith("--") ? args[0] : undefined;
|
|
671
|
+
const inputFormatRaw = extractFlag(args, "--input-format") || "text";
|
|
672
|
+
const inputFormat = (inputFormatRaw === "stream-json" ? "stream-json" : "text");
|
|
673
|
+
await startChatStream(client, false, tokenIdentifier, inputFormat);
|
|
322
674
|
process.exit(0);
|
|
323
675
|
break;
|
|
324
676
|
}
|
|
@@ -333,7 +685,228 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
333
685
|
// Clear the output box
|
|
334
686
|
screen.children[1].setContent("");
|
|
335
687
|
screen.render();
|
|
688
|
+
// Show playful message with ASCII cat
|
|
689
|
+
const catArt = currentTheme === "dark"
|
|
690
|
+
? `{green-fg} /\\_/\\{/green-fg}
|
|
691
|
+
{green-fg} ( ^.^ ){/green-fg}
|
|
692
|
+
{green-fg} > ^ <{/green-fg}`
|
|
693
|
+
: `{black-fg} /\\_/\\{/black-fg}
|
|
694
|
+
{black-fg} ( ^.^ ){/black-fg}
|
|
695
|
+
{black-fg} > ^ <{/black-fg}`;
|
|
696
|
+
log("");
|
|
697
|
+
log(catArt);
|
|
698
|
+
log(chalk.green.bold("Terminal cleared!"));
|
|
699
|
+
log("");
|
|
336
700
|
break;
|
|
701
|
+
case "claim": {
|
|
702
|
+
if (args.length < 1) {
|
|
703
|
+
log(chalk.red("Usage: claim <address|name|symbol> [--execute] [--address <address>]"));
|
|
704
|
+
log(chalk.dim(' Examples: claim MTK, claim "My Token" --execute'));
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const [identifier] = args;
|
|
708
|
+
const execute = args.includes("--execute");
|
|
709
|
+
const callerAddress = extractFlag(args, "--address");
|
|
710
|
+
if (execute) {
|
|
711
|
+
const privateKey = config.getPrivateKey();
|
|
712
|
+
const account = privateKeyToAccount(privateKey);
|
|
713
|
+
const address = callerAddress || account.address;
|
|
714
|
+
log(chalk.blue("Claiming fees..."));
|
|
715
|
+
screen.render();
|
|
716
|
+
const result = await claimFees(client, identifier, address, false);
|
|
717
|
+
displayClaimResultToLog(result, log, logLines);
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
log(chalk.blue("Fetching fee information..."));
|
|
721
|
+
screen.render();
|
|
722
|
+
const result = await viewFees(client, identifier, false);
|
|
723
|
+
displayFeesToLog(result, log, logLines);
|
|
724
|
+
}
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
case "transactions": {
|
|
728
|
+
const privateKey = config.getPrivateKey();
|
|
729
|
+
const account = privateKeyToAccount(privateKey);
|
|
730
|
+
const userAddress = account.address;
|
|
731
|
+
const input = {};
|
|
732
|
+
if (extractFlag(args, "--user")) {
|
|
733
|
+
input.userAddress = extractFlag(args, "--user");
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
input.userAddress = userAddress;
|
|
737
|
+
}
|
|
738
|
+
if (extractFlag(args, "--token")) {
|
|
739
|
+
input.tokenId = extractFlag(args, "--token");
|
|
740
|
+
}
|
|
741
|
+
if (extractFlag(args, "--type")) {
|
|
742
|
+
const type = extractFlag(args, "--type");
|
|
743
|
+
if (!["buy", "sell", "airdrop"].includes(type || "")) {
|
|
744
|
+
log(chalk.red("Invalid type. Must be: buy, sell, or airdrop"));
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
input.type = type;
|
|
748
|
+
}
|
|
749
|
+
if (extractFlag(args, "--limit")) {
|
|
750
|
+
input.limit = parseInt(extractFlag(args, "--limit") || "50", 10);
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
input.limit = 50;
|
|
754
|
+
}
|
|
755
|
+
if (extractFlag(args, "--offset")) {
|
|
756
|
+
input.offset = parseInt(extractFlag(args, "--offset") || "0", 10);
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
input.offset = 0;
|
|
760
|
+
}
|
|
761
|
+
log(chalk.blue("Fetching transactions..."));
|
|
762
|
+
screen.render();
|
|
763
|
+
const result = await getTransactions(client, input);
|
|
764
|
+
displayTransactionsToLog(result, log, logLines);
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
case "account": {
|
|
768
|
+
if (args.length === 0) {
|
|
769
|
+
// Show account info
|
|
770
|
+
log(chalk.blue("Fetching account information..."));
|
|
771
|
+
screen.render();
|
|
772
|
+
const result = await getAccountInfo();
|
|
773
|
+
displayAccountInfoToLog(result, log, logLines);
|
|
774
|
+
}
|
|
775
|
+
else if (args[0] === "list") {
|
|
776
|
+
// For list, we need to capture console output or recreate the display
|
|
777
|
+
const accounts = config.getAllAccounts();
|
|
778
|
+
const activeIndex = config.getActiveAccountIndex();
|
|
779
|
+
log("");
|
|
780
|
+
log(chalk.green.bold("📋 All Accounts"));
|
|
781
|
+
log("");
|
|
782
|
+
if (accounts.length === 0) {
|
|
783
|
+
log(chalk.yellow("No accounts configured."));
|
|
784
|
+
log(chalk.blue('Run "httpcat config" to set up your wallet.'));
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
for (const account of accounts) {
|
|
788
|
+
const isActive = account.index === activeIndex;
|
|
789
|
+
const status = isActive ? chalk.green("● Active") : chalk.blue("○ Inactive");
|
|
790
|
+
const type = account.type === "custom" ? "Custom" : "Seed-Derived";
|
|
791
|
+
const address = account.address.slice(0, 6) + "..." + account.address.slice(-4);
|
|
792
|
+
log(` ${chalk.green(account.index.toString().padEnd(5))} ${chalk.blue(type.padEnd(12))} ${chalk.green(address.padEnd(15))} ${status}`);
|
|
793
|
+
}
|
|
794
|
+
log("");
|
|
795
|
+
log(chalk.blue(`Active account: ${chalk.green(activeIndex.toString())}`));
|
|
796
|
+
}
|
|
797
|
+
else if (args[0] === "switch") {
|
|
798
|
+
if (args.length < 2) {
|
|
799
|
+
log(chalk.red("Usage: account switch <index>"));
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const index = parseInt(args[1], 10);
|
|
803
|
+
if (isNaN(index)) {
|
|
804
|
+
log(chalk.red("Invalid account index"));
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
switchAccount(index);
|
|
808
|
+
const accounts = config.getAllAccounts();
|
|
809
|
+
const account = accounts.find((acc) => acc.index === index);
|
|
810
|
+
log(chalk.green(`✅ Switched to account ${index}`));
|
|
811
|
+
if (account) {
|
|
812
|
+
log(chalk.blue(` Address: ${account.address.slice(0, 6)}...${account.address.slice(-4)}`));
|
|
813
|
+
}
|
|
814
|
+
// Refresh header with new account info
|
|
815
|
+
buildWelcomeContent(currentTheme).then((content) => {
|
|
816
|
+
headerBox.setContent(content);
|
|
817
|
+
screen.render();
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
else if (args[0] === "add") {
|
|
821
|
+
log(chalk.blue("Adding new account..."));
|
|
822
|
+
screen.render();
|
|
823
|
+
await addAccount();
|
|
824
|
+
log(chalk.green("✅ Account added"));
|
|
825
|
+
// Refresh header with new account info
|
|
826
|
+
buildWelcomeContent(currentTheme).then((content) => {
|
|
827
|
+
headerBox.setContent(content);
|
|
828
|
+
screen.render();
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
log(chalk.red("Usage: account [list|switch <index>|add]"));
|
|
833
|
+
}
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
case "env": {
|
|
837
|
+
if (args.length === 0 || args[0] === "show") {
|
|
838
|
+
const current = config.getCurrentEnvironment();
|
|
839
|
+
if (!current) {
|
|
840
|
+
log(chalk.yellow("No environment selected"));
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const env = config.getEnvironmentConfig(current);
|
|
844
|
+
if (!env) {
|
|
845
|
+
log(chalk.yellow(`Environment "${current}" not found`));
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
log(chalk.cyan.bold(`Current Environment: ${chalk.green(current)}`));
|
|
849
|
+
log(chalk.dim(`Agent URL: ${env.agentUrl}`));
|
|
850
|
+
log(chalk.dim(`Network: ${env.network}`));
|
|
851
|
+
}
|
|
852
|
+
else if (args[0] === "list") {
|
|
853
|
+
const envs = config.getEnvironments();
|
|
854
|
+
const current = config.getCurrentEnvironment();
|
|
855
|
+
log(chalk.cyan.bold("Available Environments:"));
|
|
856
|
+
log("");
|
|
857
|
+
for (const [name, env] of Object.entries(envs)) {
|
|
858
|
+
const isCurrent = name === current;
|
|
859
|
+
const prefix = isCurrent ? chalk.green("→ ") : " ";
|
|
860
|
+
const nameDisplay = isCurrent ? chalk.green.bold(name) : chalk.bold(name);
|
|
861
|
+
log(`${prefix}${nameDisplay}`);
|
|
862
|
+
log(chalk.dim(` Agent URL: ${env.agentUrl}`));
|
|
863
|
+
log(chalk.dim(` Network: ${env.network}`));
|
|
864
|
+
log("");
|
|
865
|
+
}
|
|
866
|
+
if (current) {
|
|
867
|
+
log(chalk.dim(`Current environment: ${chalk.green(current)}`));
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
else if (args[0] === "use") {
|
|
871
|
+
if (args.length < 2) {
|
|
872
|
+
log(chalk.red("Usage: env use <name>"));
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
config.setEnvironment(args[1]);
|
|
876
|
+
const env = config.getEnvironmentConfig(args[1]);
|
|
877
|
+
log(chalk.green(`✅ Switched to environment: ${args[1]}`));
|
|
878
|
+
if (env) {
|
|
879
|
+
log(chalk.dim(` Agent URL: ${env.agentUrl}`));
|
|
880
|
+
log(chalk.dim(` Network: ${env.network}`));
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
else if (args[0] === "add") {
|
|
884
|
+
if (args.length < 3) {
|
|
885
|
+
log(chalk.red("Usage: env add <name> <agentUrl> [--network <network>]"));
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const network = extractFlag(args, "--network") || "base-sepolia";
|
|
889
|
+
config.addEnvironment(args[1], args[2], network);
|
|
890
|
+
log(chalk.green(`✅ Added environment: ${args[1]}`));
|
|
891
|
+
log(chalk.dim(` Agent URL: ${args[2]}`));
|
|
892
|
+
log(chalk.dim(` Network: ${network}`));
|
|
893
|
+
}
|
|
894
|
+
else if (args[0] === "update") {
|
|
895
|
+
if (args.length < 3) {
|
|
896
|
+
log(chalk.red("Usage: env update <name> <agentUrl> [--network <network>]"));
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
const network = extractFlag(args, "--network") || "base-sepolia";
|
|
900
|
+
config.updateEnvironment(args[1], args[2], network);
|
|
901
|
+
log(chalk.green(`✅ Updated environment: ${args[1]}`));
|
|
902
|
+
log(chalk.dim(` Agent URL: ${args[2]}`));
|
|
903
|
+
log(chalk.dim(` Network: ${network}`));
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
log(chalk.red("Usage: env [list|use <name>|show|add <name> <url>|update <name> <url>]"));
|
|
907
|
+
}
|
|
908
|
+
break;
|
|
909
|
+
}
|
|
337
910
|
default:
|
|
338
911
|
log(chalk.red(`Unknown command: ${command}`));
|
|
339
912
|
log(chalk.dim('Type "help" for available commands'));
|
|
@@ -341,107 +914,135 @@ async function handleCommand(client, command, args, log, logLines, screen, input
|
|
|
341
914
|
}
|
|
342
915
|
function displayHelp(log, logLines) {
|
|
343
916
|
log("");
|
|
344
|
-
log(chalk.
|
|
917
|
+
log(chalk.green.bold("Available Commands:"));
|
|
345
918
|
log("");
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
];
|
|
365
|
-
for (const [cmd, desc] of commands) {
|
|
366
|
-
log(` ${chalk.cyan(cmd.padEnd(30))} ${chalk.dim(desc)}`);
|
|
367
|
-
}
|
|
919
|
+
log(chalk.green(" create <name> <symbol>"));
|
|
920
|
+
log(chalk.green(" buy <id> <amount> [--repeat N] [--delay MS]"));
|
|
921
|
+
log(chalk.green(" sell <id> <amount|all>"));
|
|
922
|
+
log(chalk.green(" info <id>"));
|
|
923
|
+
log(chalk.green(" list [--sort mcap|created|name] [--page N] [--limit N]"));
|
|
924
|
+
log(chalk.green(" positions [--active|--graduated]"));
|
|
925
|
+
log(chalk.green(" balances"));
|
|
926
|
+
log(chalk.green(" claim <id> [--execute] [--address ADDR]"));
|
|
927
|
+
log(chalk.green(" transactions [--user ADDR] [--token ID] [--type TYPE] [--limit N] [--offset N]"));
|
|
928
|
+
log(chalk.green(" account [list|switch <index>|add]"));
|
|
929
|
+
log(chalk.green(" env [list|use <name>|show|add|update]"));
|
|
930
|
+
log(chalk.green(" chat [token] [--input-format FORMAT]"));
|
|
931
|
+
log(chalk.green(" health"));
|
|
932
|
+
log(chalk.green(" config [--show|--set|--reset]"));
|
|
933
|
+
log(chalk.green(" network"));
|
|
934
|
+
log(chalk.green(" clear"));
|
|
935
|
+
log(chalk.green(" help"));
|
|
936
|
+
log(chalk.green(" exit"));
|
|
368
937
|
log("");
|
|
369
|
-
log(chalk.
|
|
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"));
|
|
938
|
+
log(chalk.blue("Tip: Run any command without arguments to see detailed help"));
|
|
378
939
|
}
|
|
379
940
|
function displayConfigToLog(log, logLines) {
|
|
380
941
|
const cfg = config.getAll();
|
|
381
942
|
log("");
|
|
382
|
-
log(chalk.
|
|
943
|
+
log(chalk.green.bold("Current Configuration:"));
|
|
383
944
|
log("");
|
|
384
|
-
log(chalk.
|
|
385
|
-
log(chalk.
|
|
386
|
-
log(chalk.
|
|
387
|
-
log(chalk.
|
|
388
|
-
log(chalk.
|
|
389
|
-
chalk.
|
|
390
|
-
log(chalk.
|
|
391
|
-
chalk.
|
|
945
|
+
log(chalk.blue("Network: ") + chalk.green(cfg.network));
|
|
946
|
+
log(chalk.blue("Agent URL: ") + chalk.green(cfg.agentUrl));
|
|
947
|
+
log(chalk.blue("Facilitator: ") + chalk.green(cfg.facilitatorUrl));
|
|
948
|
+
log(chalk.blue("Max Payment: ") + chalk.green("$" + cfg.defaultMaxPayment));
|
|
949
|
+
log(chalk.blue("ASCII Art: ") +
|
|
950
|
+
chalk.green(cfg.preferences.enableAsciiArt ? "enabled" : "disabled"));
|
|
951
|
+
log(chalk.blue("Private Key: ") +
|
|
952
|
+
chalk.blue(cfg.privateKey ? "configured" : "not configured"));
|
|
392
953
|
log("");
|
|
393
|
-
log(chalk.
|
|
954
|
+
log(chalk.blue(`Config file: ${config.getConfigPath()}`));
|
|
394
955
|
}
|
|
395
956
|
// Simplified display functions that log to blessed output
|
|
396
957
|
function displayCreateResultToLog(result, log, logLines) {
|
|
397
958
|
log(chalk.green("Token created successfully!"));
|
|
398
|
-
log(chalk.
|
|
399
|
-
log(chalk.
|
|
959
|
+
log(chalk.blue("Name: ") + chalk.green(result.name));
|
|
960
|
+
log(chalk.blue("Symbol: ") + chalk.green(result.symbol));
|
|
400
961
|
if (result.tokenAddress) {
|
|
401
|
-
log(chalk.
|
|
962
|
+
log(chalk.blue("Address: ") + chalk.green(result.tokenAddress));
|
|
402
963
|
}
|
|
403
964
|
}
|
|
404
965
|
function displayBuyResultToLog(result, log, logLines) {
|
|
405
966
|
log(chalk.green("Purchase successful!"));
|
|
406
|
-
log(chalk.
|
|
407
|
-
log(chalk.
|
|
408
|
-
log(chalk.
|
|
409
|
-
log(chalk.
|
|
967
|
+
log(chalk.blue("Tokens received: ") + chalk.green(result.tokensReceived));
|
|
968
|
+
log(chalk.blue("Amount spent: ") + chalk.yellow(result.amountSpent));
|
|
969
|
+
log(chalk.blue("New price: ") + chalk.green(result.newPrice));
|
|
970
|
+
log(chalk.blue("Graduation: ") +
|
|
410
971
|
chalk.magenta(result.graduationProgress.toFixed(2) + "%"));
|
|
411
972
|
}
|
|
412
973
|
function displaySellResultToLog(result, log, logLines) {
|
|
413
974
|
log(chalk.green("Sale successful!"));
|
|
414
|
-
log(chalk.
|
|
415
|
-
log(chalk.
|
|
416
|
-
log(chalk.
|
|
417
|
-
log(chalk.
|
|
975
|
+
log(chalk.blue("Tokens sold: ") + chalk.green(result.tokensSold));
|
|
976
|
+
log(chalk.blue("USDC received: ") + chalk.yellow(result.usdcReceived));
|
|
977
|
+
log(chalk.blue("New price: ") + chalk.green(result.newPrice));
|
|
978
|
+
log(chalk.blue("Graduation: ") +
|
|
418
979
|
chalk.magenta(result.graduationProgress.toFixed(2) + "%"));
|
|
419
980
|
}
|
|
420
981
|
function displayTokenInfoToLog(info, log, logLines) {
|
|
421
|
-
log(chalk.
|
|
982
|
+
log(chalk.green.bold(info.name) + ` (${info.symbol})`);
|
|
422
983
|
if (info.tokenAddress) {
|
|
423
|
-
log(chalk.
|
|
984
|
+
log(chalk.blue("Address: ") + chalk.green(info.tokenAddress));
|
|
424
985
|
}
|
|
425
|
-
log(chalk.
|
|
426
|
-
log(chalk.
|
|
427
|
-
log(chalk.
|
|
986
|
+
log(chalk.blue("Price: ") + chalk.green(info.currentPrice));
|
|
987
|
+
log(chalk.blue("Market Cap: ") + chalk.yellow(info.marketCap));
|
|
988
|
+
log(chalk.blue("Supply: ") + chalk.green(info.totalSupply));
|
|
428
989
|
if (info.graduationProgress !== undefined) {
|
|
429
|
-
log(chalk.
|
|
990
|
+
log(chalk.blue("Graduation: ") +
|
|
430
991
|
chalk.magenta(info.graduationProgress.toFixed(2) + "%"));
|
|
431
992
|
}
|
|
432
993
|
if (info.userPosition && info.userPosition.tokensOwned !== "0") {
|
|
433
|
-
log(chalk.
|
|
994
|
+
log(chalk.blue("Your position: ") +
|
|
434
995
|
chalk.green(info.userPosition.tokensOwned + " tokens"));
|
|
435
996
|
}
|
|
436
997
|
}
|
|
437
998
|
function displayTokenListToLog(result, log, logLines) {
|
|
438
|
-
log(chalk.cyan.bold(`Tokens (${result.tokens.length} total):`));
|
|
439
999
|
log("");
|
|
440
|
-
|
|
441
|
-
|
|
1000
|
+
log(chalk.green.bold(`Tokens (${result.total || result.tokens.length} total):`));
|
|
1001
|
+
if (result.page && result.pages) {
|
|
1002
|
+
log(chalk.dim(`Page ${result.page} of ${result.pages}`));
|
|
442
1003
|
}
|
|
443
|
-
|
|
444
|
-
|
|
1004
|
+
log("");
|
|
1005
|
+
if (result.tokens.length === 0) {
|
|
1006
|
+
log(chalk.yellow("No tokens found."));
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
// Show header
|
|
1010
|
+
log(` ${chalk.cyan.bold("Name".padEnd(20))} ${chalk.cyan.bold("Symbol".padEnd(10))} ${chalk.cyan.bold("Market Cap".padEnd(15))} ${chalk.cyan.bold("Price".padEnd(12))} ${chalk.cyan.bold("Graduation".padEnd(12))} ${chalk.cyan.bold("Status")}`);
|
|
1011
|
+
log(chalk.dim(" " + "─".repeat(90)));
|
|
1012
|
+
// Show tokens
|
|
1013
|
+
for (const token of result.tokens) {
|
|
1014
|
+
const graduationIcon = token.status === "graduated"
|
|
1015
|
+
? "[G]"
|
|
1016
|
+
: token.graduationProgress >= 90
|
|
1017
|
+
? "[H]"
|
|
1018
|
+
: token.graduationProgress >= 50
|
|
1019
|
+
? "[M]"
|
|
1020
|
+
: "[L]";
|
|
1021
|
+
const mcapDisplay = token.status === "graduated"
|
|
1022
|
+
? chalk.dim("--")
|
|
1023
|
+
: formatCurrency(token.mcap || "0");
|
|
1024
|
+
const priceDisplay = token.status === "graduated"
|
|
1025
|
+
? chalk.dim("--")
|
|
1026
|
+
: formatCurrency(token.price || "0");
|
|
1027
|
+
const graduationDisplay = `${graduationIcon} ${(token.graduationProgress || 0).toFixed(1)}%`;
|
|
1028
|
+
const statusDisplay = token.status === "graduated"
|
|
1029
|
+
? chalk.green("✓ Graduated")
|
|
1030
|
+
: chalk.yellow("Active");
|
|
1031
|
+
log(` ${chalk.bold(token.name.padEnd(20))} ${chalk.green(token.symbol.padEnd(10))} ${mcapDisplay.padEnd(15)} ${priceDisplay.padEnd(12)} ${graduationDisplay.padEnd(12)} ${statusDisplay}`);
|
|
1032
|
+
}
|
|
1033
|
+
log("");
|
|
1034
|
+
if (result.page && result.pages && result.page < result.pages) {
|
|
1035
|
+
log(chalk.dim(`Use --page ${result.page + 1} to see more tokens`));
|
|
1036
|
+
log("");
|
|
1037
|
+
}
|
|
1038
|
+
// Show example commands
|
|
1039
|
+
if (result.tokens.length > 0) {
|
|
1040
|
+
const firstToken = result.tokens[0];
|
|
1041
|
+
log(chalk.dim("Example commands:"));
|
|
1042
|
+
log(chalk.dim(` buy ${firstToken.symbol}`));
|
|
1043
|
+
log(chalk.dim(` sell ${firstToken.symbol}`));
|
|
1044
|
+
log(chalk.dim(` info ${firstToken.symbol}`));
|
|
1045
|
+
log("");
|
|
445
1046
|
}
|
|
446
1047
|
}
|
|
447
1048
|
function displayPositionsToLog(result, log, logLines) {
|
|
@@ -449,27 +1050,49 @@ function displayPositionsToLog(result, log, logLines) {
|
|
|
449
1050
|
log(chalk.yellow("No positions found."));
|
|
450
1051
|
return;
|
|
451
1052
|
}
|
|
452
|
-
log(chalk.
|
|
1053
|
+
log(chalk.green.bold(`Your Positions (${result.positions.length}):`));
|
|
453
1054
|
log("");
|
|
454
1055
|
for (const pos of result.positions) {
|
|
455
|
-
log(` ${chalk.
|
|
1056
|
+
log(` ${chalk.green(pos.symbol || "???")} - ${chalk.green(pos.tokensOwned + " tokens")} @ ${chalk.yellow(pos.currentValue || "N/A")}`);
|
|
456
1057
|
}
|
|
457
1058
|
}
|
|
458
1059
|
function displayHealthStatusToLog(health, log, logLines) {
|
|
459
|
-
const statusColor = health.status === "healthy" ? chalk.green : chalk.red;
|
|
1060
|
+
const statusColor = health.status === "ok" || health.status === "healthy" ? chalk.green : chalk.red;
|
|
460
1061
|
log(statusColor(`Agent Status: ${health.status}`));
|
|
461
1062
|
if (health.version) {
|
|
462
|
-
log(chalk.
|
|
1063
|
+
log(chalk.blue("Version: ") + chalk.green(health.version));
|
|
463
1064
|
}
|
|
464
1065
|
if (health.uptime) {
|
|
465
|
-
log(chalk.
|
|
1066
|
+
log(chalk.blue("Uptime: ") + chalk.green(health.uptime));
|
|
466
1067
|
}
|
|
467
1068
|
}
|
|
468
1069
|
function displayBalanceToLog(balance, log, logLines) {
|
|
469
|
-
log(
|
|
470
|
-
log(chalk.
|
|
471
|
-
log(chalk.
|
|
472
|
-
log(chalk.
|
|
1070
|
+
log("");
|
|
1071
|
+
log(chalk.green.bold("Wallet Balance:"));
|
|
1072
|
+
log(chalk.blue("Address: ") + chalk.green(balance.address));
|
|
1073
|
+
log(chalk.blue("ETH: ") + chalk.yellow(balance.ethFormatted || balance.ethBalance));
|
|
1074
|
+
log(chalk.blue("USDC: ") + chalk.green(balance.usdcFormatted || balance.usdcBalance));
|
|
1075
|
+
if (balance.cat402Formatted) {
|
|
1076
|
+
log(chalk.blue("CAT: ") + chalk.cyan(balance.cat402Formatted));
|
|
1077
|
+
}
|
|
1078
|
+
log("");
|
|
1079
|
+
// Show warnings if balances are low
|
|
1080
|
+
const ethNum = Number(balance.ethFormatted || balance.ethBalance);
|
|
1081
|
+
const usdcNum = Number((balance.usdcFormatted || balance.usdcBalance).replace("$", "").replace(",", ""));
|
|
1082
|
+
if (ethNum < 0.001 && usdcNum < 1) {
|
|
1083
|
+
log(chalk.yellow("⚠️ Low balances detected!"));
|
|
1084
|
+
log(chalk.dim(" You need Base Sepolia ETH for gas fees"));
|
|
1085
|
+
log(chalk.dim(" You need Base Sepolia USDC for trading"));
|
|
1086
|
+
log("");
|
|
1087
|
+
}
|
|
1088
|
+
else if (ethNum < 0.001) {
|
|
1089
|
+
log(chalk.yellow("⚠️ Low ETH balance - you may not have enough for gas fees"));
|
|
1090
|
+
log("");
|
|
1091
|
+
}
|
|
1092
|
+
else if (usdcNum < 1) {
|
|
1093
|
+
log(chalk.yellow("⚠️ Low USDC balance - you may not have enough for trades"));
|
|
1094
|
+
log("");
|
|
1095
|
+
}
|
|
473
1096
|
}
|
|
474
1097
|
function extractFlag(args, flag) {
|
|
475
1098
|
const index = args.findIndex((arg) => arg === flag);
|
|
@@ -478,4 +1101,107 @@ function extractFlag(args, flag) {
|
|
|
478
1101
|
}
|
|
479
1102
|
return undefined;
|
|
480
1103
|
}
|
|
1104
|
+
function displayFeesToLog(fees, log, logLines) {
|
|
1105
|
+
const hasFeesToken = BigInt(fees.feeToken || "0") > 0n;
|
|
1106
|
+
const hasFeesPaired = BigInt(fees.feePaired || "0") > 0n;
|
|
1107
|
+
const hasFees = hasFeesToken || hasFeesPaired;
|
|
1108
|
+
log("");
|
|
1109
|
+
log(chalk.green.bold(`💰 Accumulated Fees: ${fees.tokenName} (${fees.tokenSymbol})`));
|
|
1110
|
+
log(chalk.blue("━".repeat(50)));
|
|
1111
|
+
log(chalk.blue("Contract: ") + chalk.green(fees.tokenAddress));
|
|
1112
|
+
log(chalk.blue("LP Status: ") + chalk.green(fees.isLocked ? "🔒 Locked" : "❌ Not Locked"));
|
|
1113
|
+
if (typeof fees.v4TickLower === "number" &&
|
|
1114
|
+
typeof fees.v4TickUpper === "number" &&
|
|
1115
|
+
typeof fees.v4TickCurrent === "number" &&
|
|
1116
|
+
typeof fees.v4InRange === "boolean") {
|
|
1117
|
+
log(chalk.blue("V4 Range: ") +
|
|
1118
|
+
chalk.green(`[${fees.v4TickLower}, ${fees.v4TickUpper}) | Current: ${fees.v4TickCurrent} | In Range: ${fees.v4InRange ? "✅" : "❌"}`));
|
|
1119
|
+
}
|
|
1120
|
+
log("");
|
|
1121
|
+
log(chalk.green("Total Fees:"));
|
|
1122
|
+
log(chalk.blue(" Tokens: ") + chalk.green(hasFeesToken ? fees.feeToken : "0"));
|
|
1123
|
+
log(chalk.blue(" USDC: ") + chalk.green(hasFeesPaired ? fees.feePaired : "$0.00"));
|
|
1124
|
+
log("");
|
|
1125
|
+
log(chalk.green("Creator Share (80%):"));
|
|
1126
|
+
log(chalk.blue(" Tokens: ") + chalk.green(fees.creatorToken || "0"));
|
|
1127
|
+
log(chalk.blue(" USDC: ") + chalk.green(fees.creatorPaired || "$0.00"));
|
|
1128
|
+
log("");
|
|
1129
|
+
log(chalk.green("Platform Share (20%):"));
|
|
1130
|
+
log(chalk.blue(" Tokens: ") + chalk.green(fees.platformToken || "0"));
|
|
1131
|
+
log(chalk.blue(" USDC: ") + chalk.green(fees.platformPaired || "$0.00"));
|
|
1132
|
+
log(chalk.blue("━".repeat(50)));
|
|
1133
|
+
if (!hasFees) {
|
|
1134
|
+
if (fees.v4InRange === false) {
|
|
1135
|
+
log(chalk.yellow("\n⚠️ No fees accrued: position is out of range"));
|
|
1136
|
+
}
|
|
1137
|
+
else {
|
|
1138
|
+
log(chalk.yellow("\n⚠️ No fees accumulated yet. Trade volume needed."));
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
else {
|
|
1142
|
+
log(chalk.blue("\n💡 Run with --execute to claim fees"));
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
function displayClaimResultToLog(result, log, logLines) {
|
|
1146
|
+
log(chalk.green("✅ Fees claimed successfully!"));
|
|
1147
|
+
log(chalk.blue("Token: ") + chalk.green(result.tokenAddress));
|
|
1148
|
+
log(chalk.blue("Transaction: ") + chalk.green(result.txHash));
|
|
1149
|
+
log(chalk.blue("Claimed:"));
|
|
1150
|
+
log(chalk.blue(" Tokens: ") + chalk.green(result.creatorToken || "0"));
|
|
1151
|
+
log(chalk.blue(" USDC: ") + chalk.green(result.creatorPaired || "$0.00"));
|
|
1152
|
+
}
|
|
1153
|
+
function displayTransactionsToLog(result, log, logLines) {
|
|
1154
|
+
log(chalk.green.bold(`📜 Transactions (${result.total} total)`));
|
|
1155
|
+
log("");
|
|
1156
|
+
if (result.transactions.length === 0) {
|
|
1157
|
+
log(chalk.yellow("No transactions found."));
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
for (let i = 0; i < result.transactions.length; i++) {
|
|
1161
|
+
const tx = result.transactions[i];
|
|
1162
|
+
const typeIcon = tx.type === "buy" ? "🟢" : tx.type === "sell" ? "🔴" : "🎁";
|
|
1163
|
+
const statusIcon = tx.status === "success" ? "✅" : tx.status === "pending" ? "⏳" : "❌";
|
|
1164
|
+
log(chalk.green.bold(`${i + 1}. ${typeIcon} ${tx.type.toUpperCase()} ${statusIcon} ${tx.status.toUpperCase()}`));
|
|
1165
|
+
if (tx.token) {
|
|
1166
|
+
log(chalk.blue(` Token: ${tx.token.name} (${tx.token.symbol})`));
|
|
1167
|
+
}
|
|
1168
|
+
log(chalk.blue(` Amount: ${tx.amount}`));
|
|
1169
|
+
log(chalk.blue(` Fee: ${tx.fee}`));
|
|
1170
|
+
if (tx.txHash) {
|
|
1171
|
+
log(chalk.blue(` TX: ${tx.txHash}`));
|
|
1172
|
+
}
|
|
1173
|
+
log(chalk.blue(` Date: ${new Date(tx.createdAt).toLocaleString()}`));
|
|
1174
|
+
log("");
|
|
1175
|
+
}
|
|
1176
|
+
if (result.hasMore) {
|
|
1177
|
+
log(chalk.blue(`Showing ${result.offset + 1}-${result.offset + result.transactions.length} of ${result.total}`));
|
|
1178
|
+
log(chalk.blue(`Use --offset ${result.offset + result.limit} to see more`));
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
function displayAccountInfoToLog(data, log, logLines) {
|
|
1182
|
+
const { account, balance } = data;
|
|
1183
|
+
log("");
|
|
1184
|
+
log(chalk.green.bold("👤 Account Information"));
|
|
1185
|
+
log(chalk.blue("=".repeat(50)));
|
|
1186
|
+
log("");
|
|
1187
|
+
log(chalk.blue("Account Index: ") + chalk.green(account.index.toString()));
|
|
1188
|
+
log(chalk.blue("Type: ") + chalk.green(account.type === "custom" ? "Custom" : "Seed-Derived"));
|
|
1189
|
+
log(chalk.blue("Address: ") + chalk.green(account.address));
|
|
1190
|
+
if (account.label) {
|
|
1191
|
+
log(chalk.blue("Label: ") + chalk.green(account.label));
|
|
1192
|
+
}
|
|
1193
|
+
log("");
|
|
1194
|
+
log(chalk.green.bold("💰 Balances"));
|
|
1195
|
+
log(chalk.blue("ETH: ") + chalk.yellow(balance.ethBalance));
|
|
1196
|
+
log(chalk.blue("USDC: ") + chalk.green(balance.usdcBalance));
|
|
1197
|
+
if (balance.cat402Formatted) {
|
|
1198
|
+
log(chalk.blue("CAT: ") + chalk.green(balance.cat402Formatted));
|
|
1199
|
+
}
|
|
1200
|
+
log("");
|
|
1201
|
+
if (data.positions && data.positions.positions.length > 0) {
|
|
1202
|
+
log(chalk.green.bold("💼 Positions"));
|
|
1203
|
+
log("");
|
|
1204
|
+
displayPositionsToLog(data.positions, log, logLines);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
481
1207
|
//# sourceMappingURL=shell.js.map
|