hedgequantx 2.6.161 → 2.6.163

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 (57) hide show
  1. package/package.json +1 -1
  2. package/src/menus/ai-agent-connect.js +181 -0
  3. package/src/menus/ai-agent-models.js +219 -0
  4. package/src/menus/ai-agent-oauth.js +292 -0
  5. package/src/menus/ai-agent-ui.js +141 -0
  6. package/src/menus/ai-agent.js +88 -1489
  7. package/src/pages/algo/copy-engine.js +449 -0
  8. package/src/pages/algo/copy-trading.js +11 -543
  9. package/src/pages/algo/smart-logs-data.js +218 -0
  10. package/src/pages/algo/smart-logs.js +9 -214
  11. package/src/pages/algo/ui-constants.js +144 -0
  12. package/src/pages/algo/ui-summary.js +184 -0
  13. package/src/pages/algo/ui.js +42 -526
  14. package/src/pages/stats-calculations.js +191 -0
  15. package/src/pages/stats-ui.js +381 -0
  16. package/src/pages/stats.js +14 -507
  17. package/src/services/ai/client-analysis.js +194 -0
  18. package/src/services/ai/client-models.js +333 -0
  19. package/src/services/ai/client.js +6 -489
  20. package/src/services/ai/index.js +2 -257
  21. package/src/services/ai/providers/direct-providers.js +323 -0
  22. package/src/services/ai/providers/index.js +8 -472
  23. package/src/services/ai/providers/other-providers.js +104 -0
  24. package/src/services/ai/proxy-install.js +249 -0
  25. package/src/services/ai/proxy-manager.js +29 -411
  26. package/src/services/ai/proxy-remote.js +161 -0
  27. package/src/services/ai/supervisor-optimize.js +215 -0
  28. package/src/services/ai/supervisor-sync.js +178 -0
  29. package/src/services/ai/supervisor.js +50 -515
  30. package/src/services/ai/validation.js +250 -0
  31. package/src/services/hqx-server-events.js +110 -0
  32. package/src/services/hqx-server-handlers.js +217 -0
  33. package/src/services/hqx-server-latency.js +136 -0
  34. package/src/services/hqx-server.js +51 -403
  35. package/src/services/position-constants.js +28 -0
  36. package/src/services/position-exit-logic.js +174 -0
  37. package/src/services/position-manager.js +90 -629
  38. package/src/services/position-momentum.js +206 -0
  39. package/src/services/projectx/accounts.js +142 -0
  40. package/src/services/projectx/index.js +40 -289
  41. package/src/services/projectx/trading.js +180 -0
  42. package/src/services/rithmic/contracts.js +218 -0
  43. package/src/services/rithmic/handlers.js +2 -208
  44. package/src/services/rithmic/index.js +28 -712
  45. package/src/services/rithmic/latency-tracker.js +182 -0
  46. package/src/services/rithmic/market-data-decoders.js +229 -0
  47. package/src/services/rithmic/market-data.js +1 -278
  48. package/src/services/rithmic/orders-fast.js +246 -0
  49. package/src/services/rithmic/orders.js +1 -251
  50. package/src/services/rithmic/proto-decoders.js +403 -0
  51. package/src/services/rithmic/protobuf.js +7 -443
  52. package/src/services/rithmic/specs.js +146 -0
  53. package/src/services/rithmic/trade-history.js +254 -0
  54. package/src/services/strategy/hft-signal-calc.js +147 -0
  55. package/src/services/strategy/hft-tick.js +33 -133
  56. package/src/services/tradovate/index.js +6 -119
  57. package/src/services/tradovate/orders.js +145 -0
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * CLIProxyAPI Manager
3
3
  *
4
- * Automatically downloads, installs, and manages CLIProxyAPI
5
- * so users can connect their AI subscription accounts (ChatGPT Plus, Claude Pro, etc.)
6
- * without needing to understand technical details.
4
+ * Automatically manages CLIProxyAPI for connecting AI subscription accounts
5
+ * (ChatGPT Plus, Claude Pro, etc.) without needing API keys.
7
6
  *
8
7
  * Flow:
9
8
  * 1. User selects "Connect Account" in CLI
