hedgequantx 2.6.163 → 2.7.0

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.
Files changed (146) hide show
  1. package/README.md +15 -88
  2. package/bin/cli.js +0 -11
  3. package/dist/lib/api.jsc +0 -0
  4. package/dist/lib/api2.jsc +0 -0
  5. package/dist/lib/core.jsc +0 -0
  6. package/dist/lib/core2.jsc +0 -0
  7. package/dist/lib/data.js +1 -1
  8. package/dist/lib/data.jsc +0 -0
  9. package/dist/lib/data2.jsc +0 -0
  10. package/dist/lib/decoder.jsc +0 -0
  11. package/dist/lib/m/mod1.jsc +0 -0
  12. package/dist/lib/m/mod2.jsc +0 -0
  13. package/dist/lib/n/r1.jsc +0 -0
  14. package/dist/lib/n/r2.jsc +0 -0
  15. package/dist/lib/n/r3.jsc +0 -0
  16. package/dist/lib/n/r4.jsc +0 -0
  17. package/dist/lib/n/r5.jsc +0 -0
  18. package/dist/lib/n/r6.jsc +0 -0
  19. package/dist/lib/n/r7.jsc +0 -0
  20. package/dist/lib/o/util1.jsc +0 -0
  21. package/dist/lib/o/util2.jsc +0 -0
  22. package/package.json +6 -3
  23. package/src/app.js +40 -162
  24. package/src/config/constants.js +31 -33
  25. package/src/config/propfirms.js +13 -217
  26. package/src/config/settings.js +0 -43
  27. package/src/lib/api.js +198 -0
  28. package/src/lib/api2.js +353 -0
  29. package/src/lib/core.js +539 -0
  30. package/src/lib/core2.js +341 -0
  31. package/src/lib/data.js +555 -0
  32. package/src/lib/data2.js +492 -0
  33. package/src/lib/decoder.js +599 -0
  34. package/src/lib/m/s1.js +804 -0
  35. package/src/lib/m/s2.js +34 -0
  36. package/src/lib/n/r1.js +454 -0
  37. package/src/lib/n/r2.js +514 -0
  38. package/src/lib/n/r3.js +631 -0
  39. package/src/lib/n/r4.js +401 -0
  40. package/src/lib/n/r5.js +335 -0
  41. package/src/lib/n/r6.js +425 -0
  42. package/src/lib/n/r7.js +530 -0
  43. package/src/lib/o/l1.js +44 -0
  44. package/src/lib/o/l2.js +427 -0
  45. package/src/lib/python-bridge.js +206 -0
  46. package/src/menus/connect.js +14 -176
  47. package/src/menus/dashboard.js +65 -110
  48. package/src/pages/accounts.js +18 -18
  49. package/src/pages/algo/copy-trading.js +210 -240
  50. package/src/pages/algo/index.js +41 -104
  51. package/src/pages/algo/one-account.js +386 -33
  52. package/src/pages/algo/ui.js +312 -151
  53. package/src/pages/orders.js +3 -3
  54. package/src/pages/positions.js +3 -3
  55. package/src/pages/stats/chart.js +74 -0
  56. package/src/pages/stats/display.js +228 -0
  57. package/src/pages/stats/index.js +236 -0
  58. package/src/pages/stats/metrics.js +213 -0
  59. package/src/pages/user.js +6 -6
  60. package/src/services/hqx-server/constants.js +55 -0
  61. package/src/services/hqx-server/index.js +401 -0
  62. package/src/services/hqx-server/latency.js +81 -0
  63. package/src/services/index.js +12 -3
  64. package/src/services/rithmic/accounts.js +7 -32
  65. package/src/services/rithmic/connection.js +1 -204
  66. package/src/services/rithmic/contracts.js +116 -99
  67. package/src/services/rithmic/handlers.js +21 -196
  68. package/src/services/rithmic/index.js +63 -120
  69. package/src/services/rithmic/market.js +31 -0
  70. package/src/services/rithmic/orders.js +5 -111
  71. package/src/services/rithmic/protobuf.js +384 -138
  72. package/src/services/session.js +22 -173
  73. package/src/ui/box.js +10 -18
  74. package/src/ui/index.js +1 -3
  75. package/src/ui/menu.js +1 -1
  76. package/src/utils/prompts.js +2 -2
  77. package/dist/lib/m/s1.js +0 -1
  78. package/src/menus/ai-agent-connect.js +0 -181
  79. package/src/menus/ai-agent-models.js +0 -219
  80. package/src/menus/ai-agent-oauth.js +0 -292
  81. package/src/menus/ai-agent-ui.js +0 -141
  82. package/src/menus/ai-agent.js +0 -484
  83. package/src/pages/algo/algo-config.js +0 -195
  84. package/src/pages/algo/algo-multi.js +0 -801
  85. package/src/pages/algo/algo-utils.js +0 -58
  86. package/src/pages/algo/copy-engine.js +0 -449
  87. package/src/pages/algo/custom-strategy.js +0 -459
  88. package/src/pages/algo/logger.js +0 -245
  89. package/src/pages/algo/smart-logs-data.js +0 -218
  90. package/src/pages/algo/smart-logs.js +0 -387
  91. package/src/pages/algo/ui-constants.js +0 -144
  92. package/src/pages/algo/ui-summary.js +0 -184
  93. package/src/pages/stats-calculations.js +0 -191
  94. package/src/pages/stats-ui.js +0 -381
  95. package/src/pages/stats.js +0 -339
  96. package/src/services/ai/client-analysis.js +0 -194
  97. package/src/services/ai/client-models.js +0 -333
  98. package/src/services/ai/client.js +0 -343
  99. package/src/services/ai/index.js +0 -384
  100. package/src/services/ai/oauth-anthropic.js +0 -265
  101. package/src/services/ai/oauth-gemini.js +0 -223
  102. package/src/services/ai/oauth-iflow.js +0 -269
  103. package/src/services/ai/oauth-openai.js +0 -233
  104. package/src/services/ai/oauth-qwen.js +0 -279
  105. package/src/services/ai/providers/direct-providers.js +0 -323
  106. package/src/services/ai/providers/index.js +0 -62
  107. package/src/services/ai/providers/other-providers.js +0 -104
  108. package/src/services/ai/proxy-install.js +0 -249
  109. package/src/services/ai/proxy-manager.js +0 -494
  110. package/src/services/ai/proxy-remote.js +0 -161
  111. package/src/services/ai/strategy-supervisor.js +0 -1312
  112. package/src/services/ai/supervisor-data.js +0 -195
  113. package/src/services/ai/supervisor-optimize.js +0 -215
  114. package/src/services/ai/supervisor-sync.js +0 -178
  115. package/src/services/ai/supervisor-utils.js +0 -158
  116. package/src/services/ai/supervisor.js +0 -484
  117. package/src/services/ai/validation.js +0 -250
  118. package/src/services/hqx-server-events.js +0 -110
  119. package/src/services/hqx-server-handlers.js +0 -217
  120. package/src/services/hqx-server-latency.js +0 -136
  121. package/src/services/hqx-server.js +0 -403
  122. package/src/services/position-constants.js +0 -28
  123. package/src/services/position-exit-logic.js +0 -174
  124. package/src/services/position-manager.js +0 -438
  125. package/src/services/position-momentum.js +0 -206
  126. package/src/services/projectx/accounts.js +0 -142
  127. package/src/services/projectx/index.js +0 -443
  128. package/src/services/projectx/market.js +0 -172
  129. package/src/services/projectx/stats.js +0 -110
  130. package/src/services/projectx/trading.js +0 -180
  131. package/src/services/rithmic/latency-tracker.js +0 -182
  132. package/src/services/rithmic/market-data-decoders.js +0 -229
  133. package/src/services/rithmic/market-data.js +0 -272
  134. package/src/services/rithmic/orders-fast.js +0 -246
  135. package/src/services/rithmic/proto-decoders.js +0 -403
  136. package/src/services/rithmic/specs.js +0 -146
  137. package/src/services/rithmic/trade-history.js +0 -254
  138. package/src/services/session-history.js +0 -475
  139. package/src/services/strategy/hft-signal-calc.js +0 -147
  140. package/src/services/strategy/hft-tick.js +0 -407
  141. package/src/services/strategy/recovery-math.js +0 -402
  142. package/src/services/tradovate/constants.js +0 -109
  143. package/src/services/tradovate/index.js +0 -392
  144. package/src/services/tradovate/market.js +0 -47
  145. package/src/services/tradovate/orders.js +0 -145
  146. package/src/services/tradovate/websocket.js +0 -97
