hedgequantx 1.2.147 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.2.147",
3
+ "version": "1.3.0",
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": {
package/src/app.js CHANGED
@@ -8,7 +8,7 @@ const inquirer = require('inquirer');
8
8
  const ora = require('ora');
9
9
 
10
10
  const { connections } = require('./services');
11
- const { getLogoWidth, centerText } = require('./ui');
11
+ const { getLogoWidth, centerText, prepareStdin } = require('./ui');
12
12
 
13
13
  // Pages
14
14
  const { showStats } = require('./pages/stats');
@@ -42,27 +42,6 @@ const restoreTerminal = () => {
42
42
  }
43
43
  };
44
44
 
45
- /**
46
- * Ensure stdin is ready for inquirer prompts
47
- * This fixes input leaking to bash after session restore
48
- */
49
- const prepareStdin = () => {
50
- try {
51
- // Remove any raw mode that might be left from previous operations
52
- if (process.stdin.isTTY && process.stdin.isRaw) {
53
- process.stdin.setRawMode(false);
54
- }
55
- // Remove any lingering keypress listeners
56
- process.stdin.removeAllListeners('keypress');
57
- process.stdin.removeAllListeners('data');
58
- // Pause stdin so inquirer can take control
59
- process.stdin.pause();
60
- // Small delay to let event loop settle
61
- } catch (e) {
62
- // Ignore errors
63
- }
64
- };
65
-
66
45
  // Register global handlers to restore terminal on exit/crash
67
46
  process.on('exit', restoreTerminal);
68
47
  process.on('SIGINT', () => { restoreTerminal(); process.exit(0); });
@@ -11,7 +11,7 @@ const { ProjectXService, connections } = require('../services');
11
11
  const { RithmicService } = require('../services/rithmic');
12
12
  const { TradovateService } = require('../services/tradovate');
13
13
  const { getPropFirmsByPlatform } = require('../config');
14
- const { getDevice, getLogoWidth, centerText } = require('../ui');
14
+ const { getDevice, getLogoWidth, centerText, prepareStdin } = require('../ui');
15
15
  const { validateUsername, validatePassword } = require('../security');
16
16
 
17
17
  /**
@@ -20,6 +20,7 @@ const { validateUsername, validatePassword } = require('../security');
20
20
  * @returns {Promise<{username: string, password: string}>}
21
21
  */
