hedgequantx 2.7.27 → 2.7.29

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": "2.7.27",
3
+ "version": "2.7.29",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
package/src/app.js CHANGED
@@ -161,19 +161,21 @@ const getMobileLogo = () => [
161
161
  const run = async () => {
162
162
  try {
163
163
  log.info('Starting HQX CLI');
164
- await bannerClosed();
165
-
166
- // Restore session
167
- const spinner = ora({ text: 'Restoring session...', color: 'yellow' }).start();
164
+
165
+ // First launch - show banner with loading spinner
166
+ await banner();
167
+ const boxWidth = getLogoWidth();
168
+ const innerWidth = boxWidth - 2;
169
+ console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
170
+
171
+ const spinner = ora({ text: ' Loading...', color: 'yellow' }).start();
168
172
  const restored = await connections.restoreFromStorage();
169
173
 
170
174
  if (restored) {
171
- spinner.succeed('Session restored');
172
175
  currentService = connections.getAll()[0].service;
173
176
  await refreshStats();
174
- } else {
175
- spinner.info('No active session');
176
177
  }
178
+ spinner.stop();
177
179
 
178
180
  // Main loop
179
181
  while (true) {
@@ -11,12 +11,18 @@ const path = require('path');
11
11
  const fs = require('fs');
12
12
  const ora = require('ora');
13
13
 
14
- const { getLogoWidth } = require('../ui');
14
+ const { getLogoWidth, displayBanner } = require('../ui');
15
15
  const { prompts } = require('../utils');
16
16
  const { fetchModelsFromApi } = require('./ai-models');
17
17
  const { drawProvidersTable, drawModelsTable, drawProviderWindow } = require('./ai-agents-ui');
18
18
  const cliproxy = require('../services/cliproxy');
19
19
 
20
+ /** Clear screen and show banner */
21
+ const clearWithBanner = () => {
22
+ console.clear();
23
+ displayBanner();
24
+ };
25
+
20
26
  // Config file path
21
27
  const CONFIG_DIR = path.join(os.homedir(), '.hqx');
22
28
  const CONFIG_FILE = path.join(CONFIG_DIR, 'ai-config.json');
@@ -57,7 +63,7 @@ const saveConfig = (config) => {
57
63
  /** Select a model from a pre-fetched list */
58
64
  const selectModelFromList = async (provider, models, boxWidth) => {
59
65
  while (true) {
60
- console.clear();
66
+ clearWithBanner();
61
67
  drawModelsTable(provider, models, boxWidth);
62
68
 
63
69
  const input = await prompts.textInput(chalk.cyan('Select model: '));
@@ -216,7 +222,7 @@ const handleCliProxyConnection = async (provider, config, boxWidth) => {
216
222
 
217
223
  /** Handle API Key connection */
218
224
  const handleApiKeyConnection = async (provider, config) => {
219
- console.clear();
225
+ clearWithBanner();
220
226
  console.log(chalk.yellow(`\n Enter your ${provider.name} API key:`));
221
227
  console.log(chalk.gray(' (Press Enter to cancel)\n'));
222
228
 
@@ -259,7 +265,7 @@ const handleProviderConfig = async (provider, config) => {
259
265
  const boxWidth = getLogoWidth();
260
266
 
261
267
  while (true) {
262
- console.clear();
268
+ clearWithBanner();
263
269
  drawProviderWindow(provider, config, boxWidth);
264
270
 
265
271
  const input = await prompts.textInput(chalk.cyan('Select option: '));
@@ -313,7 +319,7 @@ const getActiveAgentCount = () => getActiveProvider() ? 1 : 0;
313
319
 
314
320
  /** Show CLIProxy status */
315
321
  const showCliProxyStatus = async () => {
316
- console.clear();
322
+ clearWithBanner();
317
323
  console.log(chalk.yellow('\n CLIProxyAPI Status\n'));
318
324
 
319
325
  const installed = cliproxy.isInstalled();
@@ -337,7 +343,7 @@ const aiAgentsMenu = async () => {
337
343
  const boxWidth = getLogoWidth();
338
344
 
339
345
  while (true) {
340
- console.clear();
346
+ clearWithBanner();
341
347
  const status = await cliproxy.isRunning();
342
348
  const statusText = status.running ? `localhost:${cliproxy.DEFAULT_PORT}` : 'Not running';
343
349
  drawProvidersTable(AI_PROVIDERS, config, boxWidth, statusText);
@@ -388,48 +388,64 @@ const ensureRunning = async (onProgress = null) => {
388
388
  /**
389
389
  * Get OAuth login URL for a provider
390
390
  * @param {string} provider - Provider ID (anthropic, openai, google, etc.)
391
- * @returns {Promise<Object>} { success, url, error }
391
+ * @returns {Promise<Object>} { success, url, childProcess, error }
392
392
  */
393
393
  const getLoginUrl = async (provider) => {
394
394
  const providerFlags = {
395
- anthropic: '--claude-login',
396
- openai: '--codex-login',
397
- google: '--gemini-login',
398
- qwen: '--qwen-login'
395
+ anthropic: '-claude-login',
396
+ openai: '-codex-login',
397
+ google: '-gemini-login',
398
+ qwen: '-qwen-login'
399
399
  };
400
400
 
401
401
  const flag = providerFlags[provider];
402
402
  if (!flag) {
403
- return { success: false, url: null, error: 'Provider not supported for OAuth' };
403
+ return { success: false, url: null, childProcess: null, error: 'Provider not supported for OAuth' };
404
404
  }
405
405
 
406
- // For headless/VPS, use --no-browser flag
406
+ // For headless/VPS, use -no-browser flag
407
407
  return new Promise((resolve) => {
408
- const args = [flag, '--no-browser'];
408
+ const args = [flag, '-no-browser'];
409
409
  const child = spawn(BINARY_PATH, args, {
410
- cwd: INSTALL_DIR,
411
- env: { ...process.env, AUTH_DIR: AUTH_DIR }
410
+ cwd: INSTALL_DIR
412
411
  });
413
412
 
414
413
  let output = '';
414
+ let resolved = false;
415
+
416
+ const checkForUrl = () => {
417
+ if (resolved) return;
418
+ const urlMatch = output.match(/https?:\/\/[^\s]+/);
419
+ if (urlMatch) {
420
+ resolved = true;
421
+ // Return child process so caller can wait for auth completion
422
+ resolve({ success: true, url: urlMatch[0], childProcess: child, error: null });
423
+ }
424
+ };
415
425
 
416
426
  child.stdout.on('data', (data) => {
417
427
  output += data.toString();
428
+ checkForUrl();
418
429
  });
419
430
 
420
431
  child.stderr.on('data', (data) => {
421
432
  output += data.toString();
433
+ checkForUrl();
422
434
  });
423
435
 
424
- // Look for URL in output
425
- setTimeout(() => {
426
- const urlMatch = output.match(/https?:\/\/[^\s]+/);
427
- if (urlMatch) {
428
- resolve({ success: true, url: urlMatch[0], error: null });
429
- } else {
430
- resolve({ success: false, url: null, error: 'Could not get login URL' });
436
+ child.on('error', (err) => {
437
+ if (!resolved) {
438
+ resolved = true;
439
+ resolve({ success: false, url: null, childProcess: null, error: err.message });
431
440
  }
432
- }, 3000);
441
+ });
442
+
443
+ child.on('close', (code) => {
444
+ if (!resolved) {
445
+ resolved = true;
446
+ resolve({ success: false, url: null, childProcess: null, error: `Process exited with code ${code}` });
447
+ }
448
+ });
433
449
  });
434
450
  };
435
451