hedgequantx 2.6.119 → 2.6.121
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 +69 -58
- package/src/services/ai/proxy-manager.js +65 -6
package/package.json
CHANGED
package/src/menus/ai-agent.js
CHANGED
|
@@ -1104,71 +1104,82 @@ const setupProxyOAuth = async (provider, config) => {
|
|
|
1104
1104
|
return await selectProviderOption(provider);
|
|
1105
1105
|
}
|
|
1106
1106
|
|
|
1107
|
-
//
|
|
1108
|
-
const modelSpinner = ora({ text: 'Fetching available models from API...', color: 'cyan' }).start();
|
|
1109
|
-
|
|
1110
|
-
let models = [];
|
|
1107
|
+
// Wrap entire post-auth flow in try-catch to catch any uncaught errors
|
|
1111
1108
|
try {
|
|
1112
|
-
models
|
|
1113
|
-
modelSpinner
|
|
1114
|
-
|
|
1115
|
-
|
|
1109
|
+
// Step 5: Fetch models from CLIProxyAPI
|
|
1110
|
+
const modelSpinner = ora({ text: 'Fetching available models from API...', color: 'cyan' }).start();
|
|
1111
|
+
|
|
1112
|
+
let models = [];
|
|
1113
|
+
try {
|
|
1114
|
+
models = await proxyManager.getModels();
|
|
1115
|
+
modelSpinner.succeed(`Found ${models.length} models`);
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
modelSpinner.fail(`Failed to fetch models: ${error.message}`);
|
|
1118
|
+
await prompts.waitForEnter();
|
|
1119
|
+
return await selectProviderOption(provider);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Filter models for this provider
|
|
1123
|
+
const providerPrefixes = {
|
|
1124
|
+
anthropic: ['claude'],
|
|
1125
|
+
openai: ['gpt', 'o1', 'o3', 'o4'],
|
|
1126
|
+
gemini: ['gemini'],
|
|
1127
|
+
qwen: ['qwen'],
|
|
1128
|
+
iflow: ['deepseek', 'kimi', 'glm']
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
const prefixes = providerPrefixes[provider.id] || [];
|
|
1132
|
+
const filteredModels = models.filter(m => {
|
|
1133
|
+
const modelLower = m.toLowerCase();
|
|
1134
|
+
return prefixes.some(p => modelLower.includes(p));
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
// Use filtered models if available, otherwise use all models
|
|
1138
|
+
const availableModels = filteredModels.length > 0 ? filteredModels : models;
|
|
1139
|
+
|
|
1140
|
+
if (!availableModels || availableModels.length === 0) {
|
|
1141
|
+
console.log();
|
|
1142
|
+
console.log(chalk.red(' ERROR: No models found for this provider'));
|
|
1143
|
+
console.log(chalk.gray(' Make sure your subscription is active.'));
|
|
1144
|
+
console.log();
|
|
1145
|
+
await prompts.waitForEnter();
|
|
1146
|
+
return await selectProviderOption(provider);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// Step 6: Let user select model
|
|
1150
|
+
const selectedModel = await selectModelFromList(availableModels, config.name);
|
|
1151
|
+
if (!selectedModel) {
|
|
1152
|
+
return await selectProviderOption(provider);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// Step 7: Save agent config (CLIProxyAPI stores the actual tokens)
|
|
1156
|
+
// We just save a reference to use the proxy
|
|
1157
|
+
const credentials = {
|
|
1158
|
+
useProxy: true,
|
|
1159
|
+
provider: provider.id
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1162
|
+
try {
|
|
1163
|
+
await aiService.addAgent(provider.id, config.optionId, credentials, selectedModel, config.agentName);
|
|
1164
|
+
|
|
1165
|
+
console.log(chalk.green(`\n CONNECTED TO ${config.name}`));
|
|
1166
|
+
console.log(chalk.white(` MODEL: ${selectedModel}`));
|
|
1167
|
+
console.log(chalk.white(' UNLIMITED USAGE WITH YOUR SUBSCRIPTION'));
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
console.log(chalk.red(`\n FAILED TO SAVE: ${error.message}`));
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1116
1172
|
await prompts.waitForEnter();
|
|
1117
|
-
return await
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// Filter models for this provider
|
|
1121
|
-
const providerPrefixes = {
|
|
1122
|
-
anthropic: ['claude'],
|
|
1123
|
-
openai: ['gpt', 'o1', 'o3', 'o4'],
|
|
1124
|
-
gemini: ['gemini'],
|
|
1125
|
-
qwen: ['qwen'],
|
|
1126
|
-
iflow: ['deepseek', 'kimi', 'glm']
|
|
1127
|
-
};
|
|
1128
|
-
|
|
1129
|
-
const prefixes = providerPrefixes[provider.id] || [];
|
|
1130
|
-
const filteredModels = models.filter(m => {
|
|
1131
|
-
const modelLower = m.toLowerCase();
|
|
1132
|
-
return prefixes.some(p => modelLower.includes(p));
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
// Use filtered models if available, otherwise use all models
|
|
1136
|
-
const availableModels = filteredModels.length > 0 ? filteredModels : models;
|
|
1137
|
-
|
|
1138
|
-
if (!availableModels || availableModels.length === 0) {
|
|
1173
|
+
return await aiAgentMenu();
|
|
1174
|
+
} catch (unexpectedError) {
|
|
1175
|
+
// Catch any uncaught errors in the post-auth flow
|
|
1139
1176
|
console.log();
|
|
1140
|
-
console.log(chalk.red(
|
|
1141
|
-
console.log(chalk.gray(
|
|
1177
|
+
console.log(chalk.red(` UNEXPECTED ERROR: ${unexpectedError.message}`));
|
|
1178
|
+
console.log(chalk.gray(` Stack: ${unexpectedError.stack}`));
|
|
1142
1179
|
console.log();
|
|
1143
1180
|
await prompts.waitForEnter();
|
|
1144
1181
|
return await selectProviderOption(provider);
|
|
1145
1182
|
}
|
|
1146
|
-
|
|
1147
|
-
// Step 6: Let user select model
|
|
1148
|
-
const selectedModel = await selectModelFromList(availableModels, config.name);
|
|
1149
|
-
if (!selectedModel) {
|
|
1150
|
-
return await selectProviderOption(provider);
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
// Step 7: Save agent config (CLIProxyAPI stores the actual tokens)
|
|
1154
|
-
// We just save a reference to use the proxy
|
|
1155
|
-
const credentials = {
|
|
1156
|
-
useProxy: true,
|
|
1157
|
-
provider: provider.id
|
|
1158
|
-
};
|
|
1159
|
-
|
|
1160
|
-
try {
|
|
1161
|
-
await aiService.addAgent(provider.id, config.optionId, credentials, selectedModel, config.agentName);
|
|
1162
|
-
|
|
1163
|
-
console.log(chalk.green(`\n CONNECTED TO ${config.name}`));
|
|
1164
|
-
console.log(chalk.white(` MODEL: ${selectedModel}`));
|
|
1165
|
-
console.log(chalk.white(' UNLIMITED USAGE WITH YOUR SUBSCRIPTION'));
|
|
1166
|
-
} catch (error) {
|
|
1167
|
-
console.log(chalk.red(`\n FAILED TO SAVE: ${error.message}`));
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
await prompts.waitForEnter();
|
|
1171
|
-
return await aiAgentMenu();
|
|
1172
1183
|
};
|
|
1173
1184
|
|
|
1174
1185
|
/**
|
|
@@ -495,13 +495,72 @@ const getAuthUrl = async (provider) => {
|
|
|
495
495
|
};
|
|
496
496
|
|
|
497
497
|
/**
|
|
498
|
-
*
|
|
499
|
-
* @param {string}
|
|
500
|
-
* @returns {Promise<
|
|
498
|
+
* @param {string} callbackUrl - Full callback URL (http://localhost:54545/callback?code=xxx&state=yyy)
|
|
499
|
+
* @param {string} provider - Provider ID (anthropic, openai, gemini, qwen, iflow)
|
|
500
|
+
* @returns {Promise<boolean>}
|
|
501
501
|
*/
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
502
|
+
const submitCallback = async (callbackUrl, provider = 'anthropic') => {
|
|
503
|
+
// Parse the callback URL
|
|
504
|
+
let url;
|
|
505
|
+
try {
|
|
506
|
+
url = new URL(callbackUrl);
|
|
507
|
+
} catch (e) {
|
|
508
|
+
throw new Error('Invalid callback URL format');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const code = url.searchParams.get('code');
|
|
512
|
+
const state = url.searchParams.get('state');
|
|
513
|
+
|
|
514
|
+
if (!code || !state) {
|
|
515
|
+
throw new Error('Missing code or state in callback URL');
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Each provider has its own OAuth callback port and path in CLIProxyAPI
|
|
519
|
+
// We need to submit the callback to the correct port
|
|
520
|
+
const providerConfig = {
|
|
521
|
+
anthropic: { port: 54545, path: '/callback' },
|
|
522
|
+
openai: { port: 16168, path: '/callback' },
|
|
523
|
+
gemini: { port: 8085, path: '/oauth2callback' },
|
|
524
|
+
qwen: { port: 8087, path: '/oauth2callback' },
|
|
525
|
+
iflow: { port: 8088, path: '/callback' }
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const config = providerConfig[provider] || providerConfig.anthropic;
|
|
529
|
+
|
|
530
|
+
// Submit to the provider's OAuth callback port directly
|
|
531
|
+
return new Promise((resolve, reject) => {
|
|
532
|
+
const callbackPath = `${config.path}?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`;
|
|
533
|
+
|
|
534
|
+
const req = http.request({
|
|
535
|
+
hostname: '127.0.0.1',
|
|
536
|
+
port: config.port,
|
|
537
|
+
path: callbackPath,
|
|
538
|
+
method: 'GET',
|
|
539
|
+
timeout: 30000
|
|
540
|
+
}, (res) => {
|
|
541
|
+
let data = '';
|
|
542
|
+
res.on('data', chunk => data += chunk);
|
|
543
|
+
res.on('end', () => {
|
|
544
|
+
// Success if we get a redirect or success page
|
|
545
|
+
if (res.statusCode === 200 || res.statusCode === 302 || res.statusCode === 301) {
|
|
546
|
+
resolve(true);
|
|
547
|
+
} else {
|
|
548
|
+
reject(new Error(`Callback failed with status ${res.statusCode}`));
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
req.on('error', (e) => {
|
|
554
|
+
reject(new Error(`Failed to submit callback: ${e.message}`));
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
req.on('timeout', () => {
|
|
558
|
+
req.destroy();
|
|
559
|
+
reject(new Error('Callback request timeout'));
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
req.end();
|
|
563
|
+
});
|
|
505
564
|
};
|
|
506
565
|
|
|
507
566
|
/**
|