hedgequantx 1.7.8 → 1.7.9

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 (2) hide show
  1. package/package.json +2 -1
  2. package/src/utils/prompts.js +96 -164
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.7.8",
3
+ "version": "1.7.9",
4
4
  "description": "Prop Futures Algo Trading CLI - Connect to Topstep, Alpha Futures, and other prop firms",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -45,6 +45,7 @@
45
45
  "chalk": "^4.1.2",
46
46
  "commander": "^11.1.0",
47
47
  "figlet": "^1.7.0",
48
+ "inquirer": "^8.2.7",
48
49
  "ora": "^5.4.1",
49
50
  "protobufjs": "^8.0.0",
50
51
  "ws": "^8.18.3"
@@ -1,195 +1,127 @@
1
1
  /**
2
- * Centralized prompts utility - lightweight, no external UI
3
- * Uses readline for simple input, keeps our custom box design
2
+ * Centralized prompts utility
3
+ * Uses inquirer for reliable stdin handling
4
4
  */
5
5
 
6
- const readline = require('readline');
6
+ const inquirer = require('inquirer');
7
7
 
8
8
  /**
9
- * Create readline interface
9
+ * Ensure stdin is ready
10
10
  */
11
- const createRL = () => readline.createInterface({
12
- input: process.stdin,
13
- output: process.stdout
14
- });
11
+ const prepareStdin = () => {
12
+ try {
13
+ if (process.stdin.isPaused()) process.stdin.resume();
14
+ if (process.stdin.isTTY && process.stdin.isRaw) process.stdin.setRawMode(false);
15
+ } catch (e) {}
16
+ };
15
17
 
16
18
  /**
17
- * Wait for Enter key
19
+ * Wait for Enter
18
20
  */
19
- const waitForEnter = (message = 'Press Enter to continue...') => new Promise(resolve => {
20
- const rl = createRL();
21
- rl.question(message, () => { rl.close(); resolve(); });
22
- });
21
+ const waitForEnter = async (message = 'Press Enter to continue...') => {
22
+ prepareStdin();
23
+ await inquirer.prompt([{ type: 'input', name: '_', message, prefix: '' }]);
24
+ };
23
25
 
24
26
  /**
25
- * Simple text input
27
+ * Text input
26
28
  */
27
- const textInput = (message, defaultVal = '') => new Promise(resolve => {
28
- const rl = createRL();
29
- const prompt = defaultVal ? `${message} (${defaultVal}): ` : `${message}: `;
30
- rl.question(prompt, (answer) => {
31
- rl.close();
32
- resolve(answer.trim() || defaultVal);
33
- });
34
- });
29
+ const textInput = async (message, defaultVal = '') => {
30
+ prepareStdin();
31
+ const { value } = await inquirer.prompt([{
32
+ type: 'input',
33
+ name: 'value',
34
+ message,
35
+ default: defaultVal,
36
+ prefix: ''
37
+ }]);
38
+ return value;
39
+ };
35
40
 
36
41
  /**
37
- * Password input (hidden)
42
+ * Password input
38
43
  */
39
- const passwordInput = (message) => new Promise(resolve => {
40
- const rl = createRL();
41
- process.stdout.write(`${message}: `);
42
-
43
- if (process.stdin.isTTY) {
44
- process.stdin.setRawMode(true);
45
- }
46
-
47
- let password = '';
48
- const onData = (char) => {
49
- char = char.toString();
50
-
51
- if (char === '\n' || char === '\r') {
52
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
53
- process.stdin.removeListener('data', onData);
54
- console.log();
55
- rl.close();
56
- resolve(password);
57
- } else if (char === '\u0003') { // Ctrl+C
58
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
59
- process.stdin.removeListener('data', onData);
60
- rl.close();
61
- resolve(null);
62
- } else if (char === '\u007F' || char === '\b') { // Backspace
63
- if (password.length > 0) {
64
- password = password.slice(0, -1);
65
- process.stdout.write('\b \b');
66
- }
67
- } else {
68
- password += char;
69
- process.stdout.write('*');
70
- }
71
- };
72
-
73
- process.stdin.on('data', onData);
74
- process.stdin.resume();
75
- });
44
+ const passwordInput = async (message) => {
45
+ prepareStdin();
46
+ const { value } = await inquirer.prompt([{
47
+ type: 'password',
48
+ name: 'value',
49
+ message,
50
+ mask: '*',
51
+ prefix: ''
52
+ }]);
53
+ return value;
54
+ };
76
55
 
77
56
  /**
78
- * Select from options using arrow keys
57
+ * Confirm Y/n
79
58
  */
