hedgequantx 2.5.24 → 2.5.26
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/README.md +87 -14
- package/package.json +1 -1
- package/src/menus/ai-agent.js +161 -250
- package/src/services/ai/client.js +3 -2
- package/src/services/ai/index.js +10 -40
- package/src/services/ai/providers/index.js +3 -12
- package/src/services/ai/token-scanner.js +0 -1414
package/src/menus/ai-agent.js
CHANGED
|
@@ -10,7 +10,6 @@ const { getLogoWidth, drawBoxHeader, drawBoxHeaderContinue, drawBoxFooter, displ
|
|
|
10
10
|
const { prompts } = require('../utils');
|
|
11
11
|
const aiService = require('../services/ai');
|
|
12
12
|
const { getCategories, getProvidersByCategory } = require('../services/ai/providers');
|
|
13
|
-
const tokenScanner = require('../services/ai/token-scanner');
|
|
14
13
|
const oauthAnthropic = require('../services/ai/oauth-anthropic');
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -39,7 +38,7 @@ const aiAgentMenu = async () => {
|
|
|
39
38
|
const agentCount = agents.length;
|
|
40
39
|
|
|
41
40
|
if (agentCount === 0) {
|
|
42
|
-
console.log(makeLine(chalk.
|
|
41
|
+
console.log(makeLine(chalk.white('STATUS: NO AGENTS CONNECTED'), 'left'));
|
|
43
42
|
} else {
|
|
44
43
|
console.log(makeLine(chalk.green(`STATUS: ${agentCount} AGENT${agentCount > 1 ? 'S' : ''} CONNECTED`), 'left'));
|
|
45
44
|
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
@@ -49,16 +48,26 @@ const aiAgentMenu = async () => {
|
|
|
49
48
|
const agent = agents[i];
|
|
50
49
|
// Show ACTIVE marker (if single agent, it's always active)
|
|
51
50
|
const isActive = agent.isActive || agents.length === 1;
|
|
52
|
-
const activeMarker = isActive ?
|
|
51
|
+
const activeMarker = isActive ? ' [ACTIVE]' : '';
|
|
53
52
|
const providerColor = agent.providerId === 'anthropic' ? chalk.magenta :
|
|
54
53
|
agent.providerId === 'openai' ? chalk.green :
|
|
55
54
|
agent.providerId === 'openrouter' ? chalk.yellow : chalk.cyan;
|
|
56
55
|
|
|
56
|
+
// Calculate max lengths to fit in box
|
|
57
|
+
const prefix = `[${i + 1}] `;
|
|
58
|
+
const suffix = ` - ${agent.model || 'N/A'}`;
|
|
59
|
+
const maxNameLen = W - prefix.length - activeMarker.length - suffix.length - 2;
|
|
60
|
+
|
|
61
|
+
// Truncate agent name if too long
|
|
62
|
+
const displayName = agent.name.length > maxNameLen
|
|
63
|
+
? agent.name.substring(0, maxNameLen - 3) + '...'
|
|
64
|
+
: agent.name;
|
|
65
|
+
|
|
57
66
|
console.log(makeLine(
|
|
58
|
-
chalk.white(
|
|
59
|
-
providerColor(
|
|
60
|
-
activeMarker +
|
|
61
|
-
chalk.
|
|
67
|
+
chalk.white(prefix) +
|
|
68
|
+
providerColor(displayName) +
|
|
69
|
+
chalk.green(activeMarker) +
|
|
70
|
+
chalk.white(suffix)
|
|
62
71
|
));
|
|
63
72
|
}
|
|
64
73
|
}
|
|
@@ -96,13 +105,13 @@ const aiAgentMenu = async () => {
|
|
|
96
105
|
if (agentCount > 1) {
|
|
97
106
|
menuRow2(menuItem('+', 'ADD AGENT', chalk.green), menuItem('S', 'SET ACTIVE', chalk.cyan));
|
|
98
107
|
menuRow2(menuItem('M', 'CHANGE MODEL', chalk.yellow), menuItem('R', 'REMOVE AGENT', chalk.red));
|
|
99
|
-
menuRow2(menuItem('X', 'REMOVE ALL', chalk.red), menuItem('<', 'BACK', chalk.
|
|
108
|
+
menuRow2(menuItem('X', 'REMOVE ALL', chalk.red), menuItem('<', 'BACK', chalk.white));
|
|
100
109
|
} else {
|
|
101
110
|
menuRow2(menuItem('+', 'ADD AGENT', chalk.green), menuItem('M', 'CHANGE MODEL', chalk.yellow));
|
|
102
|
-
menuRow2(menuItem('R', 'REMOVE AGENT', chalk.red), menuItem('<', 'BACK', chalk.
|
|
111
|
+
menuRow2(menuItem('R', 'REMOVE AGENT', chalk.red), menuItem('<', 'BACK', chalk.white));
|
|
103
112
|
}
|
|
104
113
|
} else {
|
|
105
|
-
menuRow2(menuItem('+', 'ADD AGENT', chalk.green), menuItem('<', 'BACK', chalk.
|
|
114
|
+
menuRow2(menuItem('+', 'ADD AGENT', chalk.green), menuItem('<', 'BACK', chalk.white));
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
drawBoxFooter(boxWidth);
|
|
@@ -118,7 +127,7 @@ const aiAgentMenu = async () => {
|
|
|
118
127
|
|
|
119
128
|
switch (input) {
|
|
120
129
|
case '+':
|
|
121
|
-
return await
|
|
130
|
+
return await selectCategory();
|
|
122
131
|
case 's':
|
|
123
132
|
if (agentCount > 1) {
|
|
124
133
|
return await selectActiveAgent();
|
|
@@ -171,9 +180,9 @@ const showAgentDetails = async (agent) => {
|
|
|
171
180
|
agent.providerId === 'openrouter' ? chalk.yellow : chalk.cyan;
|
|
172
181
|
|
|
173
182
|
console.log(makeLine(chalk.white('NAME: ') + providerColor(agent.name)));
|
|
174
|
-
console.log(makeLine(chalk.white('PROVIDER: ') + chalk.
|
|
175
|
-
console.log(makeLine(chalk.white('MODEL: ') + chalk.
|
|
176
|
-
console.log(makeLine(chalk.white('STATUS: ') + (agent.isActive ? chalk.green('ACTIVE') : chalk.
|
|
183
|
+
console.log(makeLine(chalk.white('PROVIDER: ') + chalk.white(agent.provider?.name || agent.providerId)));
|
|
184
|
+
console.log(makeLine(chalk.white('MODEL: ') + chalk.white(agent.model)));
|
|
185
|
+
console.log(makeLine(chalk.white('STATUS: ') + (agent.isActive ? chalk.green('ACTIVE') : chalk.white('STANDBY'))));
|
|
177
186
|
|
|
178
187
|
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
179
188
|
|
|
@@ -182,7 +191,7 @@ const showAgentDetails = async (agent) => {
|
|
|
182
191
|
}
|
|
183
192
|
console.log(makeLine(chalk.yellow('[M] CHANGE MODEL')));
|
|
184
193
|
console.log(makeLine(chalk.red('[R] REMOVE')));
|
|
185
|
-
console.log(makeLine(chalk.
|
|
194
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
186
195
|
|
|
187
196
|
drawBoxFooter(boxWidth);
|
|
188
197
|
|
|
@@ -242,7 +251,7 @@ const selectActiveAgent = async () => {
|
|
|
242
251
|
}
|
|
243
252
|
|
|
244
253
|
console.log(makeLine(''));
|
|
245
|
-
console.log(makeLine(chalk.
|
|
254
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
246
255
|
|
|
247
256
|
drawBoxFooter(boxWidth);
|
|
248
257
|
|
|
@@ -289,12 +298,12 @@ const selectAgentForModelChange = async () => {
|
|
|
289
298
|
for (let i = 0; i < agents.length; i++) {
|
|
290
299
|
const agent = agents[i];
|
|
291
300
|
console.log(makeLine(
|
|
292
|
-
chalk.white(`[${i + 1}] `) + chalk.cyan(agent.name) + chalk.
|
|
301
|
+
chalk.white(`[${i + 1}] `) + chalk.cyan(agent.name) + chalk.white(` - ${agent.model}`)
|
|
293
302
|
));
|
|
294
303
|
}
|
|
295
304
|
|
|
296
305
|
console.log(makeLine(''));
|
|
297
|
-
console.log(makeLine(chalk.
|
|
306
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
298
307
|
|
|
299
308
|
drawBoxFooter(boxWidth);
|
|
300
309
|
|
|
@@ -346,7 +355,7 @@ const selectAgentToRemove = async () => {
|
|
|
346
355
|
}
|
|
347
356
|
|
|
348
357
|
console.log(makeLine(''));
|
|
349
|
-
console.log(makeLine(chalk.
|
|
358
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
350
359
|
|
|
351
360
|
drawBoxFooter(boxWidth);
|
|
352
361
|
|
|
@@ -367,178 +376,6 @@ const selectAgentToRemove = async () => {
|
|
|
367
376
|
return await aiAgentMenu();
|
|
368
377
|
};
|
|
369
378
|
|
|
370
|
-
// Cache for scanned tokens (avoid multiple Keychain prompts)
|
|
371
|
-
let cachedTokens = null;
|
|
372
|
-
let cacheTimestamp = 0;
|
|
373
|
-
const CACHE_TTL = 60000; // 1 minute cache
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Show existing tokens found on the system
|
|
377
|
-
*/
|
|
378
|
-
const showExistingTokens = async () => {
|
|
379
|
-
const boxWidth = getLogoWidth();
|
|
380
|
-
const W = boxWidth - 2;
|
|
381
|
-
|
|
382
|
-
const makeLine = (content) => {
|
|
383
|
-
const plainLen = content.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
384
|
-
const padding = W - plainLen;
|
|
385
|
-
return chalk.cyan('║') + ' ' + content + ' '.repeat(Math.max(0, padding - 1)) + chalk.cyan('║');
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
// Check cache first
|
|
389
|
-
const now = Date.now();
|
|
390
|
-
let tokens;
|
|
391
|
-
|
|
392
|
-
if (cachedTokens && (now - cacheTimestamp) < CACHE_TTL) {
|
|
393
|
-
tokens = cachedTokens;
|
|
394
|
-
} else {
|
|
395
|
-
console.clear();
|
|
396
|
-
displayBanner();
|
|
397
|
-
drawBoxHeaderContinue('SCANNING FOR EXISTING SESSIONS...', boxWidth);
|
|
398
|
-
console.log(makeLine(''));
|
|
399
|
-
console.log(makeLine(chalk.gray('CHECKING VS CODE, CURSOR, CLAUDE CLI, OPENCODE...')));
|
|
400
|
-
console.log(makeLine(''));
|
|
401
|
-
drawBoxFooter(boxWidth);
|
|
402
|
-
|
|
403
|
-
// Scan for tokens and cache
|
|
404
|
-
tokens = tokenScanner.scanAllSources();
|
|
405
|
-
cachedTokens = tokens;
|
|
406
|
-
cacheTimestamp = now;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (tokens.length === 0) {
|
|
410
|
-
// No tokens found, go directly to category selection
|
|
411
|
-
return await selectCategory();
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Show found tokens
|
|
415
|
-
console.clear();
|
|
416
|
-
displayBanner();
|
|
417
|
-
drawBoxHeaderContinue('EXISTING SESSIONS FOUND', boxWidth);
|
|
418
|
-
|
|
419
|
-
console.log(makeLine(chalk.green(`FOUND ${tokens.length} EXISTING SESSION(S)`)));
|
|
420
|
-
console.log(makeLine(''));
|
|
421
|
-
|
|
422
|
-
const formatted = tokenScanner.formatResults(tokens);
|
|
423
|
-
|
|
424
|
-
for (const t of formatted) {
|
|
425
|
-
const providerColor = t.provider.includes('CLAUDE') ? chalk.magenta :
|
|
426
|
-
t.provider.includes('OPENAI') ? chalk.green :
|
|
427
|
-
t.provider.includes('OPENROUTER') ? chalk.yellow : chalk.cyan;
|
|
428
|
-
|
|
429
|
-
console.log(makeLine(
|
|
430
|
-
chalk.white(`[${t.index}] `) +
|
|
431
|
-
providerColor(t.provider) +
|
|
432
|
-
chalk.gray(` (${t.type})`)
|
|
433
|
-
));
|
|
434
|
-
console.log(makeLine(
|
|
435
|
-
chalk.gray(` ${t.icon} ${t.source} - ${t.lastUsed}`)
|
|
436
|
-
));
|
|
437
|
-
console.log(makeLine(
|
|
438
|
-
chalk.gray(` TOKEN: ${t.tokenPreview}`)
|
|
439
|
-
));
|
|
440
|
-
console.log(makeLine(''));
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
444
|
-
console.log(makeLine(chalk.cyan('[N] CONNECT NEW PROVIDER')));
|
|
445
|
-
console.log(makeLine(chalk.gray('[<] BACK')));
|
|
446
|
-
|
|
447
|
-
drawBoxFooter(boxWidth);
|
|
448
|
-
|
|
449
|
-
const choice = await prompts.textInput(chalk.cyan('SELECT (1-' + tokens.length + '/N/<):'));
|
|
450
|
-
|
|
451
|
-
if (choice === '<' || choice?.toLowerCase() === 'b') {
|
|
452
|
-
return await aiAgentMenu();
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (choice?.toLowerCase() === 'n') {
|
|
456
|
-
return await selectCategory();
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
const index = parseInt(choice) - 1;
|
|
460
|
-
if (isNaN(index) || index < 0 || index >= tokens.length) {
|
|
461
|
-
return await showExistingTokens();
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Use selected token
|
|
465
|
-
const selectedToken = tokens[index];
|
|
466
|
-
|
|
467
|
-
const spinner = ora({ text: 'VALIDATING TOKEN...', color: 'cyan' }).start();
|
|
468
|
-
|
|
469
|
-
try {
|
|
470
|
-
// Validate the token - include metadata from scanner
|
|
471
|
-
const credentials = {
|
|
472
|
-
apiKey: selectedToken.token,
|
|
473
|
-
sessionKey: selectedToken.token,
|
|
474
|
-
accessToken: selectedToken.token,
|
|
475
|
-
fromKeychain: selectedToken.sourceId === 'secureStorage' || selectedToken.sourceId === 'keychain',
|
|
476
|
-
subscriptionType: selectedToken.subscriptionType,
|
|
477
|
-
refreshToken: selectedToken.refreshToken,
|
|
478
|
-
expiresAt: selectedToken.expiresAt
|
|
479
|
-
};
|
|
480
|
-
const validation = await aiService.validateConnection(selectedToken.provider, selectedToken.type, credentials);
|
|
481
|
-
|
|
482
|
-
if (!validation.valid) {
|
|
483
|
-
spinner.fail(`TOKEN INVALID OR EXPIRED: ${validation.error}`);
|
|
484
|
-
await prompts.waitForEnter();
|
|
485
|
-
return await showExistingTokens();
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Get provider info
|
|
489
|
-
const { getProvider } = require('../services/ai/providers');
|
|
490
|
-
const provider = getProvider(selectedToken.provider);
|
|
491
|
-
|
|
492
|
-
if (!provider) {
|
|
493
|
-
spinner.fail('PROVIDER NOT SUPPORTED');
|
|
494
|
-
await prompts.waitForEnter();
|
|
495
|
-
return await showExistingTokens();
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
spinner.text = 'FETCHING AVAILABLE MODELS...';
|
|
499
|
-
|
|
500
|
-
// Fetch models from API with the token
|
|
501
|
-
const { fetchAnthropicModels, fetchOpenAIModels } = require('../services/ai/client');
|
|
502
|
-
|
|
503
|
-
let models = null;
|
|
504
|
-
if (selectedToken.provider === 'anthropic') {
|
|
505
|
-
models = await fetchAnthropicModels(credentials.apiKey);
|
|
506
|
-
} else {
|
|
507
|
-
models = await fetchOpenAIModels(provider.endpoint, credentials.apiKey);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if (!models || models.length === 0) {
|
|
511
|
-
spinner.fail('COULD NOT FETCH MODELS FROM API');
|
|
512
|
-
await prompts.waitForEnter();
|
|
513
|
-
return await showExistingTokens();
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
spinner.succeed(`FOUND ${models.length} MODELS`);
|
|
517
|
-
|
|
518
|
-
// Let user select model
|
|
519
|
-
const selectedModel = await selectModelFromList(models, provider.name);
|
|
520
|
-
if (!selectedModel) {
|
|
521
|
-
return await showExistingTokens();
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Add agent with selected model
|
|
525
|
-
const agentName = `${provider.name} (${selectedToken.source})`;
|
|
526
|
-
await aiService.addAgent(selectedToken.provider, 'api_key', credentials, selectedModel, agentName);
|
|
527
|
-
|
|
528
|
-
console.log(chalk.green(`\n AGENT ADDED: ${provider.name}`));
|
|
529
|
-
console.log(chalk.gray(` SOURCE: ${selectedToken.source}`));
|
|
530
|
-
console.log(chalk.gray(` MODEL: ${selectedModel}`));
|
|
531
|
-
|
|
532
|
-
await prompts.waitForEnter();
|
|
533
|
-
return await aiAgentMenu();
|
|
534
|
-
|
|
535
|
-
} catch (error) {
|
|
536
|
-
spinner.fail(`CONNECTION FAILED: ${error.message}`);
|
|
537
|
-
await prompts.waitForEnter();
|
|
538
|
-
return await showExistingTokens();
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
|
|
542
379
|
/**
|
|
543
380
|
* Select provider category
|
|
544
381
|
*/
|
|
@@ -573,20 +410,20 @@ const selectCategory = async () => {
|
|
|
573
410
|
chalk.cyan('[2] DIRECT PROVIDERS')
|
|
574
411
|
));
|
|
575
412
|
console.log(make2ColRow(
|
|
576
|
-
chalk.
|
|
577
|
-
chalk.
|
|
413
|
+
chalk.white(' 1 API = 100+ models'),
|
|
414
|
+
chalk.white(' Connect to each provider')
|
|
578
415
|
));
|
|
579
416
|
console.log(makeLine(''));
|
|
580
417
|
console.log(make2ColRow(
|
|
581
418
|
chalk.yellow('[3] LOCAL (FREE)'),
|
|
582
|
-
chalk.
|
|
419
|
+
chalk.white('[4] CUSTOM')
|
|
583
420
|
));
|
|
584
421
|
console.log(make2ColRow(
|
|
585
|
-
chalk.
|
|
586
|
-
chalk.
|
|
422
|
+
chalk.white(' Run on your machine'),
|
|
423
|
+
chalk.white(' Self-hosted solutions')
|
|
587
424
|
));
|
|
588
425
|
console.log(makeLine(''));
|
|
589
|
-
console.log(makeLine(chalk.
|
|
426
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
590
427
|
|
|
591
428
|
drawBoxFooter(boxWidth);
|
|
592
429
|
|
|
@@ -637,7 +474,7 @@ const selectProvider = async (categoryId) => {
|
|
|
637
474
|
const providers = getProvidersByCategory(categoryId);
|
|
638
475
|
|
|
639
476
|
if (providers.length === 0) {
|
|
640
|
-
console.log(makeLine(chalk.
|
|
477
|
+
console.log(makeLine(chalk.white('NO PROVIDERS IN THIS CATEGORY')));
|
|
641
478
|
drawBoxFooter(boxWidth);
|
|
642
479
|
await prompts.waitForEnter();
|
|
643
480
|
return await selectCategory();
|
|
@@ -662,14 +499,14 @@ const selectProvider = async (categoryId) => {
|
|
|
662
499
|
const rightDesc = right ? ' ' + right.description : '';
|
|
663
500
|
|
|
664
501
|
console.log(make2ColRow(
|
|
665
|
-
chalk.
|
|
666
|
-
chalk.
|
|
502
|
+
chalk.white(leftDesc.length > col1Width - 3 ? leftDesc.substring(0, col1Width - 6) + '...' : leftDesc),
|
|
503
|
+
chalk.white(rightDesc.length > col1Width - 3 ? rightDesc.substring(0, col1Width - 6) + '...' : rightDesc)
|
|
667
504
|
));
|
|
668
505
|
|
|
669
506
|
console.log(makeLine(''));
|
|
670
507
|
}
|
|
671
508
|
|
|
672
|
-
console.log(makeLine(chalk.
|
|
509
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
673
510
|
|
|
674
511
|
drawBoxFooter(boxWidth);
|
|
675
512
|
|
|
@@ -738,8 +575,8 @@ const selectProviderOption = async (provider) => {
|
|
|
738
575
|
const leftDesc1 = left.description[0] ? ' ' + left.description[0] : '';
|
|
739
576
|
const rightDesc1 = right?.description[0] ? ' ' + right.description[0] : '';
|
|
740
577
|
console.log(make2ColRow(
|
|
741
|
-
chalk.
|
|
742
|
-
chalk.
|
|
578
|
+
chalk.white(leftDesc1.length > col1Width - 2 ? leftDesc1.substring(0, col1Width - 5) + '...' : leftDesc1),
|
|
579
|
+
chalk.white(rightDesc1.length > col1Width - 2 ? rightDesc1.substring(0, col1Width - 5) + '...' : rightDesc1)
|
|
743
580
|
));
|
|
744
581
|
|
|
745
582
|
// Second description line if exists
|
|
@@ -747,15 +584,15 @@ const selectProviderOption = async (provider) => {
|
|
|
747
584
|
const rightDesc2 = right?.description[1] ? ' ' + right.description[1] : '';
|
|
748
585
|
if (leftDesc2 || rightDesc2) {
|
|
749
586
|
console.log(make2ColRow(
|
|
750
|
-
chalk.
|
|
751
|
-
chalk.
|
|
587
|
+
chalk.white(leftDesc2.length > col1Width - 2 ? leftDesc2.substring(0, col1Width - 5) + '...' : leftDesc2),
|
|
588
|
+
chalk.white(rightDesc2.length > col1Width - 2 ? rightDesc2.substring(0, col1Width - 5) + '...' : rightDesc2)
|
|
752
589
|
));
|
|
753
590
|
}
|
|
754
591
|
|
|
755
592
|
console.log(makeLine(''));
|
|
756
593
|
}
|
|
757
594
|
|
|
758
|
-
console.log(makeLine(chalk.
|
|
595
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
759
596
|
|
|
760
597
|
drawBoxFooter(boxWidth);
|
|
761
598
|
|
|
@@ -787,7 +624,7 @@ const openBrowser = (url) => {
|
|
|
787
624
|
else cmd = `xdg-open "${url}"`;
|
|
788
625
|
|
|
789
626
|
exec(cmd, (err) => {
|
|
790
|
-
if (err) console.log(chalk.
|
|
627
|
+
if (err) console.log(chalk.white(' Could not open browser automatically'));
|
|
791
628
|
});
|
|
792
629
|
};
|
|
793
630
|
|
|
@@ -870,7 +707,7 @@ const setupOAuthConnection = async (provider) => {
|
|
|
870
707
|
console.log(makeLine(chalk.white('3. COPY THE AUTHORIZATION CODE')));
|
|
871
708
|
console.log(makeLine(chalk.white('4. PASTE IT HERE')));
|
|
872
709
|
console.log(makeLine(''));
|
|
873
|
-
console.log(makeLine(chalk.
|
|
710
|
+
console.log(makeLine(chalk.white('OPENING BROWSER IN 3 SECONDS...')));
|
|
874
711
|
|
|
875
712
|
drawBoxFooter(boxWidth);
|
|
876
713
|
|
|
@@ -891,9 +728,9 @@ const setupOAuthConnection = async (provider) => {
|
|
|
891
728
|
console.log(makeLine(chalk.white('AFTER LOGGING IN, YOU WILL SEE A CODE')));
|
|
892
729
|
console.log(makeLine(chalk.white('COPY THE ENTIRE CODE AND PASTE IT BELOW')));
|
|
893
730
|
console.log(makeLine(''));
|
|
894
|
-
console.log(makeLine(chalk.
|
|
731
|
+
console.log(makeLine(chalk.white('THE CODE LOOKS LIKE: abc123...#xyz789...')));
|
|
895
732
|
console.log(makeLine(''));
|
|
896
|
-
console.log(makeLine(chalk.
|
|
733
|
+
console.log(makeLine(chalk.white('TYPE < TO CANCEL')));
|
|
897
734
|
|
|
898
735
|
drawBoxFooter(boxWidth);
|
|
899
736
|
console.log();
|
|
@@ -937,16 +774,18 @@ const setupOAuthConnection = async (provider) => {
|
|
|
937
774
|
spinner.succeed(`FOUND ${models.length} MODELS`);
|
|
938
775
|
}
|
|
939
776
|
|
|
777
|
+
if (!models || models.length === 0) {
|
|
778
|
+
spinner.fail('COULD NOT FETCH MODELS FROM API');
|
|
779
|
+
console.log(chalk.white(' OAuth authentication may not support model listing.'));
|
|
780
|
+
console.log(chalk.white(' Please use API KEY authentication instead.'));
|
|
781
|
+
await prompts.waitForEnter();
|
|
782
|
+
return await selectProviderOption(provider);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
spinner.succeed(`FOUND ${models.length} MODELS`);
|
|
786
|
+
|
|
940
787
|
// Let user select model
|
|
941
|
-
const
|
|
942
|
-
'claude-sonnet-4-20250514',
|
|
943
|
-
'claude-sonnet-4-5-20250514',
|
|
944
|
-
'claude-3-5-sonnet-20241022',
|
|
945
|
-
'claude-3-5-haiku-20241022',
|
|
946
|
-
'claude-3-opus-20240229'
|
|
947
|
-
];
|
|
948
|
-
|
|
949
|
-
const selectedModel = await selectModelFromList(availableModels, 'CLAUDE PRO/MAX');
|
|
788
|
+
const selectedModel = await selectModelFromList(models, 'CLAUDE PRO/MAX');
|
|
950
789
|
if (!selectedModel) {
|
|
951
790
|
return await selectProviderOption(provider);
|
|
952
791
|
}
|
|
@@ -956,8 +795,8 @@ const setupOAuthConnection = async (provider) => {
|
|
|
956
795
|
await aiService.addAgent('anthropic', 'oauth_max', credentials, selectedModel, 'Claude Pro/Max');
|
|
957
796
|
|
|
958
797
|
console.log(chalk.green('\n CONNECTED TO CLAUDE PRO/MAX'));
|
|
959
|
-
console.log(chalk.
|
|
960
|
-
console.log(chalk.
|
|
798
|
+
console.log(chalk.white(` MODEL: ${selectedModel}`));
|
|
799
|
+
console.log(chalk.white(' UNLIMITED USAGE WITH YOUR SUBSCRIPTION'));
|
|
961
800
|
} catch (error) {
|
|
962
801
|
console.log(chalk.red(`\n FAILED TO SAVE: ${error.message}`));
|
|
963
802
|
}
|
|
@@ -1009,17 +848,17 @@ const setupConnection = async (provider, option) => {
|
|
|
1009
848
|
if (option.url && (field === 'apiKey' || field === 'sessionKey' || field === 'accessToken')) {
|
|
1010
849
|
console.log(makeLine(chalk.cyan('LINK: ') + chalk.green(option.url)));
|
|
1011
850
|
console.log(makeLine(''));
|
|
1012
|
-
console.log(makeLine(chalk.
|
|
851
|
+
console.log(makeLine(chalk.white('OPENING BROWSER...')));
|
|
1013
852
|
openBrowser(option.url);
|
|
1014
853
|
}
|
|
1015
854
|
|
|
1016
855
|
// Show default for endpoint
|
|
1017
856
|
if (field === 'endpoint' && option.defaultEndpoint) {
|
|
1018
|
-
console.log(makeLine(chalk.
|
|
857
|
+
console.log(makeLine(chalk.white(`DEFAULT: ${option.defaultEndpoint}`)));
|
|
1019
858
|
}
|
|
1020
859
|
|
|
1021
860
|
console.log(makeLine(''));
|
|
1022
|
-
console.log(makeLine(chalk.
|
|
861
|
+
console.log(makeLine(chalk.white('TYPE < TO GO BACK')));
|
|
1023
862
|
|
|
1024
863
|
drawBoxFooter(boxWidth);
|
|
1025
864
|
console.log();
|
|
@@ -1029,33 +868,33 @@ const setupConnection = async (provider, option) => {
|
|
|
1029
868
|
switch (field) {
|
|
1030
869
|
case 'apiKey':
|
|
1031
870
|
value = await prompts.textInput(chalk.cyan('PASTE API KEY:'));
|
|
1032
|
-
if (!value || value === '<') return await selectProviderOption(provider);
|
|
871
|
+
if (!value || value.trim() === '<' || value.trim() === '') return await selectProviderOption(provider);
|
|
1033
872
|
credentials.apiKey = value.trim();
|
|
1034
873
|
break;
|
|
1035
874
|
|
|
1036
875
|
case 'sessionKey':
|
|
1037
876
|
value = await prompts.textInput(chalk.cyan('PASTE SESSION KEY:'));
|
|
1038
|
-
if (!value || value === '<') return await selectProviderOption(provider);
|
|
877
|
+
if (!value || value.trim() === '<' || value.trim() === '') return await selectProviderOption(provider);
|
|
1039
878
|
credentials.sessionKey = value.trim();
|
|
1040
879
|
break;
|
|
1041
880
|
|
|
1042
881
|
case 'accessToken':
|
|
1043
882
|
value = await prompts.textInput(chalk.cyan('PASTE ACCESS TOKEN:'));
|
|
1044
|
-
if (!value || value === '<') return await selectProviderOption(provider);
|
|
883
|
+
if (!value || value.trim() === '<' || value.trim() === '') return await selectProviderOption(provider);
|
|
1045
884
|
credentials.accessToken = value.trim();
|
|
1046
885
|
break;
|
|
1047
886
|
|
|
1048
887
|
case 'endpoint':
|
|
1049
888
|
const defaultEndpoint = option.defaultEndpoint || '';
|
|
1050
889
|
value = await prompts.textInput(chalk.cyan(`ENDPOINT [${defaultEndpoint || 'required'}]:`));
|
|
1051
|
-
if (value === '<') return await selectProviderOption(provider);
|
|
890
|
+
if (value && value.trim() === '<') return await selectProviderOption(provider);
|
|
1052
891
|
credentials.endpoint = (value || defaultEndpoint).trim();
|
|
1053
892
|
if (!credentials.endpoint) return await selectProviderOption(provider);
|
|
1054
893
|
break;
|
|
1055
894
|
|
|
1056
895
|
case 'model':
|
|
1057
896
|
value = await prompts.textInput(chalk.cyan('MODEL NAME:'));
|
|
1058
|
-
if (!value || value === '<') return await selectProviderOption(provider);
|
|
897
|
+
if (!value || value.trim() === '<' || value.trim() === '') return await selectProviderOption(provider);
|
|
1059
898
|
credentials.model = value.trim();
|
|
1060
899
|
break;
|
|
1061
900
|
}
|
|
@@ -1081,10 +920,10 @@ const setupConnection = async (provider, option) => {
|
|
|
1081
920
|
|
|
1082
921
|
// Show available models for local providers
|
|
1083
922
|
if (validation.models && validation.models.length > 0) {
|
|
1084
|
-
console.log(chalk.
|
|
923
|
+
console.log(chalk.white(` AVAILABLE MODELS: ${validation.models.slice(0, 5).join(', ')}`));
|
|
1085
924
|
}
|
|
1086
925
|
|
|
1087
|
-
console.log(chalk.
|
|
926
|
+
console.log(chalk.white(` USING MODEL: ${model}`));
|
|
1088
927
|
} catch (error) {
|
|
1089
928
|
spinner.fail(`FAILED TO SAVE: ${error.message}`);
|
|
1090
929
|
}
|
|
@@ -1117,7 +956,7 @@ const selectModelFromList = async (models, providerName) => {
|
|
|
1117
956
|
|
|
1118
957
|
if (!models || models.length === 0) {
|
|
1119
958
|
console.log(makeLine(chalk.red('NO MODELS AVAILABLE')));
|
|
1120
|
-
console.log(makeLine(chalk.
|
|
959
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
1121
960
|
drawBoxFooter(boxWidth);
|
|
1122
961
|
await prompts.waitForEnter();
|
|
1123
962
|
return null;
|
|
@@ -1126,14 +965,44 @@ const selectModelFromList = async (models, providerName) => {
|
|
|
1126
965
|
// Sort models (newest first)
|
|
1127
966
|
const sortedModels = [...models].sort((a, b) => b.localeCompare(a));
|
|
1128
967
|
|
|
1129
|
-
// Display models
|
|
1130
|
-
sortedModels.
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
968
|
+
// Display models in 2 columns
|
|
969
|
+
const rows = Math.ceil(sortedModels.length / 2);
|
|
970
|
+
const colWidth = Math.floor((W - 4) / 2);
|
|
971
|
+
|
|
972
|
+
for (let i = 0; i < rows; i++) {
|
|
973
|
+
const leftIndex = i;
|
|
974
|
+
const rightIndex = i + rows;
|
|
975
|
+
|
|
976
|
+
// Left column
|
|
977
|
+
const leftModel = sortedModels[leftIndex];
|
|
978
|
+
const leftNum = chalk.cyan(`[${leftIndex + 1}]`);
|
|
979
|
+
const leftName = leftModel.length > colWidth - 6
|
|
980
|
+
? leftModel.substring(0, colWidth - 9) + '...'
|
|
981
|
+
: leftModel;
|
|
982
|
+
const leftText = `${leftNum} ${chalk.yellow(leftName)}`;
|
|
983
|
+
const leftPlain = `[${leftIndex + 1}] ${leftName}`;
|
|
984
|
+
|
|
985
|
+
// Right column (if exists)
|
|
986
|
+
let rightText = '';
|
|
987
|
+
let rightPlain = '';
|
|
988
|
+
if (rightIndex < sortedModels.length) {
|
|
989
|
+
const rightModel = sortedModels[rightIndex];
|
|
990
|
+
const rightNum = chalk.cyan(`[${rightIndex + 1}]`);
|
|
991
|
+
const rightName = rightModel.length > colWidth - 6
|
|
992
|
+
? rightModel.substring(0, colWidth - 9) + '...'
|
|
993
|
+
: rightModel;
|
|
994
|
+
rightText = `${rightNum} ${chalk.yellow(rightName)}`;
|
|
995
|
+
rightPlain = `[${rightIndex + 1}] ${rightName}`;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Pad left column and combine
|
|
999
|
+
const leftPadding = colWidth - leftPlain.length;
|
|
1000
|
+
const line = leftText + ' '.repeat(Math.max(2, leftPadding)) + rightText;
|
|
1001
|
+
console.log(makeLine(line));
|
|
1002
|
+
}
|
|
1134
1003
|
|
|
1135
1004
|
console.log(makeLine(''));
|
|
1136
|
-
console.log(makeLine(chalk.
|
|
1005
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
1137
1006
|
|
|
1138
1007
|
drawBoxFooter(boxWidth);
|
|
1139
1008
|
|
|
@@ -1169,17 +1038,28 @@ const selectModel = async (agent) => {
|
|
|
1169
1038
|
displayBanner();
|
|
1170
1039
|
drawBoxHeaderContinue(`SELECT MODEL - ${agent.name}`, boxWidth);
|
|
1171
1040
|
|
|
1172
|
-
console.log(makeLine(chalk.
|
|
1041
|
+
console.log(makeLine(chalk.white('FETCHING AVAILABLE MODELS FROM API...')));
|
|
1173
1042
|
drawBoxFooter(boxWidth);
|
|
1174
1043
|
|
|
1175
1044
|
// Fetch models from real API
|
|
1176
|
-
const { fetchAnthropicModels, fetchOpenAIModels } = require('../services/ai/client');
|
|
1045
|
+
const { fetchAnthropicModels, fetchAnthropicModelsOAuth, fetchOpenAIModels } = require('../services/ai/client');
|
|
1177
1046
|
|
|
1178
1047
|
let models = null;
|
|
1179
1048
|
const agentCredentials = aiService.getAgentCredentials(agent.id);
|
|
1180
1049
|
|
|
1181
1050
|
if (agent.providerId === 'anthropic') {
|
|
1182
|
-
|
|
1051
|
+
// Check if OAuth credentials or OAuth-like token (sk-ant-oat...)
|
|
1052
|
+
const token = agentCredentials?.apiKey || agentCredentials?.accessToken || agentCredentials?.sessionKey;
|
|
1053
|
+
const isOAuthToken = agentCredentials?.oauth?.access || (token && token.startsWith('sk-ant-oat'));
|
|
1054
|
+
|
|
1055
|
+
if (isOAuthToken) {
|
|
1056
|
+
// Use OAuth endpoint with Bearer token
|
|
1057
|
+
const accessToken = agentCredentials?.oauth?.access || token;
|
|
1058
|
+
models = await fetchAnthropicModelsOAuth(accessToken);
|
|
1059
|
+
} else {
|
|
1060
|
+
// Standard API key
|
|
1061
|
+
models = await fetchAnthropicModels(token);
|
|
1062
|
+
}
|
|
1183
1063
|
} else {
|
|
1184
1064
|
// OpenAI-compatible providers
|
|
1185
1065
|
const endpoint = agentCredentials?.endpoint || agent.provider?.endpoint;
|
|
@@ -1193,9 +1073,9 @@ const selectModel = async (agent) => {
|
|
|
1193
1073
|
|
|
1194
1074
|
if (!models || models.length === 0) {
|
|
1195
1075
|
console.log(makeLine(chalk.red('COULD NOT FETCH MODELS FROM API')));
|
|
1196
|
-
console.log(makeLine(chalk.
|
|
1076
|
+
console.log(makeLine(chalk.white('Check your API key or network connection.')));
|
|
1197
1077
|
console.log(makeLine(''));
|
|
1198
|
-
console.log(makeLine(chalk.
|
|
1078
|
+
console.log(makeLine(chalk.white('[<] BACK')));
|
|
1199
1079
|
drawBoxFooter(boxWidth);
|
|
1200
1080
|
|
|
1201
1081
|
await prompts.waitForEnter();
|
|
@@ -1205,15 +1085,46 @@ const selectModel = async (agent) => {
|
|
|
1205
1085
|
// Sort models (newest first typically)
|
|
1206
1086
|
models.sort((a, b) => b.localeCompare(a));
|
|
1207
1087
|
|
|
1208
|
-
// Display models
|
|
1209
|
-
models.
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1088
|
+
// Display models in 2 columns
|
|
1089
|
+
const rows = Math.ceil(models.length / 2);
|
|
1090
|
+
const colWidth = Math.floor((W - 4) / 2);
|
|
1091
|
+
|
|
1092
|
+
for (let i = 0; i < rows; i++) {
|
|
1093
|
+
const leftIndex = i;
|
|
1094
|
+
const rightIndex = i + rows;
|
|
1095
|
+
|
|
1096
|
+
// Left column
|
|
1097
|
+
const leftModel = models[leftIndex];
|
|
1098
|
+
const leftNum = chalk.cyan(`[${leftIndex + 1}]`);
|
|
1099
|
+
const leftCurrent = leftModel === agent.model ? chalk.green(' *') : '';
|
|
1100
|
+
const leftName = leftModel.length > colWidth - 8
|
|
1101
|
+
? leftModel.substring(0, colWidth - 11) + '...'
|
|
1102
|
+
: leftModel;
|
|
1103
|
+
const leftText = `${leftNum} ${chalk.yellow(leftName)}${leftCurrent}`;
|
|
1104
|
+
const leftPlain = `[${leftIndex + 1}] ${leftName}${leftModel === agent.model ? ' *' : ''}`;
|
|
1105
|
+
|
|
1106
|
+
// Right column (if exists)
|
|
1107
|
+
let rightText = '';
|
|
1108
|
+
let rightPlain = '';
|
|
1109
|
+
if (rightIndex < models.length) {
|
|
1110
|
+
const rightModel = models[rightIndex];
|
|
1111
|
+
const rightNum = chalk.cyan(`[${rightIndex + 1}]`);
|
|
1112
|
+
const rightCurrent = rightModel === agent.model ? chalk.green(' *') : '';
|
|
1113
|
+
const rightName = rightModel.length > colWidth - 8
|
|
1114
|
+
? rightModel.substring(0, colWidth - 11) + '...'
|
|
1115
|
+
: rightModel;
|
|
1116
|
+
rightText = `${rightNum} ${chalk.yellow(rightName)}${rightCurrent}`;
|
|
1117
|
+
rightPlain = `[${rightIndex + 1}] ${rightName}${rightModel === agent.model ? ' *' : ''}`;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// Pad left column and combine
|
|
1121
|
+
const leftPadding = colWidth - leftPlain.length;
|
|
1122
|
+
const line = leftText + ' '.repeat(Math.max(2, leftPadding)) + rightText;
|
|
1123
|
+
console.log(makeLine(line));
|
|
1124
|
+
}
|
|
1214
1125
|
|
|
1215
1126
|
console.log(makeLine(''));
|
|
1216
|
-
console.log(makeLine(chalk.
|
|
1127
|
+
console.log(makeLine(chalk.white('[<] BACK') + chalk.white(' * = CURRENT')));
|
|
1217
1128
|
|
|
1218
1129
|
drawBoxFooter(boxWidth);
|
|
1219
1130
|
|