hedgequantx 2.9.20 → 2.9.22

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 (36) hide show
  1. package/package.json +1 -1
  2. package/src/app.js +64 -42
  3. package/src/menus/connect.js +17 -14
  4. package/src/menus/dashboard.js +76 -58
  5. package/src/pages/accounts.js +49 -38
  6. package/src/pages/ai-agents-ui.js +388 -0
  7. package/src/pages/ai-agents.js +494 -0
  8. package/src/pages/ai-models.js +389 -0
  9. package/src/pages/algo/algo-executor.js +307 -0
  10. package/src/pages/algo/copy-executor.js +331 -0
  11. package/src/pages/algo/copy-trading.js +178 -546
  12. package/src/pages/algo/custom-strategy.js +313 -0
  13. package/src/pages/algo/index.js +75 -18
  14. package/src/pages/algo/one-account.js +57 -322
  15. package/src/pages/algo/ui.js +15 -15
  16. package/src/pages/orders.js +22 -19
  17. package/src/pages/positions.js +22 -19
  18. package/src/pages/stats/index.js +16 -15
  19. package/src/pages/user.js +11 -7
  20. package/src/services/ai-supervision/consensus.js +284 -0
  21. package/src/services/ai-supervision/context.js +275 -0
  22. package/src/services/ai-supervision/directive.js +167 -0
  23. package/src/services/ai-supervision/health.js +47 -35
  24. package/src/services/ai-supervision/index.js +359 -0
  25. package/src/services/ai-supervision/parser.js +278 -0
  26. package/src/services/ai-supervision/symbols.js +259 -0
  27. package/src/services/cliproxy/index.js +256 -0
  28. package/src/services/cliproxy/installer.js +111 -0
  29. package/src/services/cliproxy/manager.js +387 -0
  30. package/src/services/index.js +9 -1
  31. package/src/services/llmproxy/index.js +166 -0
  32. package/src/services/llmproxy/manager.js +411 -0
  33. package/src/services/rithmic/accounts.js +6 -8
  34. package/src/ui/box.js +5 -9
  35. package/src/ui/index.js +18 -5
  36. package/src/ui/menu.js +4 -4
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Custom Strategy - AI-powered strategy builder
3
+ * Config flow + AI chat to create strategy, then execute with AI supervision
4
+ */
5
+
6
+ const chalk = require('chalk');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const os = require('os');
10
+ const ora = require('ora');
11
+
12
+ const { getLogoWidth, centerText, displayBanner , clearScreen } = require('../../ui');
13
+ const { prompts } = require('../../utils');
14
+ const { connections } = require('../../services');
15
+ const { getActiveProvider, getActiveAgents } = require('../ai-agents');
16
+ const cliproxy = require('../../services/cliproxy');
17
+ const { runPreflightCheck, formatPreflightResults, getPreflightSummary } = require('../../services/ai-supervision');
18
+ const { checkMarketHours } = require('../../services/rithmic/market');
19
+ const { executeAlgo } = require('./algo-executor');
20
+
21
+ const STRATEGIES_DIR = path.join(os.homedir(), '.hqx', 'strategies');
22
+
23
+ const ensureStrategiesDir = () => {
24
+ if (!fs.existsSync(STRATEGIES_DIR)) fs.mkdirSync(STRATEGIES_DIR, { recursive: true });
25
+ };
26
+
27
+ /** Custom Strategy Menu */
28
+ const customStrategyMenu = async (service) => {
29
+ const aiProvider = getActiveProvider();
30
+ if (!aiProvider) {
31
+ console.log(chalk.red('\n No AI Agent connected. Go to AI Agents menu first.'));
32
+ await prompts.waitForEnter();
33
+ return;
34
+ }
35
+
36
+ const market = checkMarketHours();
37
+ if (!market.isOpen && !market.message.includes('early')) {
38
+ console.log(chalk.red(`\n ${market.message}`));
39
+ console.log(chalk.gray(' Custom strategy requires market to be open\n'));
40
+ await prompts.waitForEnter();
41
+ return;
42
+ }
43
+
44
+ const spinner = ora({ text: 'Fetching active accounts...', color: 'yellow' }).start();
45
+ const allAccounts = await connections.getAllAccounts();
46
+
47
+ if (!allAccounts?.length) { spinner.fail('No accounts found'); await prompts.waitForEnter(); return; }
48
+
49
+ const activeAccounts = allAccounts.filter(acc => acc.status === 0);
50
+ if (!activeAccounts.length) { spinner.fail('No active accounts'); await prompts.waitForEnter(); return; }
51
+
52
+ spinner.succeed(`Found ${activeAccounts.length} active account(s)`);
53
+
54
+ // Step 1: Select account
55
+ console.log(chalk.cyan.bold('\n STEP 1: SELECT ACCOUNT'));
56
+ const accountOptions = activeAccounts.map(acc => {
57
+ const name = acc.accountName || acc.rithmicAccountId || acc.accountId;
58
+ const balance = acc.balance !== null && acc.balance !== undefined ? ` - $${acc.balance.toLocaleString()}` : '';
59
+ return { label: `${name} (${acc.propfirm || acc.platform || 'Unknown'})${balance}`, value: acc };
60
+ });
61
+ accountOptions.push({ label: '< Back', value: 'back' });
62
+
63
+ const selectedAccount = await prompts.selectOption('Select Account:', accountOptions);
64
+ if (!selectedAccount || selectedAccount === 'back') return;
65
+
66
+ const accountService = selectedAccount.service || connections.getServiceForAccount(selectedAccount.accountId) || service;
67
+
68
+ // Step 2: Select symbol
69
+ console.log(chalk.cyan.bold('\n STEP 2: SELECT SYMBOL'));
70
+ const contract = await selectSymbol(accountService);
71
+ if (!contract) return;
72
+
73
+ // Step 3: Configure parameters
74
+ console.log(chalk.cyan.bold('\n STEP 3: CONFIGURE PARAMETERS\n'));
75
+
76
+ const contracts = await prompts.numberInput('Number of contracts:', 1, 1, 10);
77
+ if (contracts === null) return;
78
+
79
+ const dailyTarget = await prompts.numberInput('Daily target ($):', 200, 1, 10000);
80
+ if (dailyTarget === null) return;
81
+
82
+ const maxRisk = await prompts.numberInput('Max risk ($):', 100, 1, 5000);
83
+ if (maxRisk === null) return;
84
+
85
+ const showName = await prompts.confirmPrompt('Show account name?', false);
86
+ if (showName === null) return;
87
+
88
+ // Step 4: AI Supervision with Pre-flight Check
89
+ console.log(chalk.cyan.bold('\n STEP 4: AI SUPERVISION'));
90
+ const aiSupervision = await prompts.confirmPrompt('Enable AI supervision during execution?', true);
91
+ if (aiSupervision === null) return;
92
+
93
+ if (aiSupervision) {
94
+ // Run pre-flight check - agent must pass
95
+ console.log();
96
+ console.log(chalk.yellow(' Running AI pre-flight check...'));
97
+ console.log();
98
+
99
+ const agents = getActiveAgents();
100
+ const preflightResults = await runPreflightCheck(agents);
101
+
102
+ const lines = formatPreflightResults(preflightResults, 60);
103
+ for (const line of lines) console.log(line);
104
+
105
+ const summary = getPreflightSummary(preflightResults);
106
+ console.log();
107
+ console.log(` ${summary.text}`);
108
+ console.log();
109
+
110
+ if (!preflightResults.success) {
111
+ console.log(chalk.red(' Cannot start - fix agent connection first.'));
112
+ await prompts.waitForEnter();
113
+ return;
114
+ }
115
+ }
116
+
117
+ const config = { account: selectedAccount, contract, contracts, dailyTarget, maxRisk, showName, aiSupervision, aiProvider };
118
+
119
+ // Step 5: AI Chat
120
+ console.log(chalk.cyan.bold('\n STEP 5: CREATE YOUR STRATEGY WITH AI'));
121
+ console.log(chalk.gray(' Describe your trading strategy. AI will help build it.\n'));
122
+
123
+ await strategyChat(config, accountService);
124
+ };
125
+
126
+ /** Select symbol */
127
+ const selectSymbol = async (service) => {
128
+ const spinner = ora({ text: 'Loading symbols...', color: 'yellow' }).start();
129
+
130
+ const result = await service.getContracts();
131
+ if (!result.success || !result.contracts?.length) { spinner.fail('Failed to load contracts'); return null; }
132
+
133
+ const popular = ['ES', 'NQ', 'MES', 'MNQ', 'M2K', 'RTY', 'YM', 'MYM', 'NKD', 'GC', 'SI', 'CL'];
134
+ result.contracts.sort((a, b) => {
135
+ const baseA = a.baseSymbol || a.symbol || '', baseB = b.baseSymbol || b.symbol || '';
136
+ const idxA = popular.findIndex(p => baseA === p || baseA.startsWith(p));
137
+ const idxB = popular.findIndex(p => baseB === p || baseB.startsWith(p));
138
+ if (idxA !== -1 && idxB !== -1) return idxA - idxB;
139
+ if (idxA !== -1) return -1;
140
+ if (idxB !== -1) return 1;
141
+ return baseA.localeCompare(baseB);
142
+ });
143
+
144
+ spinner.succeed(`Found ${result.contracts.length} contracts`);
145
+
146
+ const options = result.contracts.map(c => ({ label: `${c.symbol} - ${c.name} (${c.exchange})`, value: c }));
147
+ options.push({ label: chalk.gray('< Back'), value: 'back' });
148
+
149
+ const selected = await prompts.selectOption(chalk.yellow('Select Symbol:'), options);
150
+ return selected === 'back' || selected === null ? null : selected;
151
+ };
152
+
153
+ /** AI Chat for strategy creation */
154
+ const strategyChat = async (config, service) => {
155
+ const { account, contract, contracts, dailyTarget, maxRisk, showName, aiSupervision, aiProvider } = config;
156
+ const accountName = showName ? (account.accountName || account.rithmicAccountId || account.accountId) : 'HQX *****';
157
+
158
+ clearScreen();
159
+ displayBanner();
160
+
161
+ const W = getLogoWidth() - 2;
162
+ console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
163
+ console.log(chalk.cyan('║') + chalk.green.bold(centerText('CUSTOM STRATEGY - AI CHAT', W)) + chalk.cyan('║'));
164
+ console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
165
+ console.log(chalk.cyan('║') + centerText(`Account: ${accountName} | Symbol: ${contract.name} | Qty: ${contracts}`, W) + chalk.cyan('║'));
166
+ console.log(chalk.cyan('║') + centerText(`Target: $${dailyTarget} | Risk: $${maxRisk} | AI: ${aiSupervision ? 'ON' : 'OFF'}`, W) + chalk.cyan('║'));
167
+ console.log(chalk.cyan('║') + chalk.gray(centerText(`Provider: ${aiProvider.name}`, W)) + chalk.cyan('║'));
168
+ console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
169
+ console.log(chalk.cyan('║') + chalk.gray(centerText('"run" to execute, "save" to save, "cancel" to abort', W)) + chalk.cyan('║'));
170
+ console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝\n'));
171
+
172
+ const systemPrompt = `You are an expert algorithmic trading assistant.
173
+ Setup: ${accountName} | ${contract.name} | ${contracts} contracts | Target: $${dailyTarget} | Risk: $${maxRisk}
174
+ Help create a trading strategy. Be concise (2-3 sentences). When ready, say [STRATEGY_READY] with JSON config.`;
175
+
176
+ const messages = [{ role: 'system', content: systemPrompt }];
177
+ let strategyReady = false, strategyConfig = null;
178
+
179
+ console.log(chalk.green(' AI: ') + `I'll help you create a custom strategy for ${contract.name}. What kind of strategy?`);
180
+ console.log();
181
+
182
+ while (true) {
183
+ const userInput = await prompts.textInput(chalk.yellow(' You: '));
184
+ if (!userInput) continue;
185
+
186
+ const cmd = userInput.toLowerCase().trim();
187
+
188
+ if (cmd === 'cancel' || cmd === 'exit' || cmd === 'quit') {
189
+ console.log(chalk.gray('\n Cancelled.')); await prompts.waitForEnter(); return;
190
+ }
191
+
192
+ if (cmd === 'save') {
193
+ if (strategyConfig) await saveStrategy(strategyConfig, config);
194
+ else console.log(chalk.yellow('\n No strategy to save yet.'));
195
+ continue;
196
+ }
197
+
198
+ if (cmd === 'run') {
199
+ if (strategyReady && strategyConfig) {
200
+ console.log(chalk.green('\n Launching strategy...'));
201
+ await launchCustomStrategy(config, strategyConfig, service);
202
+ return;
203
+ }
204
+ console.log(chalk.yellow('\n Strategy not ready. Describe your entry/exit conditions first.'));
205
+ continue;
206
+ }
207
+
208
+ messages.push({ role: 'user', content: userInput });
209
+ const spinner = ora({ text: 'AI thinking...', color: 'yellow' }).start();
210
+
211
+ try {
212
+ const modelId = aiProvider.modelId || getDefaultModel(aiProvider.id);
213
+ const result = await cliproxy.chatCompletion(modelId, messages);
214
+
215
+ if (!result.success) { spinner.fail(`AI Error: ${result.error}`); messages.pop(); continue; }
216
+
217
+ const response = result.response?.choices?.[0]?.message?.content || '';
218
+ messages.push({ role: 'assistant', content: response });
219
+ spinner.stop();
220
+
221
+ console.log('\n' + chalk.green(' AI: ') + formatResponse(response) + '\n');
222
+
223
+ if (response.includes('[STRATEGY_READY]') || response.toLowerCase().includes('strategy is ready')) {
224
+ strategyReady = true;
225
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
226
+ if (jsonMatch) try { strategyConfig = JSON.parse(jsonMatch[0]); } catch (e) {}
227
+ if (!strategyConfig) strategyConfig = { description: userInput, messages: messages.slice(1) };
228
+ console.log(chalk.cyan(' [Strategy ready! "run" to execute or "save" to save]\n'));
229
+ }
230
+ } catch (e) { spinner.fail(`Error: ${e.message}`); messages.pop(); }
231
+ }
232
+ };
233
+
234
+ const getDefaultModel = (id) => ({ anthropic: 'claude-sonnet-4-20250514', google: 'gemini-2.5-pro', openai: 'gpt-4o' }[id] || 'claude-sonnet-4-20250514');
235
+
236
+ const formatResponse = (text) => {
237
+ const lines = text.replace(/\[STRATEGY_READY\]/g, '').trim().split('\n');
238
+ return lines.map((l, i) => i === 0 ? l : ' ' + l).join('\n');
239
+ };
240
+
241
+ /** Save strategy */
242
+ const saveStrategy = async (strategyConfig, config) => {
243
+ ensureStrategiesDir();
244
+ const name = await prompts.textInput(chalk.cyan(' Strategy name: '));
245
+ if (!name?.trim()) { console.log(chalk.gray(' Save cancelled.')); return; }
246
+
247
+ const folderName = name.trim().toLowerCase().replace(/[^a-z0-9]/g, '-');
248
+ const strategyPath = path.join(STRATEGIES_DIR, folderName);
249
+ if (!fs.existsSync(strategyPath)) fs.mkdirSync(strategyPath, { recursive: true });
250
+
251
+ const configFile = {
252
+ name: name.trim(), symbol: config.contract.name, contracts: config.contracts,
253
+ dailyTarget: config.dailyTarget, maxRisk: config.maxRisk, aiSupervision: config.aiSupervision,
254
+ strategy: strategyConfig, createdAt: new Date().toISOString()
255
+ };
256
+
257
+ fs.writeFileSync(path.join(strategyPath, 'config.json'), JSON.stringify(configFile, null, 2));
258
+ console.log(chalk.green(`\n ✓ Saved: ${strategyPath}`));
259
+ };
260
+
261
+ /** Launch custom strategy with AI supervision */
262
+ const launchCustomStrategy = async (config, strategyConfig, service) => {
263
+ const { account, contract, contracts, dailyTarget, maxRisk, showName, aiSupervision, aiProvider } = config;
264
+
265
+ // AI supervision function
266
+ const askAI = async (aiContext, signal, ctx) => {
267
+ if (!aiSupervision) return { approve: true };
268
+
269
+ const prompt = `Trading supervisor check:
270
+ Symbol: ${ctx.symbolName} | Position: ${ctx.currentPosition === 0 ? 'FLAT' : (ctx.currentPosition > 0 ? 'LONG' : 'SHORT')}
271
+ P&L: $${ctx.stats.pnl.toFixed(2)} | Trades: ${ctx.stats.trades} (W:${ctx.stats.wins} L:${ctx.stats.losses})
272
+ Strategy: ${JSON.stringify(strategyConfig.description || strategyConfig).substring(0, 200)}
273
+ Signal: ${signal.direction.toUpperCase()} @ ${signal.entry.toFixed(2)} (${(signal.confidence * 100).toFixed(0)}%)
274
+ Recent prices: ${aiContext.recentTicks.slice(-5).map(t => t.price?.toFixed(2)).join(', ') || 'N/A'}
275
+ Reply JSON: {"approve": true/false, "reason": "brief"}`;
276
+
277
+ try {
278
+ const modelId = aiProvider.modelId || getDefaultModel(aiProvider.id);
279
+ const result = await cliproxy.chatCompletion(modelId, [
280
+ { role: 'system', content: 'Trading supervisor. JSON only.' },
281
+ { role: 'user', content: prompt }
282
+ ]);
283
+
284
+ if (result.success) {
285
+ const content = result.response?.choices?.[0]?.message?.content || '';
286
+ const match = content.match(/\{[\s\S]*\}/);
287
+ if (match) return JSON.parse(match[0]);
288
+ }
289
+ } catch (e) { /* fallback */ }
290
+ return { approve: true, reason: 'AI unavailable' };
291
+ };
292
+
293
+ await executeAlgo({
294
+ service, account, contract,
295
+ config: { contracts, dailyTarget, maxRisk, showName },
296
+ options: { aiSupervision, aiProvider, askAI, subtitle: 'CUSTOM STRATEGY + AI' }
297
+ });
298
+ };
299
+
300
+ /** Load saved strategies */
301
+ const loadStrategies = () => {
302
+ ensureStrategiesDir();
303
+ try {
304
+ const items = fs.readdirSync(STRATEGIES_DIR, { withFileTypes: true });
305
+ return items.filter(i => i.isDirectory()).map(dir => {
306
+ const configPath = path.join(STRATEGIES_DIR, dir.name, 'config.json');
307
+ if (fs.existsSync(configPath)) return { folder: dir.name, ...JSON.parse(fs.readFileSync(configPath, 'utf8')) };
308
+ return { folder: dir.name, name: dir.name };
309
+ });
310
+ } catch (e) { return []; }
311
+ };
312
+
313
+ module.exports = { customStrategyMenu, loadStrategies };
@@ -3,13 +3,15 @@
3
3
  */