80
- const selectOption = (message, options) => new Promise(resolve => {
81
- if (!process.stdin.isTTY) {
82
- // Fallback for non-TTY
83
- const rl = createRL();
84
- console.log(message);
85
- options.forEach((opt, i) => console.log(` ${i + 1}. ${opt.label}`));
86
- rl.question('Enter number: ', (answer) => {
87
- rl.close();
88
- const idx = parseInt(answer) - 1;
89
- resolve(options[idx]?.value || null);
90
- });
91
- return;
92
- }
93
-
94
- let selectedIndex = 0;
95
- const maxIndex = options.length - 1;
96
-
97
- const render = () => {
98
- // Move cursor up and clear lines
99
- if (selectedIndex > 0 || options.length > 1) {
100
- process.stdout.write(`\x1B[${options.length}A`);
101
- }
102
-
103
- options.forEach((opt, i) => {
104
- const prefix = i === selectedIndex ? '› ' : ' ';
105
- const style = i === selectedIndex ? '\x1B[36m' : '\x1B[90m'; // cyan : gray
106
- process.stdout.write(`\x1B[2K${style}${prefix}${opt.label}\x1B[0m\n`);
107
- });
108
- };
109
-
110
- // Initial render
111
- console.log(`\x1B[36m${message}\x1B[0m`);
112
- options.forEach((opt, i) => {
113
- const prefix = i === selectedIndex ? '› ' : ' ';
114
- const style = i === selectedIndex ? '\x1B[36m' : '\x1B[90m';
115
- console.log(`${style}${prefix}${opt.label}\x1B[0m`);
116
- });
117
-
118
- readline.emitKeypressEvents(process.stdin);
119
- process.stdin.setRawMode(true);
120
- process.stdin.resume();
121
-
122
- const onKeypress = (str, key) => {
123
- if (key.name === 'up' || key.name === 'k') {
124
- selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : maxIndex;
125
- render();
126
- } else if (key.name === 'down' || key.name === 'j') {
127
- selectedIndex = selectedIndex < maxIndex ? selectedIndex + 1 : 0;
128
- render();
129
- } else if (key.name === 'return') {
130
- cleanup();
131
- resolve(options[selectedIndex].value);
132
- } else if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {
133
- cleanup();
134
- resolve(null);
135
- } else if (str >= '1' && str <= '9') {
136
- const idx = parseInt(str) - 1;
137
- if (idx <= maxIndex) {
138
- cleanup();
139
- resolve(options[idx].value);
140
- }
141
- }
142
- };
143
-
144
- const cleanup = () => {
145
- process.stdin.removeListener('keypress', onKeypress);
146
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
147
- // Clear the menu display
148
- process.stdout.write(`\x1B[${options.length + 1}A`);
149
- for (let i = 0; i <= options.length; i++) {
150
- process.stdout.write('\x1B[2K\n');
151
- }
152
- process.stdout.write(`\x1B[${options.length + 1}A`);
153
- };
154
-
155
- process.stdin.on('keypress', onKeypress);
156
- });
59
+ const confirmPrompt = async (message, defaultVal = true) => {
60
+ prepareStdin();
61
+ const { value } = await inquirer.prompt([{
62
+ type: 'confirm',
63
+ name: 'value',
64
+ message,
65
+ default: defaultVal,
66
+ prefix: ''
67
+ }]);
68
+ return value;
69
+ };
157
70
 
158
71
  /**
159
- * Confirm yes/no
72
+ * Number input
160
73
  */
161
- const confirmPrompt = (message, defaultVal = true) => new Promise(resolve => {
162
- const rl = createRL();
163
- const hint = defaultVal ? '(Y/n)' : '(y/N)';
164
- rl.question(`${message} ${hint}: `, (answer) => {
165
- rl.close();
166
- const a = answer.toLowerCase().trim();
167
- if (a === '') resolve(defaultVal);
168
- else if (a === 'y' || a === 'yes') resolve(true);
169
- else if (a === 'n' || a === 'no') resolve(false);
170
- else resolve(defaultVal);
171
- });
172
- });
74
+ const numberInput = async (message, defaultVal = 1, min = 1, max = 1000) => {
75
+ prepareStdin();
76
+ const { value } = await inquirer.prompt([{
77
+ type: 'input',
78
+ name: 'value',
79
+ message,
80
+ default: String(defaultVal),
81
+ prefix: '',
82
+ validate: (v) => {
83
+ const n = parseInt(v);
84
+ if (isNaN(n)) return 'Enter a number';
85
+ if (n < min) return `Min: ${min}`;
86
+ if (n > max) return `Max: ${max}`;
87
+ return true;
88
+ }
89
+ }]);
90
+ return parseInt(value) || defaultVal;
91
+ };
173
92
 
174
93
  /**
175
- * Number input
94
+ * Select - just text input, map to value
176
95
  */
177
- const numberInput = (message, defaultVal = 1, min = 1, max = 1000) => new Promise(resolve => {
178
- const rl = createRL();
179
- rl.question(`${message} (${defaultVal}): `, (answer) => {
180
- rl.close();
181
- const n = parseInt(answer) || defaultVal;
182
- if (n < min) resolve(min);
183
- else if (n > max) resolve(max);
184
- else resolve(n);
185
- });
186
- });
96
+ const selectOption = async (message, options) => {
97
+ prepareStdin();
98
+ const { value } = await inquirer.prompt([{
99
+ type: 'input',
100
+ name: 'value',
101
+ message,
102
+ prefix: ''
103
+ }]);
104
+
105
+ const input = (value || '').toLowerCase().trim();
106
+
107
+ // Find by value
108
+ for (const opt of options) {
109
+ if (String(opt.value).toLowerCase() === input) return opt.value;
110
+ }
111
+
112
+ // Find by index (1-based)
113
+ const idx = parseInt(input) - 1;
114
+ if (idx >= 0 && idx < options.length) return options[idx].value;
115
+
116
+ return null;
117
+ };
187
118
 
188
119
  module.exports = {
120
+ prepareStdin,
189
121
  waitForEnter,
190
- selectOption,
191
122
  textInput,
192
123
  passwordInput,
193
124
  confirmPrompt,
194
- numberInput
125
+ numberInput,
126
+ selectOption
195
127
  };