@@ -12,54 +11,32 @@
12
11
  * 4. Callback to localhost → Token saved → Models available
13
12
  */
14
13
 
15
- const https = require('https');
16
14
  const http = require('http');
17
- const fs = require('fs');
18
- const path = require('path');
19
- const os = require('os');
20
15
  const { spawn, exec } = require('child_process');
21
16
 
22
- // Configuration
23
- const PROXY_VERSION = 'v6.6.84';
24
- const PROXY_PORT = 8317;
25
- const PROXY_DIR = path.join(os.homedir(), '.hqx', 'proxy');
26
- const PROXY_BIN = path.join(PROXY_DIR, process.platform === 'win32' ? 'cli-proxy-api.exe' : 'cli-proxy-api');
27
- const PROXY_CONFIG = path.join(PROXY_DIR, 'config.yaml');
28
- const PROXY_AUTH_DIR = path.join(PROXY_DIR, 'auths');
29
- const API_KEY = 'hqx-local-key';
30
- const MANAGEMENT_KEY = 'hqx-mgmt-key';
31
-
32
- // GitHub release URLs
33
- const getDownloadUrl = () => {
34
- const platform = process.platform;
35
- const arch = process.arch;
36
-
37
- let osName, archName, ext;
38
-
39
- if (platform === 'darwin') {
40
- osName = 'darwin';
41
- archName = arch === 'arm64' ? 'arm64' : 'amd64';
42
- ext = 'tar.gz';
43
- } else if (platform === 'win32') {
44
- osName = 'windows';
45
- archName = arch === 'arm64' ? 'arm64' : 'amd64';
46
- ext = 'zip';
47
- } else {
48
- osName = 'linux';
49
- archName = arch === 'arm64' ? 'arm64' : 'amd64';
50
- ext = 'tar.gz';
51
- }
52
-
53
- const version = PROXY_VERSION.replace('v', '');
54
- return `https://github.com/router-for-me/CLIProxyAPI/releases/download/${PROXY_VERSION}/CLIProxyAPI_${version}_${osName}_${archName}.${ext}`;
55
- };
17
+ // Import from sub-modules
18
+ const {
19
+ PROXY_PORT,
20
+ PROXY_DIR,
21
+ PROXY_BIN,
22
+ PROXY_CONFIG,
23
+ API_KEY,
24
+ MANAGEMENT_KEY,
25
+ isInstalled,
26
+ install,
27
+ isConfigValid,
28
+ rewriteConfig,
29
+ } = require('./proxy-install');
56
30
 
57
- /**
58
- * Check if CLIProxyAPI binary exists
59
- */
60
- const isInstalled = () => {
61
- return fs.existsSync(PROXY_BIN);
62
- };
31
+ const {
32
+ createRemoteSession,
33
+ pollRemoteSession,
34
+ getRemoteTokens,
35
+ waitForRemoteAuth,
36
+ isServerEnvironment,
37
+ canOpenBrowser,
38
+ REMOTE_OAUTH_URL
39
+ } = require('./proxy-remote');
63
40
 