4
4
 
5
5
  const chalk = require('chalk');
6
- const { getSeparator } = require('../../ui');
6
+ const { getLogoWidth, centerText, displayBanner , clearScreen } = require('../../ui');
7
7
  const { logger, prompts } = require('../../utils');
8
+ const { getActiveProvider } = require('../ai-agents');
8
9
 
9
10
  const log = logger.scope('AlgoMenu');
10
11
 
11
12
  const { oneAccountMenu } = require('./one-account');
12
13
  const { copyTradingMenu } = require('./copy-trading');
14
+ const { customStrategyMenu } = require('./custom-strategy');
13
15
 
14
16
  /**
15
17
  * Algo Trading Menu
@@ -18,39 +20,94 @@ const algoTradingMenu = async (service) => {
18
20
  log.info('Algo Trading menu opened');
19
21
 
20
22
  try {
21
- console.log();
22
- console.log(chalk.gray(getSeparator()));
23
- console.log(chalk.yellow.bold(' Algo-Trading'));
24
- console.log(chalk.gray(getSeparator()));
25
- console.log();
23
+ // Clear screen and show banner
24
+ clearScreen();
25
+ displayBanner();
26
+
27
+ const boxWidth = getLogoWidth();
28
+ const W = boxWidth - 2;
29
+
30
+ // Check if AI agent is connected
31
+ const aiProvider = getActiveProvider();
32
+ const hasAI = !!aiProvider;
33
+
34
+ // Draw menu rectangle
35
+ console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
36
+ console.log(chalk.cyan('║') + chalk.magenta.bold(centerText('ALGO-TRADING', W)) + chalk.cyan('║'));
37
+ console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
38
+
39
+ // 2 or 3 columns layout based on AI availability
40
+ const col1 = '[1] ONE ACCOUNT';
41
+ const col2 = '[2] COPY TRADING';
42
+ const col3 = hasAI ? '[3] CUSTOM STRATEGY' : '';
43
+
44
+ if (hasAI) {
45
+ // 3 columns
46
+ const colWidth = Math.floor(W / 3);
47
+ const lastColWidth = W - 2 * colWidth;
48
+
49
+ const pad1 = Math.floor((colWidth - col1.length) / 2);
50
+ const pad2 = Math.floor((colWidth - col2.length) / 2);
51
+ const pad3 = Math.floor((lastColWidth - col3.length) / 2);
52
+
53
+ const col1Str = ' '.repeat(pad1) + chalk.cyan(col1) + ' '.repeat(colWidth - col1.length - pad1);
54
+ const col2Str = ' '.repeat(pad2) + chalk.yellow(col2) + ' '.repeat(colWidth - col2.length - pad2);
55
+ const col3Str = ' '.repeat(pad3) + chalk.green(col3) + ' '.repeat(lastColWidth - col3.length - pad3);
56
+
57
+ console.log(chalk.cyan('║') + col1Str + col2Str + col3Str + chalk.cyan('║'));
58
+ } else {
59
+ // 2 columns only (no AI connected)
60
+ const colWidth = Math.floor(W / 2);
61
+ const pad1 = Math.floor((colWidth - col1.length) / 2);
62
+ const pad2 = Math.floor((W - colWidth - col2.length) / 2);
63
+
64
+ const col1Str = ' '.repeat(pad1) + chalk.cyan(col1) + ' '.repeat(colWidth - col1.length - pad1);
65
+ const col2Str = ' '.repeat(pad2) + chalk.yellow(col2) + ' '.repeat(W - colWidth - col2.length - pad2);
66
+
67
+ console.log(chalk.cyan('║') + col1Str + col2Str + chalk.cyan('║'));
68
+ }
69
+
70
+ console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
71
+ console.log(chalk.cyan('║') + chalk.red(centerText('[B] BACK', W)) + chalk.cyan('║'));
72
+ console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
26
73
 
27
- const action = await prompts.selectOption(chalk.yellow('Select Mode:'), [
28
- { value: 'one_account', label: 'One Account' },
29
- { value: 'copy_trading', label: 'Copy Trading' },
30
- { value: 'back', label: '< Back' }
31
- ]);
74
+ const promptText = hasAI ? 'SELECT (1/2/3/B): ' : 'SELECT (1/2/B): ';
75
+ const input = await prompts.textInput(chalk.cyan(promptText));
76
+ const choice = (input || '').toLowerCase().trim();
32
77
 
33
- log.debug('Algo mode selected', { action });
78
+ log.debug('Algo mode selected', { choice });
34
79
 
35
- if (!action || action === 'back') {
80
+ if (choice === 'b' || choice === '') {
36
81
  return 'back';
37
82
  }
38
83
 
39
- switch (action) {
40
- case 'one_account':
84
+ switch (choice) {
85
+ case '1':
41
86
  log.info('Starting One Account mode');
42
87
  await oneAccountMenu(service);
43
88
  break;
44
- case 'copy_trading':
89
+ case '2':
45
90
  log.info('Starting Copy Trading mode');
46
91
  await copyTradingMenu();
47
92
  break;
93
+ case '3':
94
+ if (hasAI) {
95
+ log.info('Starting Custom Strategy mode');
96
+ await customStrategyMenu(service);
97
+ } else {
98
+ console.log(chalk.red(' INVALID OPTION'));
99
+ await new Promise(r => setTimeout(r, 1000));
100
+ }
101
+ break;
102
+ default:
103
+ console.log(chalk.red(' INVALID OPTION'));
104
+ await new Promise(r => setTimeout(r, 1000));
48
105
  }
49
106
 
50
- return action;
107
+ return choice;
51
108
  } catch (err) {
52
109
  log.error('Algo menu error:', err.message);
53
- console.log(chalk.red(` Error: ${err.message}`));
110
+ console.log(chalk.red(` ERROR: ${err.message.toUpperCase()}`));
54
111
  await prompts.waitForEnter();
55
112
  return 'back';
56
113
  }