22
22
  const loginPrompt = async (propfirmName) => {
23
+ prepareStdin();
23
24
  const device = getDevice();
24
25
  console.log();
25
26
  console.log(chalk.cyan(`Connecting to ${propfirmName}...`));
@@ -9,13 +9,16 @@ const ora = require('ora');
9
9
  const { execSync, spawn } = require('child_process');
10
10
 
11
11
  const { connections } = require('../services');
12
- const { getLogoWidth, centerText } = require('../ui');
12
+ const { getLogoWidth, centerText, prepareStdin } = require('../ui');
13
13
 
14
14
  /**
15
15
  * Dashboard menu after login
16
16
  * @param {Object} service - Connected service
17
17
  */
18
18
  const dashboardMenu = async (service) => {
19
+ // Ensure stdin is ready for prompts
20
+ prepareStdin();
21
+
19
22
  const user = service.user;
20
23
  const boxWidth = getLogoWidth();
21
24
  const W = boxWidth - 2; // Same width as logo (inner width)
@@ -139,72 +142,173 @@ const dashboardMenu = async (service) => {
139
142
  return actionMap[action] || 'accounts';
140
143
  };
141
144
 
145
+ /**
146
+ * Wait for user to press Enter
147
+ */
148
+ const waitForEnter = async () => {
149
+ prepareStdin();
150
+ try {
151
+ await inquirer.prompt([{ type: 'input', name: 'c', message: 'Press Enter to continue...' }]);
152
+ } catch (e) {
153
+ // Ignore prompt errors
154
+ }
155
+ };
156
+
142
157
  /**
143
158
  * Handles the update process with auto-restart
159
+ * Robust version that handles all edge cases
144
160
  */
145
161
  const handleUpdate = async () => {
146
- const pkg = require('../../package.json');
147
- const currentVersion = pkg.version;
148
- const spinner = ora('Checking for updates...').start();
162
+ prepareStdin();
163
+
164
+ let spinner = null;
165
+ let currentVersion = 'unknown';
166
+ let latestVersion = null;
149
167
 
150
168
  try {
151
- // Check latest version on npm
169
+ // Get current version safely
170
+ try {
171
+ const pkg = require('../../package.json');
172
+ currentVersion = pkg.version || 'unknown';
173
+ } catch (e) {
174
+ currentVersion = 'unknown';
175
+ }
176
+
177
+ spinner = ora('Checking for updates...').start();
178
+
179
+ // Check latest version on npm with timeout
152
180
  spinner.text = 'Checking npm registry...';
153
- let latestVersion;
154
181
  try {
155
- latestVersion = execSync('npm view hedgequantx version', { stdio: 'pipe' }).toString().trim();
182
+ const result = execSync('npm view hedgequantx version 2>/dev/null', {
183
+ stdio: 'pipe',
184
+ timeout: 15000, // 15 second timeout
185
+ encoding: 'utf8'
186
+ });
187
+ latestVersion = (result || '').toString().trim();
188
+
189
+ // Validate version format (x.y.z)
190
+ if (!latestVersion || !/^\d+\.\d+\.\d+/.test(latestVersion)) {
191
+ throw new Error('Invalid version format received');
192
+ }
156
193
  } catch (e) {
157
194
  spinner.fail('Cannot reach npm registry');
195
+ console.log(chalk.gray(' Check your internet connection'));
158
196
  console.log();
159
- await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
197
+ await waitForEnter();
160
198
  return;
161
199
  }
162
200
 
201
+ // Compare versions
163
202
  if (currentVersion === latestVersion) {
164
203
  spinner.succeed('Already up to date!');
165
204
  console.log();
166
- console.log(chalk.green(` You have the latest version of HedgeQuantX CLI: v${currentVersion}`));
205
+ console.log(chalk.green(` You have the latest version: v${currentVersion}`));
206
+ console.log();
207
+ await waitForEnter();
208
+ return;
209
+ }
210
+
211
+ // Ask user before updating
212
+ spinner.stop();
213
+ console.log();
214
+ console.log(chalk.cyan(` Current version: v${currentVersion}`));
215
+ console.log(chalk.green(` Latest version: v${latestVersion}`));
216
+ console.log();
217
+
218
+ prepareStdin();
219
+ const { confirm } = await inquirer.prompt([{
220
+ type: 'confirm',
221
+ name: 'confirm',
222
+ message: 'Do you want to update now?',
223
+ default: true
224
+ }]);
225
+
226
+ if (!confirm) {
227
+ console.log(chalk.gray(' Update cancelled'));
167
228
  console.log();
168
- await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
229
+ await waitForEnter();
169
230
  return;
170
231
  }
171
232
 
172
233
  // Update via npm
173
- spinner.text = `Updating v${currentVersion} -> v${latestVersion}...`;
234
+ spinner = ora(`Updating v${currentVersion} -> v${latestVersion}...`).start();
235
+
174
236
  try {
175
- execSync('npm install -g hedgequantx@latest', { stdio: 'pipe' });
237
+ execSync('npm install -g hedgequantx@latest 2>/dev/null', {
238
+ stdio: 'pipe',
239
+ timeout: 120000, // 2 minute timeout for install
240
+ encoding: 'utf8'
241
+ });
176
242
  } catch (e) {
177
- spinner.fail('Update failed - try manually: npm install -g hedgequantx@latest');
178
- console.log(chalk.gray(` Error: ${e.message}`));
243
+ spinner.fail('Update failed');
179
244
  console.log();
180
- await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
245
+ console.log(chalk.yellow(' Try manually:'));
246
+ console.log(chalk.white(' npm install -g hedgequantx@latest'));
247
+ console.log();
248
+ if (e.message) {
249
+ console.log(chalk.gray(` Error: ${e.message.substring(0, 100)}`));
250
+ console.log();
251
+ }
252
+ await waitForEnter();
181
253
  return;
182
254
  }
183
255
 
184
- spinner.succeed('CLI updated!');
185
- console.log();
186
- console.log(chalk.green(` ✓ Updated: v${currentVersion} -> v${latestVersion}`));
256
+ spinner.succeed('Update complete!');
187
257
  console.log();
188
- console.log(chalk.cyan(' Restarting HedgeQuantX CLI...'));
258
+ console.log(chalk.green(` Updated: v${currentVersion} -> v${latestVersion}`));
189
259
  console.log();
190
260
 
191
- // Small delay so user can see the message
192
- await new Promise(resolve => setTimeout(resolve, 1500));
261
+ // Ask if user wants to restart
262
+ prepareStdin();
263
+ const { restart } = await inquirer.prompt([{
264
+ type: 'confirm',
265
+ name: 'restart',
266
+ message: 'Restart HQX now?',
267
+ default: true
268
+ }]);
193
269
 
194
- // Restart the CLI automatically
195
- const child = spawn('hedgequantx', [], {
196
- stdio: 'inherit',
197
- detached: true,
198
- shell: true
199
- });
200
- child.unref();
201
- process.exit(0);
270
+ if (restart) {
271
+ console.log();
272
+ console.log(chalk.cyan(' Restarting HedgeQuantX CLI...'));
273
+ console.log();
274
+
275
+ // Small delay so user can see the message
276
+ await new Promise(resolve => setTimeout(resolve, 1000));
277
+
278
+ // Restart the CLI
279
+ try {
280
+ const child = spawn('hedgequantx', [], {
281
+ stdio: 'inherit',
282
+ detached: true,
283
+ shell: true
284
+ });
285
+ child.unref();
286
+ process.exit(0);
287
+ } catch (e) {
288
+ console.log(chalk.yellow(' Could not auto-restart. Please run: hedgequantx'));
289
+ console.log();
290
+ await waitForEnter();
291
+ }
292
+ } else {
293
+ console.log(chalk.gray(' Run "hedgequantx" to use the new version'));
294
+ console.log();
295
+ await waitForEnter();
296
+ }
202
297
 
203
298
  } catch (error) {
204
- spinner.fail('Update failed: ' + error.message);
299
+ // Catch-all for any unexpected errors
300
+ if (spinner) {
301
+ try { spinner.fail('Update error'); } catch (e) {}
302
+ }
303
+ console.log();
304
+ console.log(chalk.red(' An error occurred during update'));
305
+ if (error && error.message) {
306
+ console.log(chalk.gray(` ${error.message.substring(0, 100)}`));
307
+ }
308
+ console.log();
205
309
  console.log(chalk.yellow(' Try manually: npm install -g hedgequantx@latest'));
206
310
  console.log();
207
- await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
311
+ await waitForEnter();
208
312
  }
209
313
  };
210
314
 
package/src/ui/index.js CHANGED
@@ -24,6 +24,26 @@ const {
24
24
  } = require('./table');
25
25
  const { createBoxMenu } = require('./menu');
26
26
 
27
+ /**
28
+ * Ensure stdin is ready for inquirer prompts
29
+ * This fixes input leaking to bash after session restore or algo trading
30
+ */
31
+ const prepareStdin = () => {
32
+ try {
33
+ // Remove any raw mode that might be left from previous operations
34
+ if (process.stdin.isTTY && process.stdin.isRaw) {
35
+ process.stdin.setRawMode(false);
36
+ }
37
+ // Remove any lingering keypress listeners
38
+ process.stdin.removeAllListeners('keypress');
39
+ process.stdin.removeAllListeners('data');
40
+ // Pause stdin so inquirer can take control
41
+ process.stdin.pause();
42
+ } catch (e) {
43
+ // Ignore errors
44
+ }
45
+ };
46
+
27
47
  module.exports = {
28
48
  // Device
29
49
  detectDevice,
@@ -47,5 +67,7 @@ module.exports = {
47
67
  draw2ColSeparator,
48
68
  fmtRow,
49
69
  // Menu
50
- createBoxMenu
70
+ createBoxMenu,
71
+ // Stdin
72
+ prepareStdin
51
73
  };