hedgequantx 2.6.101 → 2.6.102
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/menus/ai-agent.js +164 -7
- package/src/services/ai/client.js +5 -8
package/package.json
CHANGED
package/src/menus/ai-agent.js
CHANGED
|
@@ -782,7 +782,7 @@ const getOAuthConfig = (providerId) => {
|
|
|
782
782
|
|
|
783
783
|
/**
|
|
784
784
|
* Setup OAuth connection for any provider with OAuth support
|
|
785
|
-
*
|
|
785
|
+
* Uses CLIProxyAPI for proper OAuth handling and API access
|
|
786
786
|
*/
|
|
787
787
|
const setupOAuthConnection = async (provider) => {
|
|
788
788
|
const config = getOAuthConfig(provider.id);
|
|
@@ -792,8 +792,8 @@ const setupOAuthConnection = async (provider) => {
|
|
|
792
792
|
return await selectProviderOption(provider);
|
|
793
793
|
}
|
|
794
794
|
|
|
795
|
-
// Use
|
|
796
|
-
return await
|
|
795
|
+
// Use CLIProxyAPI for OAuth flow - it handles token exchange and API calls
|
|
796
|
+
return await setupProxyOAuth(provider, config);
|
|
797
797
|
};
|
|
798
798
|
|
|
799
799
|
/**
|
|
@@ -948,12 +948,169 @@ const setupRemoteOAuth = async (provider, config) => {
|
|
|
948
948
|
// NOTE: promptForModelName was removed - models MUST come from API (RULES.md)
|
|
949
949
|
|
|
950
950
|
/**
|
|
951
|
-
* Setup OAuth
|
|
952
|
-
*
|
|
951
|
+
* Setup OAuth using CLIProxyAPI
|
|
952
|
+
* CLIProxyAPI handles OAuth flow, token storage, and API calls
|
|
953
|
+
* Models are fetched from CLIProxyAPI /v1/models endpoint
|
|
953
954
|
*/
|
|
954
955
|
const setupProxyOAuth = async (provider, config) => {
|
|
955
|
-
|
|
956
|
-
|
|
956
|
+
const boxWidth = getLogoWidth();
|
|
957
|
+
const W = boxWidth - 2;
|
|
958
|
+
|
|
959
|
+
const makeLine = (content) => {
|
|
960
|
+
const plainLen = content.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
961
|
+
const padding = W - plainLen;
|
|
962
|
+
return chalk.cyan('║') + ' ' + content + ' '.repeat(Math.max(0, padding - 1)) + chalk.cyan('║');
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
// Step 1: Ensure CLIProxyAPI is installed and running
|
|
966
|
+
const spinner = ora({ text: 'Setting up CLIProxyAPI...', color: 'cyan' }).start();
|
|
967
|
+
|
|
968
|
+
try {
|
|
969
|
+
await proxyManager.ensureRunning();
|
|
970
|
+
spinner.succeed('CLIProxyAPI ready');
|
|
971
|
+
} catch (error) {
|
|
972
|
+
spinner.fail(`Failed to start CLIProxyAPI: ${error.message}`);
|
|
973
|
+
console.log();
|
|
974
|
+
console.log(chalk.yellow(' CLIProxyAPI is required for OAuth authentication.'));
|
|
975
|
+
console.log(chalk.gray(' It will be downloaded automatically on first use.'));
|
|
976
|
+
console.log();
|
|
977
|
+
await prompts.waitForEnter();
|
|
978
|
+
return await selectProviderOption(provider);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// Step 2: Get OAuth URL from CLIProxyAPI
|
|
982
|
+
spinner.text = 'Getting authorization URL...';
|
|
983
|
+
spinner.start();
|
|
984
|
+
|
|
985
|
+
let authUrl, authState;
|
|
986
|
+
try {
|
|
987
|
+
const authInfo = await proxyManager.getAuthUrl(provider.id);
|
|
988
|
+
authUrl = authInfo.url;
|
|
989
|
+
authState = authInfo.state;
|
|
990
|
+
spinner.succeed('Authorization URL ready');
|
|
991
|
+
} catch (error) {
|
|
992
|
+
spinner.fail(`Failed to get auth URL: ${error.message}`);
|
|
993
|
+
await prompts.waitForEnter();
|
|
994
|
+
return await selectProviderOption(provider);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// Step 3: Show instructions to user
|
|
998
|
+
console.clear();
|
|
999
|
+
displayBanner();
|
|
1000
|
+
drawBoxHeaderContinue(`CONNECT ${config.name}`, boxWidth);
|
|
1001
|
+
|
|
1002
|
+
console.log(makeLine(chalk.yellow('CONNECT YOUR ACCOUNT')));
|
|
1003
|
+
console.log(makeLine(''));
|
|
1004
|
+
console.log(makeLine(chalk.white('1. OPEN THE LINK BELOW IN YOUR BROWSER')));
|
|
1005
|
+
console.log(makeLine(''));
|
|
1006
|
+
console.log(makeLine(chalk.white(`2. LOGIN WITH YOUR ${config.accountName.toUpperCase()} ACCOUNT`)));
|
|
1007
|
+
console.log(makeLine(''));
|
|
1008
|
+
console.log(makeLine(chalk.white('3. CLICK "AUTHORIZE"')));
|
|
1009
|
+
console.log(makeLine(''));
|
|
1010
|
+
console.log(makeLine(chalk.green('4. WAIT FOR CONFIRMATION HERE')));
|
|
1011
|
+
console.log(makeLine(chalk.white(' (The page will close automatically)')));
|
|
1012
|
+
console.log(makeLine(''));
|
|
1013
|
+
|
|
1014
|
+
drawBoxFooter(boxWidth);
|
|
1015
|
+
|
|
1016
|
+
// Display URL
|
|
1017
|
+
console.log();
|
|
1018
|
+
console.log(chalk.yellow(' OPEN THIS URL IN YOUR BROWSER:'));
|
|
1019
|
+
console.log();
|
|
1020
|
+
console.log(chalk.cyan(` ${authUrl}`));
|
|
1021
|
+
console.log();
|
|
1022
|
+
|
|
1023
|
+
// Try to open browser automatically
|
|
1024
|
+
const browserOpened = await openBrowser(authUrl);
|
|
1025
|
+
if (browserOpened) {
|
|
1026
|
+
console.log(chalk.gray(' (Browser opened automatically)'));
|
|
1027
|
+
} else {
|
|
1028
|
+
console.log(chalk.gray(' (Copy and paste the URL in your browser)'));
|
|
1029
|
+
}
|
|
1030
|
+
console.log();
|
|
1031
|
+
|
|
1032
|
+
// Step 4: Wait for OAuth callback
|
|
1033
|
+
const waitSpinner = ora({ text: 'Waiting for authorization...', color: 'cyan' }).start();
|
|
1034
|
+
|
|
1035
|
+
try {
|
|
1036
|
+
await proxyManager.waitForAuth(authState, 300000, (status) => {
|
|
1037
|
+
waitSpinner.text = status;
|
|
1038
|
+
});
|
|
1039
|
+
waitSpinner.succeed('Authorization successful!');
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
waitSpinner.fail(`Authorization failed: ${error.message}`);
|
|
1042
|
+
await prompts.waitForEnter();
|
|
1043
|
+
return await selectProviderOption(provider);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// Step 5: Fetch models from CLIProxyAPI
|
|
1047
|
+
waitSpinner.text = 'Fetching available models from API...';
|
|
1048
|
+
waitSpinner.start();
|
|
1049
|
+
|
|
1050
|
+
let models = [];
|
|
1051
|
+
try {
|
|
1052
|
+
models = await proxyManager.getModels();
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
waitSpinner.fail(`Failed to fetch models: ${error.message}`);
|
|
1055
|
+
await prompts.waitForEnter();
|
|
1056
|
+
return await selectProviderOption(provider);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// Filter models for this provider
|
|
1060
|
+
const providerPrefixes = {
|
|
1061
|
+
anthropic: ['claude'],
|
|
1062
|
+
openai: ['gpt', 'o1', 'o3', 'o4'],
|
|
1063
|
+
gemini: ['gemini'],
|
|
1064
|
+
qwen: ['qwen'],
|
|
1065
|
+
iflow: ['deepseek', 'kimi', 'glm']
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
const prefixes = providerPrefixes[provider.id] || [];
|
|
1069
|
+
const filteredModels = models.filter(m => {
|
|
1070
|
+
const modelLower = m.toLowerCase();
|
|
1071
|
+
return prefixes.some(p => modelLower.includes(p));
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
// Use filtered models if available, otherwise use all models
|
|
1075
|
+
const availableModels = filteredModels.length > 0 ? filteredModels : models;
|
|
1076
|
+
|
|
1077
|
+
if (!availableModels || availableModels.length === 0) {
|
|
1078
|
+
waitSpinner.fail('No models available');
|
|
1079
|
+
console.log();
|
|
1080
|
+
console.log(chalk.red(' ERROR: No models found for this provider'));
|
|
1081
|
+
console.log(chalk.gray(' Make sure your subscription is active.'));
|
|
1082
|
+
console.log();
|
|
1083
|
+
await prompts.waitForEnter();
|
|
1084
|
+
return await selectProviderOption(provider);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
waitSpinner.succeed(`Found ${availableModels.length} models`);
|
|
1088
|
+
|
|
1089
|
+
// Step 6: Let user select model
|
|
1090
|
+
const selectedModel = await selectModelFromList(availableModels, config.name);
|
|
1091
|
+
if (!selectedModel) {
|
|
1092
|
+
return await selectProviderOption(provider);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// Step 7: Save agent config (CLIProxyAPI stores the actual tokens)
|
|
1096
|
+
// We just save a reference to use the proxy
|
|
1097
|
+
const credentials = {
|
|
1098
|
+
useProxy: true,
|
|
1099
|
+
provider: provider.id
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
try {
|
|
1103
|
+
await aiService.addAgent(provider.id, config.optionId, credentials, selectedModel, config.agentName);
|
|
1104
|
+
|
|
1105
|
+
console.log(chalk.green(`\n CONNECTED TO ${config.name}`));
|
|
1106
|
+
console.log(chalk.white(` MODEL: ${selectedModel}`));
|
|
1107
|
+
console.log(chalk.white(' UNLIMITED USAGE WITH YOUR SUBSCRIPTION'));
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
console.log(chalk.red(`\n FAILED TO SAVE: ${error.message}`));
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
await prompts.waitForEnter();
|
|
1113
|
+
return await aiAgentMenu();
|
|
957
1114
|
};
|
|
958
1115
|
|
|
959
1116
|
/**
|
|
@@ -527,13 +527,13 @@ const fetchAnthropicModels = async (apiKey) => {
|
|
|
527
527
|
* Fetch available models from Anthropic API (OAuth auth)
|
|
528
528
|
*
|
|
529
529
|
* @param {string} accessToken - OAuth access token
|
|
530
|
-
* @returns {Promise<
|
|
530
|
+
* @returns {Promise<Object>} { models: Array, error: string|null }
|
|
531
531
|
*
|
|
532
532
|
* Data source: https://api.anthropic.com/v1/models (GET with Bearer token)
|
|
533
533
|
* NO HARDCODED FALLBACK - models must come from API only
|
|
534
534
|
*/
|
|
535
535
|
const fetchAnthropicModelsOAuth = async (accessToken) => {
|
|
536
|
-
if (!accessToken) return null;
|
|
536
|
+
if (!accessToken) return { models: null, error: 'No access token provided' };
|
|
537
537
|
|
|
538
538
|
const modelsUrl = 'https://api.anthropic.com/v1/models';
|
|
539
539
|
|
|
@@ -547,14 +547,11 @@ const fetchAnthropicModelsOAuth = async (accessToken) => {
|
|
|
547
547
|
const response = await makeRequest(modelsUrl, { method: 'GET', headers, timeout: 15000 });
|
|
548
548
|
if (response.data && Array.isArray(response.data)) {
|
|
549
549
|
const models = response.data.map(m => m.id).filter(Boolean);
|
|
550
|
-
if (models.length > 0) return models;
|
|
550
|
+
if (models.length > 0) return { models, error: null };
|
|
551
551
|
}
|
|
552
|
-
return null;
|
|
552
|
+
return { models: null, error: 'API returned empty or invalid response' };
|
|
553
553
|
} catch (error) {
|
|
554
|
-
|
|
555
|
-
console.error('[DEBUG] fetchAnthropicModelsOAuth error:', error.message);
|
|
556
|
-
}
|
|
557
|
-
return null;
|
|
554
|
+
return { models: null, error: error.message };
|
|
558
555
|
}
|
|
559
556
|
};
|
|
560
557
|
|