hedgequantx 2.6.74 → 2.6.76

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.6.74",
3
+ "version": "2.6.76",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -858,13 +858,22 @@ const setupBrowserOAuth = async (provider, config) => {
858
858
  } else {
859
859
  console.log(makeLine(chalk.yellow('COULD NOT OPEN BROWSER (VPS/SSH?)')));
860
860
  console.log(makeLine(''));
861
- console.log(makeLine(chalk.white('AFTER LOGGING IN, YOU WILL SEE A CODE')));
862
- console.log(makeLine(chalk.white('COPY THE ENTIRE CODE AND PASTE IT BELOW')));
863
- console.log(makeLine(''));
864
- if (config.codeFormat) {
865
- console.log(makeLine(chalk.white(`THE CODE LOOKS LIKE: ${config.codeFormat}`)));
861
+
862
+ // Different instructions for different providers
863
+ if (provider.id === 'anthropic') {
864
+ console.log(makeLine(chalk.white('AFTER LOGGING IN, YOU WILL SEE A CODE')));
865
+ console.log(makeLine(chalk.white('COPY THE ENTIRE CODE AND PASTE IT BELOW')));
866
+ console.log(makeLine(''));
867
+ console.log(makeLine(chalk.white('THE CODE LOOKS LIKE: abc123...#xyz789...')));
868
+ } else {
869
+ // Gemini, OpenAI, iFlow redirect to localhost - need full URL
870
+ console.log(makeLine(chalk.white('AFTER LOGGING IN, YOU WILL SEE A BLANK PAGE')));
871
+ console.log(makeLine(chalk.white('THE URL WILL START WITH: localhost:...')));
866
872
  console.log(makeLine(''));
873
+ console.log(makeLine(chalk.green('COPY THE ENTIRE URL FROM THE ADDRESS BAR')));
874
+ console.log(makeLine(chalk.white('AND PASTE IT BELOW')));
867
875
  }
876
+ console.log(makeLine(''));
868
877
  console.log(makeLine(chalk.white('TYPE < TO CANCEL')));
869
878
  drawBoxFooter(boxWidth);
870
879
  // Display URL outside the box for easy copy-paste (no line breaks)
@@ -875,24 +884,41 @@ const setupBrowserOAuth = async (provider, config) => {
875
884
  console.log();
876
885
  }
877
886
 
878
- const code = await prompts.textInput(chalk.cyan('PASTE AUTHORIZATION CODE:'));
887
+ const promptText = provider.id === 'anthropic'
888
+ ? 'PASTE AUTHORIZATION CODE:'
889
+ : 'PASTE FULL CALLBACK URL:';
890
+
891
+ const input = await prompts.textInput(chalk.cyan(promptText));
879
892
 
880
- if (!code || code === '<') {
893
+ if (!input || input === '<') {
881
894
  return await selectProviderOption(provider);
882
895
  }
883
896
 
897
+ // Extract code from URL if user pasted full callback URL
898
+ let code = input.trim();
899
+ if (code.includes('code=')) {
900
+ try {
901
+ const urlObj = new URL(code.replace('localhost', 'http://localhost'));
902
+ code = urlObj.searchParams.get('code') || code;
903
+ } catch (e) {
904
+ // Try regex extraction as fallback
905
+ const match = code.match(/[?&]code=([^&]+)/);
906
+ if (match) code = match[1];
907
+ }
908
+ }
909
+
884
910
  // Exchange code for tokens
885
911
  const spinner = ora({ text: 'EXCHANGING CODE FOR TOKENS...', color: 'cyan' }).start();
886
912
 
887
913
  let result;
888
914
  if (provider.id === 'anthropic') {
889
- result = await config.oauthModule.exchange(code.trim(), verifier);
915
+ result = await config.oauthModule.exchange(code, verifier);
890
916
  } else if (provider.id === 'gemini') {
891
- result = await config.oauthModule.exchange(code.trim());
917
+ result = await config.oauthModule.exchange(code);
892
918
  } else if (provider.id === 'iflow') {
893
- result = await config.oauthModule.exchange(code.trim(), redirectUri);
919
+ result = await config.oauthModule.exchange(code, redirectUri);
894
920
  } else {
895
- result = await config.oauthModule.exchange(code.trim(), verifier);
921
+ result = await config.oauthModule.exchange(code, verifier);
896
922
  }
897
923
 
898
924
  if (result.type === 'failed') {
@@ -901,7 +927,7 @@ const setupBrowserOAuth = async (provider, config) => {
901
927
  return await selectProviderOption(provider);
902
928
  }
903
929
 
904
- spinner.succeed('AUTHENTICATION SUCCESSFUL');
930
+ spinner.text = 'FETCHING AVAILABLE MODELS...';
905
931
 
906
932
  // Store OAuth credentials
907
933
  const credentials = {
@@ -919,15 +945,35 @@ const setupBrowserOAuth = async (provider, config) => {
919
945
  credentials.apiKey = result.apiKey;
920
946
  }
921
947
 
922
- // Use default model for OAuth providers (they typically have limited model access)
948
+ // Fetch available models for the provider
949
+ let models = [];
950
+ try {
951
+ const { fetchModelsWithOAuth } = require('../services/ai/client');
952
+ models = await fetchModelsWithOAuth(provider.id, result.access);
953
+ } catch (e) {
954
+ // Fallback to default models if fetch fails
955
+ }
956
+
957
+ // Fallback default models if API doesn't return list
923
958
  const defaultModels = {
924
- anthropic: 'claude-sonnet-4-20250514',
925
- openai: 'gpt-4o',
926
- gemini: 'gemini-2.5-pro',
927
- iflow: 'deepseek-v3'
959
+ anthropic: ['claude-sonnet-4-20250514', 'claude-opus-4-20250514', 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022'],
960
+ openai: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1', 'o1-mini', 'o3-mini'],
961
+ gemini: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.0-flash', 'gemini-1.5-pro'],
962
+ iflow: ['deepseek-v3', 'deepseek-chat', 'kimi', 'glm-4']
928
963
  };
929
964
 
930
- const selectedModel = defaultModels[provider.id] || 'default';
965
+ if (!models || models.length === 0) {
966
+ models = defaultModels[provider.id] || ['default'];
967
+ spinner.warn('USING DEFAULT MODEL LIST');
968
+ } else {
969
+ spinner.succeed(`FOUND ${models.length} MODELS`);
970
+ }
971
+
972
+ // Let user select model
973
+ const selectedModel = await selectModelFromList(models, config.name);
974
+ if (!selectedModel) {
975
+ return await selectProviderOption(provider);
976
+ }
931
977
 
932
978
  // Add agent with OAuth credentials
933
979
  try {
@@ -1040,7 +1086,7 @@ const setupDeviceFlowOAuth = async (provider, config) => {
1040
1086
  return await selectProviderOption(provider);
1041
1087
  }
1042
1088
 
1043
- pollSpinner.succeed('AUTHENTICATION SUCCESSFUL');
1089
+ pollSpinner.text = 'FETCHING AVAILABLE MODELS...';
1044
1090
 
1045
1091
  // Store OAuth credentials
1046
1092
  const credentials = {
@@ -1052,7 +1098,16 @@ const setupDeviceFlowOAuth = async (provider, config) => {
1052
1098
  }
1053
1099
  };
1054
1100
 
1055
- const selectedModel = 'qwen3-coder-plus';
1101
+ // Default models for Qwen
1102
+ const defaultModels = ['qwen3-coder-plus', 'qwen3-235b', 'qwen-max', 'qwen-plus', 'qwen-turbo'];
1103
+
1104
+ pollSpinner.succeed('AUTHENTICATION SUCCESSFUL');
1105
+
1106
+ // Let user select model
1107
+ const selectedModel = await selectModelFromList(defaultModels, config.name);
1108
+ if (!selectedModel) {
1109
+ return await selectProviderOption(provider);
1110
+ }
1056
1111
 
1057
1112
  // Add agent with OAuth credentials
1058
1113
  try {
@@ -565,6 +565,47 @@ const fetchOpenAIModels = async (endpoint, apiKey) => {
565
565
  }
566
566
  };
567
567
 
568
+ /**
569
+ * Fetch available models for OAuth-authenticated providers
570
+ * @param {string} providerId - Provider ID (anthropic, openai, gemini, etc.)
571
+ * @param {string} accessToken - OAuth access token
572
+ * @returns {Promise<Array|null>} Array of model IDs or null on error
573
+ */
574
+ const fetchModelsWithOAuth = async (providerId, accessToken) => {
575
+ if (!accessToken) return null;
576
+
577
+ try {
578
+ switch (providerId) {
579
+ case 'anthropic':
580
+ return await fetchAnthropicModelsOAuth(accessToken);
581
+
582
+ case 'openai':
583
+ // OpenAI OAuth uses the same endpoint as API key
584
+ return await fetchOpenAIModels('https://api.openai.com/v1', accessToken);
585
+
586
+ case 'gemini':
587
+ // Gemini OAuth - try to fetch from API
588
+ const geminiUrl = 'https://generativelanguage.googleapis.com/v1/models';
589
+ const geminiHeaders = {
590
+ 'Authorization': `Bearer ${accessToken}`
591
+ };
592
+ const geminiResponse = await makeRequest(geminiUrl, { method: 'GET', headers: geminiHeaders, timeout: 10000 });
593
+ if (geminiResponse.models && Array.isArray(geminiResponse.models)) {
594
+ return geminiResponse.models
595
+ .filter(m => m.supportedGenerationMethods?.includes('generateContent'))
596
+ .map(m => m.name.replace('models/', ''))
597
+ .filter(Boolean);
598
+ }
599
+ return null;
600
+
601
+ default:
602
+ return null;
603
+ }
604
+ } catch (error) {
605
+ return null;
606
+ }
607
+ };
608
+
568
609
  module.exports = {
569
610
  callAI,
570
611
  analyzeTrading,
@@ -577,5 +618,6 @@ module.exports = {
577
618
  fetchAnthropicModelsOAuth,
578
619
  fetchGeminiModels,
579
620
  fetchOpenAIModels,
621
+ fetchModelsWithOAuth,
580
622
  getValidOAuthToken
581
623
  };