@@ -1,141 +0,0 @@
1
- /**
2
- * AI Agent UI Helpers
3
- * Shared UI functions for AI agent menu
4
- */
5
-
6
- const chalk = require('chalk');
7
- const { getLogoWidth } = require('../ui');
8
-
9
- /**
10
- * Create a line with padding for box display
11
- * @param {number} W - Box inner width
12
- * @param {string} content - Content to display
13
- * @param {string} align - Alignment: 'left' or 'center'
14
- * @returns {string} Formatted line
15
- */
16
- const makeLine = (W, content, align = 'left') => {
17
- const plainLen = content.replace(/\x1b\[[0-9;]*m/g, '').length;
18
- const padding = W - plainLen;
19
- if (align === 'center') {
20
- const leftPad = Math.floor(padding / 2);
21
- return chalk.cyan('║') + ' '.repeat(leftPad) + content + ' '.repeat(padding - leftPad) + chalk.cyan('║');
22
- }
23
- return chalk.cyan('║') + ' ' + content + ' '.repeat(Math.max(0, padding - 1)) + chalk.cyan('║');
24
- };
25
-
26
- /**
27
- * Create a 2-column row for box display
28
- * @param {number} W - Box inner width
29
- * @param {string} left - Left column content
30
- * @param {string} right - Right column content
31
- * @returns {string} Formatted line
32
- */
33
- const make2ColRow = (W, left, right) => {
34
- const col1Width = Math.floor(W / 2);
35
- const leftPlain = left.replace(/\x1b\[[0-9;]*m/g, '').length;
36
- const rightPlain = right.replace(/\x1b\[[0-9;]*m/g, '').length;
37
- const leftPadded = ' ' + left + ' '.repeat(Math.max(0, col1Width - leftPlain - 1));
38
- const rightPadded = right + ' '.repeat(Math.max(0, W - col1Width - rightPlain));
39
- return chalk.cyan('║') + leftPadded + rightPadded + chalk.cyan('║');
40
- };
41
-
42
- /**
43
- * Create a menu row with 2 columns centered
44
- * @param {number} W - Box inner width
45
- * @param {string} col1 - First column content
46
- * @param {string} col2 - Second column content
47
- * @returns {string} Formatted line
48
- */
49
- const menuRow2 = (W, col1, col2 = '') => {
50
- const colWidth = Math.floor(W / 2);
51
- const c1Plain = col1.replace(/\x1b\[[0-9;]*m/g, '');
52
- const c2Plain = col2.replace(/\x1b\[[0-9;]*m/g, '');
53
-
54
- const pad1Left = Math.floor((colWidth - c1Plain.length) / 2);
55
- const pad1Right = colWidth - c1Plain.length - pad1Left;
56
-
57
- const col2Width = W - colWidth;
58
- const pad2Left = Math.floor((col2Width - c2Plain.length) / 2);
59
- const pad2Right = col2Width - c2Plain.length - pad2Left;
60
-
61
- const line =
62
- ' '.repeat(pad1Left) + col1 + ' '.repeat(pad1Right) +
63
- ' '.repeat(pad2Left) + col2 + ' '.repeat(pad2Right);
64
-
65
- return chalk.cyan('║') + line + chalk.cyan('║');
66
- };
67
-
68
- /**
69
- * Create a menu item with key and label
70
- * @param {string} key - Key to press
71
- * @param {string} label - Label to display
72
- * @param {Function} color - Chalk color function
73
- * @returns {string} Formatted menu item
74
- */
75
- const menuItem = (key, label, color) => {
76
- const text = `[${key}] ${label.padEnd(14)}`;
77
- return color(text);
78
- };
79
-
80
- /**
81
- * Get provider color based on provider ID
82
- * @param {string} providerId - Provider identifier
83
- * @returns {Function} Chalk color function
84
- */
85
- const getProviderColor = (providerId) => {
86
- if (providerId === 'anthropic') return chalk.magenta;
87
- if (providerId === 'openai') return chalk.green;
88
- if (providerId === 'openrouter') return chalk.yellow;
89
- return chalk.cyan;
90
- };
91
-
92
- /**
93
- * Get box dimensions
94
- * @returns {Object} { boxWidth, W }
95
- */
96
- const getBoxDimensions = () => {
97
- const boxWidth = getLogoWidth();
98
- const W = boxWidth - 2;
99
- return { boxWidth, W };
100
- };
101
-
102
- /**
103
- * Open URL in default browser
104
- * @param {string} url - URL to open
105
- * @returns {Promise<boolean>} true if browser opened, false if failed
106
- */
107
- const openBrowser = (url) => {
108
- return new Promise((resolve) => {
109
- const { exec } = require('child_process');
110
- const platform = process.platform;
111
-
112
- let cmd;
113
- if (platform === 'darwin') cmd = `open "${url}"`;
114
- else if (platform === 'win32') cmd = `start "" "${url}"`;
115
- else cmd = `xdg-open "${url}"`;
116
-
117
- exec(cmd, (err) => {
118
- resolve(!err);
119
- });
120
- });
121
- };
122
-
123
- /**
124
- * Check if running on remote/VPS (SSH)
125
- * @returns {boolean} true if remote
126
- */
127
- const isRemoteEnvironment = () => {
128
- return process.env.SSH_CONNECTION || process.env.SSH_CLIENT ||
129
- (process.env.DISPLAY === undefined && process.platform === 'linux');
130
- };
131
-
132
- module.exports = {
133
- makeLine,
134
- make2ColRow,
135
- menuRow2,
136
- menuItem,
137
- getProviderColor,
138
- getBoxDimensions,
139
- openBrowser,
140
- isRemoteEnvironment
141
- };
@@ -1,484 +0,0 @@
1
- /** AI Agent Menu - Configure multiple AI provider connections */
2
- const chalk = require('chalk');
3
- const { getLogoWidth, drawBoxHeaderContinue, drawBoxFooter, displayBanner } = require('../ui');
4
- const { prompts } = require('../utils');
5
- const aiService = require('../services/ai');
6
- const { getCategories, getProvidersByCategory } = require('../services/ai/providers');
7
- const { makeLine, make2ColRow, menuRow2, menuItem, getProviderColor, getBoxDimensions } = require('./ai-agent-ui');
8
- const { setupOAuthConnection, getOAuthConfig } = require('./ai-agent-oauth');
9
- const { selectModelFromList, selectModel } = require('./ai-agent-models');
10
- const { collectCredentials, validateAndFetchModels, addConnectedAgent } = require('./ai-agent-connect');
11
-
12
- /** Main AI Agent menu */
13
- const aiAgentMenu = async () => {
14
- const { boxWidth, W } = getBoxDimensions();
15
-
16
- console.clear();
17
- displayBanner();
18
- drawBoxHeaderContinue('AI AGENTS', boxWidth);
19
-
20
- // Get all connected agents
21
- const agents = aiService.getAgents();
22
- const agentCount = agents.length;
23
-
24
- if (agentCount === 0) {
25
- console.log(makeLine(W, chalk.white('STATUS: NO AGENTS CONNECTED')));
26
- } else {
27
- console.log(makeLine(W, chalk.green(`STATUS: ${agentCount} AGENT${agentCount > 1 ? 'S' : ''} CONNECTED`)));
28
- console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
29
-
30
- // List all agents
31
- for (let i = 0; i < agents.length; i++) {
32
- const agent = agents[i];
33
- const isActive = agent.isActive || agents.length === 1;
34
- const activeMarker = isActive ? ' [ACTIVE]' : '';
35
- const providerColor = getProviderColor(agent.providerId);
36
-
37
- const prefix = `[${i + 1}] `;
38
- const suffix = ` - ${agent.model || 'N/A'}`;
39
- const maxNameLen = W - prefix.length - activeMarker.length - suffix.length - 2;
40
-
41
- const displayName = agent.name.length > maxNameLen
42
- ? agent.name.substring(0, maxNameLen - 3) + '...'
43
- : agent.name;
44
-
45
- console.log(makeLine(W,
46
- chalk.white(prefix) +
47
- providerColor(displayName) +
48
- chalk.green(activeMarker) +
49
- chalk.white(suffix)
50
- ));
51
- }
52
- }
53
-
54
- console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
55
-
56
- // Menu options in 2 columns
57
- if (agentCount > 0) {
58
- if (agentCount > 1) {
59
- console.log(menuRow2(W, menuItem('+', 'ADD AGENT', chalk.green), menuItem('S', 'SET ACTIVE', chalk.cyan)));
60
- console.log(menuRow2(W, menuItem('M', 'CHANGE MODEL', chalk.yellow), menuItem('R', 'REMOVE AGENT', chalk.red)));
61
- console.log(menuRow2(W, menuItem('X', 'REMOVE ALL', chalk.red), menuItem('<', 'BACK', chalk.white)));
62
- } else {
63
- console.log(menuRow2(W, menuItem('+', 'ADD AGENT', chalk.green), menuItem('M', 'CHANGE MODEL', chalk.yellow)));
64
- console.log(menuRow2(W, menuItem('R', 'REMOVE AGENT', chalk.red), menuItem('<', 'BACK', chalk.white)));
65
- }
66
- } else {
67
- console.log(menuRow2(W, menuItem('+', 'ADD AGENT', chalk.green), menuItem('<', 'BACK', chalk.white)));
68
- }
69
-
70
- drawBoxFooter(boxWidth);
71
-
72
- const choice = await prompts.textInput(chalk.cyan('SELECT:'));
73
- const input = (choice || '').toLowerCase();
74
-
75
- // Handle number input (select agent for details)
76
- const num = parseInt(choice);
77
- if (!isNaN(num) && num >= 1 && num <= agentCount) {
78
- return await showAgentDetails(agents[num - 1]);
79
- }
80
-
81
- switch (input) {
82
- case '+':
83
- return await selectCategory();
84
- case 's':
85
- if (agentCount > 1) return await selectActiveAgent();
86
- return await aiAgentMenu();
87
- case 'm':
88
- if (agentCount > 0) return await selectAgentForModelChange();
89
- return await aiAgentMenu();
90
- case 'r':
91
- if (agentCount > 0) return await selectAgentToRemove();
92
- return await aiAgentMenu();
93
- case 'x':
94
- if (agentCount > 1) {
95
- aiService.disconnectAll();
96
- console.log(chalk.yellow('\n ALL AGENTS REMOVED'));
97
- await prompts.waitForEnter();
98
- }
99
- return await aiAgentMenu();
100
- case '<':
101
- case 'b':
102
- return;
103
- default:
104
- return await aiAgentMenu();
105
- }
106
- };
107
-
108
- /** Show agent details */
109
- const showAgentDetails = async (agent) => {
110
- const { boxWidth, W } = getBoxDimensions();
111
-
112
- console.clear();
113
- displayBanner();
114
- drawBoxHeaderContinue('AGENT DETAILS', boxWidth);
115
-
116
- const providerColor = getProviderColor(agent.providerId);
117
-
118
- console.log(makeLine(W, chalk.white('NAME: ') + providerColor(agent.name)));
119
- console.log(makeLine(W, chalk.white('PROVIDER: ') + chalk.white(agent.provider?.name || agent.providerId)));
120
- console.log(makeLine(W, chalk.white('MODEL: ') + chalk.white(agent.model || 'N/A')));
121
- console.log(makeLine(W, chalk.white('STATUS: ') + (agent.isActive ? chalk.green('ACTIVE') : chalk.white('STANDBY'))));
122
-
123
- console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
124
-
125
- if (!agent.isActive) {
126
- console.log(menuRow2(W, chalk.cyan('[A] SET AS ACTIVE'), chalk.yellow('[M] CHANGE MODEL')));
127
- console.log(menuRow2(W, chalk.red('[R] REMOVE'), chalk.white('[<] BACK')));
128
- } else {
129
- console.log(menuRow2(W, chalk.yellow('[M] CHANGE MODEL'), chalk.red('[R] REMOVE')));
130
- console.log(menuRow2(W, chalk.white('[<] BACK'), ''));
131
- }
132
-
133
- drawBoxFooter(boxWidth);
134
-
135
- const choice = await prompts.textInput(chalk.cyan('SELECT:'));
136
- const agentDisplayName = agent.model ? `${agent.name} (${agent.model})` : agent.name;
137
-
138
- switch ((choice || '').toLowerCase()) {
139
- case 'a':
140
- if (!agent.isActive) {
141
- aiService.setActiveAgent(agent.id);
142
- console.log(chalk.green(`\n ${agentDisplayName} IS NOW ACTIVE`));
143
- await prompts.waitForEnter();
144
- }
145
- return await aiAgentMenu();
146
- case 'm':
147
- return await selectModel(agent, aiAgentMenu);
148
- case 'r':
149
- aiService.removeAgent(agent.id);
150
- console.log(chalk.yellow(`\n ${agentDisplayName} REMOVED`));
151
- await prompts.waitForEnter();
152
- return await aiAgentMenu();
153
- case '<':
154
- case 'b':
155
- return await aiAgentMenu();
156
- default:
157
- return await aiAgentMenu();
158
- }
159
- };
160
-
161
- /** Select active agent */
162
- const selectActiveAgent = async () => {
163
- const { boxWidth, W } = getBoxDimensions();
164
-
165
- console.clear();
166
- displayBanner();
167
- drawBoxHeaderContinue('SET ACTIVE AGENT', boxWidth);
168
-
169
- const agents = aiService.getAgents();
170
-
171
- for (let i = 0; i < agents.length; i++) {
172
- const agent = agents[i];
173
- const activeMarker = agent.isActive ? chalk.yellow(' (CURRENT)') : '';
174
- const providerColor = getProviderColor(agent.providerId);
175
- const modelDisplay = agent.model ? chalk.gray(` (${agent.model})`) : '';
176
- console.log(makeLine(W, chalk.white(`[${i + 1}] `) + providerColor(agent.name) + modelDisplay + activeMarker));
177
- }
178
-
179
- console.log(makeLine(W, ''));
180
- console.log(makeLine(W, chalk.white('[<] BACK')));
181
-
182
- drawBoxFooter(boxWidth);
183
-
184
- const choice = await prompts.textInput(chalk.cyan('SELECT AGENT:'));
185
-
186
- if (!choice || choice.trim() === '' || choice === '<' || choice?.toLowerCase() === 'b') {
187
- return await aiAgentMenu();
188
- }
189
-
190
- const index = parseInt(choice) - 1;
191
- if (isNaN(index) || index < 0 || index >= agents.length) {
192
- return await aiAgentMenu();
193
- }
194
-
195
- aiService.setActiveAgent(agents[index].id);
196
- const selectedAgent = agents[index];
197
- const modelInfo = selectedAgent.model ? ` (${selectedAgent.model})` : '';
198
- console.log(chalk.green(`\n ${selectedAgent.name}${modelInfo} IS NOW ACTIVE`));
199
- await prompts.waitForEnter();
200
- return await aiAgentMenu();
201
- };
202
-
203
- /** Select agent to change model */
204
- const selectAgentForModelChange = async () => {
205
- const agents = aiService.getAgents();
206
-
207
- if (agents.length === 1) {
208
- return await selectModel(agents[0], aiAgentMenu);
209
- }
210
-
211
- const { boxWidth, W } = getBoxDimensions();
212
-
213
- console.clear();
214
- displayBanner();
215
- drawBoxHeaderContinue('SELECT AGENT TO CHANGE MODEL', boxWidth);
216
-
217
- for (let i = 0; i < agents.length; i++) {
218
- const agent = agents[i];
219
- console.log(makeLine(W, chalk.white(`[${i + 1}] `) + chalk.cyan(agent.name) + chalk.white(` - ${agent.model}`)));
220
- }
221
-
222
- console.log(makeLine(W, ''));
223
- console.log(makeLine(W, chalk.white('[<] BACK')));
224
-
225
- drawBoxFooter(boxWidth);
226
-
227
- const choice = await prompts.textInput(chalk.cyan('SELECT AGENT:'));
228
-
229
- if (!choice || choice.trim() === '' || choice === '<' || choice?.toLowerCase() === 'b') {
230
- return await aiAgentMenu();
231
- }
232
-
233
- const index = parseInt(choice) - 1;
234
- if (isNaN(index) || index < 0 || index >= agents.length) {
235
- return await aiAgentMenu();
236
- }
237
-
238
- return await selectModel(agents[index], aiAgentMenu);
239
- };
240
-
241
- /** Select agent to remove */
242
- const selectAgentToRemove = async () => {
243
- const agents = aiService.getAgents();
244
-
245
- if (agents.length === 1) {
246
- aiService.removeAgent(agents[0].id);
247
- console.log(chalk.yellow(`\n ${agents[0].name} REMOVED`));
248
- await prompts.waitForEnter();
249
- return await aiAgentMenu();
250
- }
251
-
252
- const { boxWidth, W } = getBoxDimensions();
253
-
254
- console.clear();
255
- displayBanner();
256
- drawBoxHeaderContinue('SELECT AGENT TO REMOVE', boxWidth);
257
-
258
- for (let i = 0; i < agents.length; i++) {
259
- const agent = agents[i];
260
- const modelDisplay = agent.model ? chalk.gray(` (${agent.model})`) : '';
261
- console.log(makeLine(W, chalk.white(`[${i + 1}] `) + chalk.red(agent.name) + modelDisplay));
262
- }
263
-
264
- console.log(makeLine(W, ''));
265
- console.log(makeLine(W, chalk.white('[<] BACK')));
266
-
267
- drawBoxFooter(boxWidth);
268
-
269
- const choice = await prompts.textInput(chalk.cyan('SELECT AGENT TO REMOVE:'));
270
-
271
- if (!choice || choice.trim() === '' || choice === '<' || choice?.toLowerCase() === 'b') {
272
- return await aiAgentMenu();
273
- }
274
-
275
- const index = parseInt(choice) - 1;
276
- if (isNaN(index) || index < 0 || index >= agents.length) {
277
- return await aiAgentMenu();
278
- }
279
-
280
- const removedAgent = agents[index];
281
- const modelInfo = removedAgent.model ? ` (${removedAgent.model})` : '';
282
- aiService.removeAgent(removedAgent.id);
283
- console.log(chalk.yellow(`\n ${removedAgent.name}${modelInfo} REMOVED`));
284
- await prompts.waitForEnter();
285
- return await aiAgentMenu();
286
- };
287
-
288
- /** Select provider category */
289
- const selectCategory = async () => {
290
- const { boxWidth, W } = getBoxDimensions();
291
-
292
- console.clear();
293
- displayBanner();
294
- drawBoxHeaderContinue('SELECT PROVIDER TYPE', boxWidth);
295
-
296
- const categories = getCategories();
297
-
298
- console.log(make2ColRow(W,
299
- chalk.cyan('[1]') + chalk.yellow(' UNIFIED (RECOMMENDED)'),
300
- chalk.cyan('[2]') + chalk.yellow(' DIRECT PROVIDERS')
301
- ));
302
- console.log(make2ColRow(W, chalk.white(' 1 API = 100+ models'), chalk.white(' Connect to each provider')));
303
- console.log(makeLine(W, ''));
304
- console.log(make2ColRow(W,
305
- chalk.cyan('[3]') + chalk.yellow(' LOCAL (FREE)'),
306
- chalk.cyan('[4]') + chalk.yellow(' CUSTOM')
307
- ));
308
- console.log(make2ColRow(W, chalk.white(' Run on your machine'), chalk.white(' Self-hosted solutions')));
309
-
310
- drawBoxFooter(boxWidth);
311
-
312
- const choice = await prompts.textInput(chalk.cyan('SELECT (1-4):'));
313
-
314
- if (!choice || choice.trim() === '' || choice === '<' || choice?.toLowerCase() === 'b') {
315
- return await aiAgentMenu();
316
- }
317
-
318
- const index = parseInt(choice) - 1;
319
- if (isNaN(index) || index < 0 || index >= categories.length) {
320
- return await aiAgentMenu();
321
- }
322
-
323
- return await selectProvider(categories[index].id);
324
- };
325
-
326
- /** Select AI provider from category */
327
- const selectProvider = async (categoryId) => {
328
- const { boxWidth, W } = getBoxDimensions();
329
- const col1Width = Math.floor(W / 2);
330
-
331
- console.clear();
332
- displayBanner();
333
-
334
- const categories = getCategories();
335
- const category = categories.find(c => c.id === categoryId);
336
- drawBoxHeaderContinue(category.name, boxWidth);
337
-
338
- const providers = getProvidersByCategory(categoryId);
339
-
340
- if (providers.length === 0) {
341
- console.log(makeLine(W, chalk.white('NO PROVIDERS IN THIS CATEGORY')));
342
- drawBoxFooter(boxWidth);
343
- await prompts.waitForEnter();
344
- return await selectCategory();
345
- }
346
-
347
- // Display providers in 2 columns
348
- for (let i = 0; i < providers.length; i += 2) {
349
- const left = providers[i];
350
- const right = providers[i + 1];
351
-
352
- const leftNum = `[${i + 1}]`;
353
- const rightNum = right ? `[${i + 2}]` : '';
354
- const leftName = ` ${left.name}`;
355
- const rightName = right ? ` ${right.name}` : '';
356
-
357
- console.log(make2ColRow(W,
358
- chalk.cyan(leftNum) + chalk.yellow(leftName.length > col1Width - leftNum.length - 3 ? leftName.substring(0, col1Width - leftNum.length - 6) + '...' : leftName),
359
- right ? chalk.cyan(rightNum) + chalk.yellow(rightName.length > col1Width - rightNum.length - 3 ? rightName.substring(0, col1Width - rightNum.length - 6) + '...' : rightName) : ''
360
- ));
361
-
362
- const leftDesc = ' ' + left.description;
363
- const rightDesc = right ? ' ' + right.description : '';
364
-
365
- console.log(make2ColRow(W,
366
- chalk.white(leftDesc.length > col1Width - 3 ? leftDesc.substring(0, col1Width - 6) + '...' : leftDesc),
367
- chalk.white(rightDesc.length > col1Width - 3 ? rightDesc.substring(0, col1Width - 6) + '...' : rightDesc)
368
- ));
369
-
370
- console.log(makeLine(W, ''));
371
- }
372
-
373
- drawBoxFooter(boxWidth);
374
-
375
- const choice = await prompts.textInput(chalk.cyan(`SELECT (1-${providers.length}):`));
376
-
377
- if (!choice || choice.trim() === '' || choice === '<' || choice?.toLowerCase() === 'b') {
378
- return await selectCategory();
379
- }
380
-
381
- const index = parseInt(choice) - 1;
382
- if (isNaN(index) || index < 0 || index >= providers.length) {
383
- return await selectCategory();
384
- }
385
-
386
- return await selectProviderOption(providers[index]);
387
- };
388
-
389
- /** Select connection option for provider */
390
- const selectProviderOption = async (provider) => {
391
- const { boxWidth, W } = getBoxDimensions();
392
- const col1Width = Math.floor(W / 2);
393
-
394
- // If only one option, skip selection
395
- if (provider.options.length === 1) {
396
- return await setupConnection(provider, provider.options[0]);
397
- }
398
-
399
- console.clear();
400
- displayBanner();
401
- drawBoxHeaderContinue(provider.name, boxWidth);
402
-
403
- console.log(makeLine(W, chalk.white('SELECT CONNECTION METHOD:')));
404
- console.log(makeLine(W, ''));
405
-
406
- // Display options in 2 columns
407
- for (let i = 0; i < provider.options.length; i += 2) {
408
- const left = provider.options[i];
409
- const right = provider.options[i + 1];
410
-
411
- console.log(make2ColRow(W,
412
- chalk.cyan(`[${i + 1}]`) + chalk.yellow(` ${left.label}`),
413
- right ? chalk.cyan(`[${i + 2}]`) + chalk.yellow(` ${right.label}`) : ''
414
- ));
415
-
416
- const leftDesc1 = left.description[0] ? ' ' + left.description[0] : '';
417
- const rightDesc1 = right?.description[0] ? ' ' + right.description[0] : '';
418
- console.log(make2ColRow(W,
419
- chalk.white(leftDesc1.length > col1Width - 2 ? leftDesc1.substring(0, col1Width - 5) + '...' : leftDesc1),
420
- chalk.white(rightDesc1.length > col1Width - 2 ? rightDesc1.substring(0, col1Width - 5) + '...' : rightDesc1)
421
- ));
422
-
423
- const leftDesc2 = left.description[1] ? ' ' + left.description[1] : '';
424
- const rightDesc2 = right?.description[1] ? ' ' + right.description[1] : '';
425
- if (leftDesc2 || rightDesc2) {
426
- console.log(make2ColRow(W,
427
- chalk.white(leftDesc2.length > col1Width - 2 ? leftDesc2.substring(0, col1Width - 5) + '...' : leftDesc2),
428
- chalk.white(rightDesc2.length > col1Width - 2 ? rightDesc2.substring(0, col1Width - 5) + '...' : rightDesc2)
429
- ));
430
- }
431
-
432
- console.log(makeLine(W, ''));
433
- }
434
-
435
- drawBoxFooter(boxWidth);
436
-
437
- const choice = await prompts.textInput(chalk.cyan('SELECT:'));
438
-
439
- if (!choice || choice.trim() === '' || choice === '<' || choice?.toLowerCase() === 'b') {
440
- return await selectProvider(provider.category);
441
- }
442
-
443
- const index = parseInt(choice) - 1;
444
- if (isNaN(index) || index < 0 || index >= provider.options.length) {
445
- return await selectProvider(provider.category);
446
- }
447
-
448
- return await setupConnection(provider, provider.options[index]);
449
- };
450
-
451
- /** Setup connection with credentials */
452
- const setupConnection = async (provider, option) => {
453
- // Handle OAuth flow separately
454
- if (option.authType === 'oauth') {
455
- return await setupOAuthConnection(provider, selectProviderOption, selectModelFromList, aiAgentMenu);
456
- }
457
-
458
- // Collect credentials
459
- const credentials = await collectCredentials(provider, option);
460
- if (!credentials) {
461
- return await selectProviderOption(provider);
462
- }
463
-
464
- // Validate and fetch models
465
- const result = await validateAndFetchModels(provider, option, credentials);
466
- if (!result.valid) {
467
- await prompts.waitForEnter();
468
- return await selectProviderOption(provider);
469
- }
470
-
471
- // Let user select a model
472
- const selectedModel = await selectModelFromList(result.models, provider.name);
473
- if (!selectedModel) {
474
- return await selectProviderOption(provider);
475
- }
476
-
477
- // Add as new agent with selected model
478
- await addConnectedAgent(provider, option, credentials, selectedModel);
479
-
480
- await prompts.waitForEnter();
481
- return await aiAgentMenu();
482
- };
483
-
484
- module.exports = { aiAgentMenu };