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 +1 -1
- package/src/app.js +1 -22
- package/src/menus/connect.js +2 -1
- package/src/menus/dashboard.js +135 -31
- package/src/ui/index.js +23 -1
package/package.json
CHANGED
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); });
|
package/src/menus/connect.js
CHANGED
|
@@ -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}...`));
|
package/src/menus/dashboard.js
CHANGED
|
@@ -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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
162
|
+
prepareStdin();
|
|
163
|
+
|
|
164
|
+
let spinner = null;
|
|
165
|
+
let currentVersion = 'unknown';
|
|
166
|
+
let latestVersion = null;
|
|
149
167
|
|
|
150
168
|
try {
|
|
151
|
-
//
|
|
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
|
-
|
|
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
|
|
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(`
|
|
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
|
|
229
|
+
await waitForEnter();
|
|
169
230
|
return;
|
|
170
231
|
}
|
|
171
232
|
|
|
172
233
|
// Update via npm
|
|
173
|
-
spinner
|
|
234
|
+
spinner = ora(`Updating v${currentVersion} -> v${latestVersion}...`).start();
|
|
235
|
+
|
|
174
236
|
try {
|
|
175
|
-
execSync('npm install -g hedgequantx@latest', {
|
|
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
|
|
178
|
-
console.log(chalk.gray(` Error: ${e.message}`));
|
|
243
|
+
spinner.fail('Update failed');
|
|
179
244
|
console.log();
|
|
180
|
-
|
|
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('
|
|
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.
|
|
258
|
+
console.log(chalk.green(` Updated: v${currentVersion} -> v${latestVersion}`));
|
|
189
259
|
console.log();
|
|
190
260
|
|
|
191
|
-
//
|
|
192
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
|
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
|
};
|