64
41
  /**
65
42
  * Check if CLIProxyAPI is running
@@ -88,140 +65,6 @@ const isRunning = async () => {
88
65
  });
89
66
  };
90
67
 
91
- /**
92
- * Download file from URL
93
- */
94
- const downloadFile = (url, dest) => {
95
- return new Promise((resolve, reject) => {
96
- const file = fs.createWriteStream(dest);
97
-
98
- const request = (url) => {
99
- https.get(url, (res) => {
100
- if (res.statusCode === 302 || res.statusCode === 301) {
101
- // Follow redirect
102
- request(res.headers.location);
103
- return;
104
- }
105
-
106
- if (res.statusCode !== 200) {
107
- reject(new Error(`HTTP ${res.statusCode}`));
108
- return;
109
- }
110
-
111
- res.pipe(file);
112
- file.on('finish', () => {
113
- file.close();
114
- resolve();
115
- });
116
- }).on('error', (err) => {
117
- fs.unlink(dest, () => {});
118
- reject(err);
119
- });
120
- };
121
-
122
- request(url);
123
- });
124
- };
125
-
126
- /**
127
- * Extract tar.gz archive
128
- */
129
- const extractTarGz = (archive, dest) => {
130
- return new Promise((resolve, reject) => {
131
- exec(`tar -xzf "${archive}" -C "${dest}"`, (err) => {
132
- if (err) reject(err);
133
- else resolve();
134
- });
135
- });
136
- };
137
-
138
- /**
139
- * Extract zip archive (Windows)
140
- */
141
- const extractZip = (archive, dest) => {
142
- return new Promise((resolve, reject) => {
143
- exec(`powershell -command "Expand-Archive -Path '${archive}' -DestinationPath '${dest}' -Force"`, (err) => {
144
- if (err) reject(err);
145
- else resolve();
146
- });
147
- });
148
- };
149
-
150
- /**
151
- * Download and install CLIProxyAPI
152
- * @param {Function} onProgress - Progress callback (message)
153
- */
154
- const install = async (onProgress = () => {}) => {
155
- // Create directories
156
- if (!fs.existsSync(PROXY_DIR)) {
157
- fs.mkdirSync(PROXY_DIR, { recursive: true });
158
- }
159
- if (!fs.existsSync(PROXY_AUTH_DIR)) {
160
- fs.mkdirSync(PROXY_AUTH_DIR, { recursive: true });
161
- }
162
-
163
- onProgress('Downloading CLIProxyAPI...');
164
-
165
- const downloadUrl = getDownloadUrl();
166
- const ext = process.platform === 'win32' ? 'zip' : 'tar.gz';
167
- const archivePath = path.join(PROXY_DIR, `cliproxyapi.${ext}`);
168
-
169
- // Download
170
- await downloadFile(downloadUrl, archivePath);
171
-
172
- onProgress('Extracting...');
173
-
174
- // Extract
175
- if (ext === 'zip') {
176
- await extractZip(archivePath, PROXY_DIR);
177
- } else {
178
- await extractTarGz(archivePath, PROXY_DIR);
179
- }
180
-
181
- // Find the binary (it might be in a subdirectory)
182
- const possibleBins = [
183
- path.join(PROXY_DIR, 'cli-proxy-api'),
184
- path.join(PROXY_DIR, 'cli-proxy-api.exe'),
185
- path.join(PROXY_DIR, 'CLIProxyAPI', 'cli-proxy-api'),
186
- path.join(PROXY_DIR, 'CLIProxyAPI', 'cli-proxy-api.exe')
187
- ];
188
-
189
- for (const bin of possibleBins) {
190
- if (fs.existsSync(bin) && bin !== PROXY_BIN) {
191
- fs.renameSync(bin, PROXY_BIN);
192
- break;
193
- }
194
- }
195
-
196
- // Make executable (Unix)
197
- if (process.platform !== 'win32') {
198
- fs.chmodSync(PROXY_BIN, 0o755);
199
- }
200
-
201
- // Create config file
202
- const config = `port: ${PROXY_PORT}
203
- auth-dir: "${PROXY_AUTH_DIR}"
204
- api-keys:
205
- - "${API_KEY}"
206
- remote-management:
207
- secret-key: "${MANAGEMENT_KEY}"
208
- allow-remote-management: false
209
- request-retry: 3
210
- quota-exceeded:
211
- switch-project: true
212
- switch-preview-model: true
213
- `;
214
-
215
- fs.writeFileSync(PROXY_CONFIG, config);
216
-
217
- // Cleanup archive
218
- fs.unlinkSync(archivePath);
219
-
220
- onProgress('Installation complete');
221
-
222
- return true;
223
- };
224
-
225
68
  /**
226
69
  * Start CLIProxyAPI in background
227
70
  */
@@ -243,7 +86,6 @@ const start = async () => {
243
86
 
244
87
  proc.unref();
245
88
 
246
- // Wait for it to start
247
89
  let attempts = 0;
248
90
  const checkInterval = setInterval(async () => {
249
91
  attempts++;
@@ -271,79 +113,19 @@ const stop = async () => {
271
113
  });
272
114
  };
273
115
 
274
- /**
275
- * Check if config has correct management key (plain text, not hashed)
276
- * CLIProxyAPI expects plain text key in config, it does bcrypt hashing internally
277
- * @returns {boolean} true if config is correct, false if needs update
278
- */
279
- const isConfigValid = () => {
280
- if (!fs.existsSync(PROXY_CONFIG)) return false;
281
-
282
- try {
283
- const config = fs.readFileSync(PROXY_CONFIG, 'utf8');
284
-
285
- // Check if management key section exists
286
- if (!config.includes('remote-management:') || !config.includes('secret-key:')) {
287
- return false;
288
- }
289
-
290
- // Check if key is hashed (bcrypt hashes start with $2a$, $2b$, or $2y$)
291
- // Plain text key should be 'hqx-mgmt-key', not a bcrypt hash
292
- if (config.includes('$2a$') || config.includes('$2b$') || config.includes('$2y$')) {
293
- return false; // Config has hashed key, needs to be plain text
294
- }
295
-
296
- // Check if it has our expected management key
297
- if (!config.includes(MANAGEMENT_KEY)) {
298
- return false;
299
- }
300
-
301
- return true;
302
- } catch (e) {
303
- return false;
304
- }
305
- };
306
-
307
- /**
308
- * Rewrite config file with correct settings
309
- */
310
- const rewriteConfig = () => {
311
- const config = `port: ${PROXY_PORT}
312
- auth-dir: "${PROXY_AUTH_DIR}"
313
- api-keys:
314
- - "${API_KEY}"
315
- remote-management:
316
- secret-key: "${MANAGEMENT_KEY}"
317
- allow-remote-management: false
318
- request-retry: 3
319
- quota-exceeded:
320
- switch-project: true
321
- switch-preview-model: true
322
- `;
323
-
324
- // Ensure directory exists
325
- if (!fs.existsSync(PROXY_DIR)) {
326
- fs.mkdirSync(PROXY_DIR, { recursive: true });
327
- }
328
-
329
- fs.writeFileSync(PROXY_CONFIG, config);
330
- };
331
-
332
116
  /**
333
117
  * Ensure CLIProxyAPI is installed and running
334
118
  * @param {Function} onProgress - Progress callback
335
119
  */
336
120
  const ensureRunning = async (onProgress = () => {}) => {
337
- // Check if config needs to be fixed (e.g., has hashed key instead of plain text)
338
121
  const configValid = isConfigValid();
339
122
 
340
123
  if (await isRunning()) {
341
124
  if (!configValid) {
342
- // Config is invalid but proxy is running - need to restart with correct config
343
125
  onProgress('Updating proxy configuration...');
344
126
  rewriteConfig();
345
127
  await stop();
346
- await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for process to fully stop
128
+ await new Promise(resolve => setTimeout(resolve, 1000));
347
129
  onProgress('Restarting proxy with updated config...');
348
130
  await start();
349
131
  }
@@ -354,7 +136,6 @@ const ensureRunning = async (onProgress = () => {}) => {
354
136
  onProgress('Installing AI proxy (one-time setup)...');
355
137
  await install(onProgress);
356
138
  } else if (!configValid) {
357
- // Binary exists but config is invalid - fix it
358
139
  onProgress('Fixing proxy configuration...');
359
140
  rewriteConfig();
360
141
  }
@@ -409,7 +190,6 @@ const proxyRequest = (method, endpoint, body = null) => {
409
190
 
410
191
  /**
411
192
  * Make request to local proxy (Management API endpoints)
412
- * Uses management key for authentication
413
193
  */
414
194
  const managementRequest = (method, endpoint, body = null) => {
415
195
  return new Promise((resolve, reject) => {
@@ -467,8 +247,6 @@ const managementRequest = (method, endpoint, body = null) => {
467
247
  const getAuthUrl = async (provider) => {
468
248
  await ensureRunning();
469
249
 
470
- // Use is_webui=true to start the callback server on port 54545
471
- // Without this, CLIProxyAPI generates the URL but doesn't listen for callbacks
472
250
  const endpoints = {
473
251
  anthropic: '/v0/management/anthropic-auth-url?is_webui=true',
474
252
  openai: '/v0/management/codex-auth-url?is_webui=true',
@@ -495,12 +273,12 @@ const getAuthUrl = async (provider) => {
495
273
  };
496
274
 
497
275
  /**
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)
276
+ * Submit OAuth callback
277
+ * @param {string} callbackUrl - Full callback URL
278
+ * @param {string} provider - Provider ID
500
279
  * @returns {Promise<boolean>}
501
280
  */
502
281
  const submitCallback = async (callbackUrl, provider = 'anthropic') => {
503
- // Parse the callback URL
504
282
  let url;
505
283
  try {
506
284
  url = new URL(callbackUrl);
@@ -515,9 +293,6 @@ const submitCallback = async (callbackUrl, provider = 'anthropic') => {
515
293
  throw new Error('Missing code or state in callback URL');
516
294
  }
517
295
 
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
- // Ports are determined by the redirect_uri in the OAuth URL from CLIProxyAPI
521
296
  const providerConfig = {
522
297
  anthropic: { port: 54545, path: '/callback' },
523
298
  openai: { port: 1455, path: '/auth/callback' },
@@ -528,9 +303,6 @@ const submitCallback = async (callbackUrl, provider = 'anthropic') => {
528
303
 
529
304
  const config = providerConfig[provider] || providerConfig.anthropic;
530
305
 
531
- // Submit to the provider's OAuth callback port directly
532
- // Pass ALL query parameters from the callback URL, not just code and state
533
- // Some providers (like Gemini) require additional params like scope, authuser, etc.
534
306
  return new Promise((resolve, reject) => {
535
307
  const callbackPath = `${config.path}?${url.searchParams.toString()}`;
536
308
 
@@ -544,7 +316,6 @@ const submitCallback = async (callbackUrl, provider = 'anthropic') => {
544
316
  let data = '';
545
317
  res.on('data', chunk => data += chunk);
546
318
  res.on('end', () => {
547
- // Success if we get a redirect or success page
548
319
  if (res.statusCode === 200 || res.statusCode === 302 || res.statusCode === 301) {
549
320
  resolve(true);
550
321
  } else {
@@ -604,7 +375,7 @@ const waitForAuth = async (state, timeoutMs = 300000, onStatus = () => {}) => {
604
375
 
605
376
  /**
606
377
  * Get available models from the proxy
607
- * @param {string} [provider] - Optional provider to filter by (anthropic, openai, google, etc.)
378
+ * @param {string} [provider] - Optional provider to filter by
608
379
  * @returns {Promise<Array<string>>}
609
380
  */
610
381
  const getModels = async (provider = null) => {
@@ -615,9 +386,7 @@ const getModels = async (provider = null) => {
615
386
  if (response.data && Array.isArray(response.data)) {
616
387
  let models = response.data;
617
388
 
618
- // Filter by provider if specified (using owned_by field from API)
619
389
  if (provider) {
620
- // Map our provider IDs to the owned_by values from the API
621
390
  const ownerMap = {
622
391
  anthropic: 'anthropic',
623
392
  openai: 'openai',
@@ -693,157 +462,6 @@ const getProviderFromAuthFile = (filename) => {
693
462
  return 'unknown';
694
463
  };
695
464
 
696
- // ============================================
697
- // REMOTE OAUTH (for VPS/Server users)
698
- // Uses cli.hedgequantx.com as OAuth relay
699
- // ============================================
700
-
701
- const REMOTE_OAUTH_URL = 'https://cli.hedgequantx.com';
702
-
703
- /**
704
- * Make HTTPS request
705
- */
706
- const httpsRequest = (url, options, body = null) => {
707
- return new Promise((resolve, reject) => {
708
- const parsedUrl = new URL(url);
709
- const req = https.request({
710
- hostname: parsedUrl.hostname,
711
- port: 443,
712
- path: parsedUrl.pathname + parsedUrl.search,
713
- method: options.method || 'GET',
714
- headers: options.headers || {}
715
- }, (res) => {
716
- let data = '';
717
- res.on('data', chunk => data += chunk);
718
- res.on('end', () => {
719
- try {
720
- resolve(JSON.parse(data));
721
- } catch (e) {
722
- resolve(data);
723
- }
724
- });
725
- });
726
-
727
- req.on('error', reject);
728
- req.on('timeout', () => {
729
- req.destroy();
730
- reject(new Error('Request timeout'));
731
- });
732
-
733
- if (body) {
734
- req.write(typeof body === 'string' ? body : JSON.stringify(body));
735
- }
736
- req.end();
737
- });
738
- };
739
-
740
- /**
741
- * Create Remote OAuth session
742
- * @param {string} provider - Provider ID (anthropic, openai, gemini)
743
- * @returns {Promise<{sessionId: string, authUrl: string}>}
744
- */
745
- const createRemoteSession = async (provider) => {
746
- const response = await httpsRequest(
747
- `${REMOTE_OAUTH_URL}/oauth/session/create`,
748
- {
749
- method: 'POST',
750
- headers: { 'Content-Type': 'application/json' }
751
- },
752
- JSON.stringify({ provider })
753
- );
754
-
755
- if (response.error) {
756
- throw new Error(response.error);
757
- }
758
-
759
- return {
760
- sessionId: response.sessionId,
761
- authUrl: response.authUrl
762
- };
763
- };
764
-
765
- /**
766
- * Poll Remote OAuth session status
767
- * @param {string} sessionId - Session ID from createRemoteSession
768
- * @returns {Promise<{status: string, error?: string}>}
769
- */
770
- const pollRemoteSession = async (sessionId) => {
771
- const response = await httpsRequest(
772
- `${REMOTE_OAUTH_URL}/oauth/session/${sessionId}/status`,
773
- { method: 'GET' }
774
- );
775
- return response;
776
- };
777
-
778
- /**
779
- * Get tokens from Remote OAuth session
780
- * @param {string} sessionId - Session ID
781
- * @returns {Promise<{provider: string, tokens: Object}>}
782
- */
783
- const getRemoteTokens = async (sessionId) => {
784
- const response = await httpsRequest(
785
- `${REMOTE_OAUTH_URL}/oauth/session/${sessionId}/tokens`,
786
- { method: 'GET' }
787
- );
788
-
789
- if (response.error) {
790
- throw new Error(response.error);
791
- }
792
-
793
- return response;
794
- };
795
-
796
- /**
797
- * Wait for Remote OAuth to complete
798
- * @param {string} sessionId - Session ID
799
- * @param {number} timeoutMs - Timeout in milliseconds
800
- * @param {Function} onStatus - Status callback
801
- * @returns {Promise<{provider: string, tokens: Object}>}
802
- */
803
- const waitForRemoteAuth = async (sessionId, timeoutMs = 300000, onStatus = () => {}) => {
804
- const startTime = Date.now();
805
-
806
- while (Date.now() - startTime < timeoutMs) {
807
- const status = await pollRemoteSession(sessionId);
808
-
809
- if (status.status === 'success') {
810
- return await getRemoteTokens(sessionId);
811
- } else if (status.status === 'error') {
812
- throw new Error(status.error || 'Authentication failed');
813
- }
814
-
815
- onStatus('Waiting for authorization...');
816
- await new Promise(resolve => setTimeout(resolve, 2000));
817
- }
818
-
819
- throw new Error('Authentication timeout');
820
- };
821
-
822
- /**
823
- * Detect if we're running on a server (no display/browser)
824
- * @returns {boolean}
825
- */
826
- const isServerEnvironment = () => {
827
- // Check for common server indicators
828
- if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT) return true;
829
- if (process.env.DISPLAY === undefined && process.platform === 'linux') return true;
830
- if (process.env.TERM === 'dumb') return true;
831
- if (process.env.HQX_REMOTE_OAUTH === '1') return true; // Force remote mode
832
- return false;
833
- };
834
-
835
- /**
836
- * Detect if browser can be opened
837
- * @returns {boolean}
838
- */
839
- const canOpenBrowser = () => {
840
- // On macOS/Windows, we can usually open browser
841
- if (process.platform === 'darwin' || process.platform === 'win32') return true;
842
- // On Linux, check for display
843
- if (process.env.DISPLAY) return true;
844
- return false;
845
- };
846
-
847
465
  module.exports = {
848
466
  // Local OAuth (CLIProxyAPI)
849
467
  isInstalled,