hedgequantx 2.6.162 → 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 (138) 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 +235 -0
  67. package/src/services/rithmic/handlers.js +21 -196
  68. package/src/services/rithmic/index.js +60 -291
  69. package/src/services/rithmic/market.js +31 -0
  70. package/src/services/rithmic/orders.js +5 -361
  71. package/src/services/rithmic/protobuf.js +5 -195
  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/index.js +0 -526
  106. package/src/services/ai/proxy-install.js +0 -249
  107. package/src/services/ai/proxy-manager.js +0 -494
  108. package/src/services/ai/proxy-remote.js +0 -161
  109. package/src/services/ai/strategy-supervisor.js +0 -1312
  110. package/src/services/ai/supervisor-data.js +0 -195
  111. package/src/services/ai/supervisor-optimize.js +0 -215
  112. package/src/services/ai/supervisor-sync.js +0 -178
  113. package/src/services/ai/supervisor-utils.js +0 -158
  114. package/src/services/ai/supervisor.js +0 -484
  115. package/src/services/ai/validation.js +0 -250
  116. package/src/services/hqx-server-events.js +0 -110
  117. package/src/services/hqx-server-handlers.js +0 -217
  118. package/src/services/hqx-server-latency.js +0 -136
  119. package/src/services/hqx-server.js +0 -403
  120. package/src/services/position-constants.js +0 -28
  121. package/src/services/position-manager.js +0 -528
  122. package/src/services/position-momentum.js +0 -206
  123. package/src/services/projectx/accounts.js +0 -142
  124. package/src/services/projectx/index.js +0 -443
  125. package/src/services/projectx/market.js +0 -172
  126. package/src/services/projectx/stats.js +0 -110
  127. package/src/services/projectx/trading.js +0 -180
  128. package/src/services/rithmic/latency-tracker.js +0 -182
  129. package/src/services/rithmic/market-data.js +0 -549
  130. package/src/services/rithmic/specs.js +0 -146
  131. package/src/services/rithmic/trade-history.js +0 -254
  132. package/src/services/session-history.js +0 -475
  133. package/src/services/strategy/hft-tick.js +0 -507
  134. package/src/services/strategy/recovery-math.js +0 -402
  135. package/src/services/tradovate/constants.js +0 -109
  136. package/src/services/tradovate/index.js +0 -505
  137. package/src/services/tradovate/market.js +0 -47
  138. package/src/services/tradovate/websocket.js +0 -97
@@ -1,494 +0,0 @@
1
- /**
2
- * CLIProxyAPI Manager
3
- *
4
- * Automatically manages CLIProxyAPI for connecting AI subscription accounts
5
- * (ChatGPT Plus, Claude Pro, etc.) without needing API keys.
6
- *
7
- * Flow:
8
- * 1. User selects "Connect Account" in CLI
9
- * 2. We ensure CLIProxyAPI is installed and running
10
- * 3. Generate OAuth URL → User opens in browser → Logs in
11
- * 4. Callback to localhost → Token saved → Models available
12
- */
13
-
14
- const http = require('http');
15
- const { spawn, exec } = require('child_process');
16
-
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');
30
-
31
- const {
32
- createRemoteSession,
33
- pollRemoteSession,
34
- getRemoteTokens,
35
- waitForRemoteAuth,
36
- isServerEnvironment,
37
- canOpenBrowser,
38
- REMOTE_OAUTH_URL
39
- } = require('./proxy-remote');
40
-
41
- /**
42
- * Check if CLIProxyAPI is running
43
- */
44
- const isRunning = async () => {
45
- return new Promise((resolve) => {
46
- const req = http.request({
47
- hostname: '127.0.0.1',
48
- port: PROXY_PORT,
49
- path: '/v1/models',
50
- method: 'GET',
51
- headers: {
52
- 'Authorization': `Bearer ${API_KEY}`
53
- },
54
- timeout: 2000
55
- }, (res) => {
56
- resolve(res.statusCode === 200);
57
- });
58
-
59
- req.on('error', () => resolve(false));
60
- req.on('timeout', () => {
61
- req.destroy();
62
- resolve(false);
63
- });
64
- req.end();
65
- });
66
- };
67
-
68
- /**
69
- * Start CLIProxyAPI in background
70
- */
71
- const start = async () => {
72
- if (await isRunning()) {
73
- return true;
74
- }
75
-
76
- if (!isInstalled()) {
77
- throw new Error('CLIProxyAPI not installed');
78
- }
79
-
80
- return new Promise((resolve, reject) => {
81
- const proc = spawn(PROXY_BIN, ['--config', PROXY_CONFIG], {
82
- detached: true,
83
- stdio: 'ignore',
84
- cwd: PROXY_DIR
85
- });
86
-
87
- proc.unref();
88
-
89
- let attempts = 0;
90
- const checkInterval = setInterval(async () => {
91
- attempts++;
92
- if (await isRunning()) {
93
- clearInterval(checkInterval);
94
- resolve(true);
95
- } else if (attempts > 30) {
96
- clearInterval(checkInterval);
97
- reject(new Error('Failed to start CLIProxyAPI'));
98
- }
99
- }, 500);
100
- });
101
- };
102
-
103
- /**
104
- * Stop CLIProxyAPI
105
- */
106
- const stop = async () => {
107
- return new Promise((resolve) => {
108
- if (process.platform === 'win32') {
109
- exec('taskkill /F /IM cli-proxy-api.exe', () => resolve());
110
- } else {
111
- exec('pkill -f cli-proxy-api', () => resolve());
112
- }
113
- });
114
- };
115
-
116
- /**
117
- * Ensure CLIProxyAPI is installed and running
118
- * @param {Function} onProgress - Progress callback
119
- */
120
- const ensureRunning = async (onProgress = () => {}) => {
121
- const configValid = isConfigValid();
122
-
123
- if (await isRunning()) {
124
- if (!configValid) {
125
- onProgress('Updating proxy configuration...');
126
- rewriteConfig();
127
- await stop();
128
- await new Promise(resolve => setTimeout(resolve, 1000));
129
- onProgress('Restarting proxy with updated config...');
130
- await start();
131
- }
132
- return true;
133
- }
134
-
135
- if (!isInstalled()) {
136
- onProgress('Installing AI proxy (one-time setup)...');
137
- await install(onProgress);
138
- } else if (!configValid) {
139
- onProgress('Fixing proxy configuration...');
140
- rewriteConfig();
141
- }
142
-
143
- onProgress('Starting AI proxy...');
144
- await start();
145
-
146
- return true;
147
- };
148
-
149
- /**
150
- * Make request to local proxy (API endpoints)
151
- */
152
- const proxyRequest = (method, endpoint, body = null) => {
153
- return new Promise((resolve, reject) => {
154
- const options = {
155
- hostname: '127.0.0.1',
156
- port: PROXY_PORT,
157
- path: endpoint,
158
- method,
159
- headers: {
160
- 'Authorization': `Bearer ${API_KEY}`,
161
- 'Content-Type': 'application/json'
162
- },
163
- timeout: 30000
164
- };
165
-
166
- const req = http.request(options, (res) => {
167
- let data = '';
168
- res.on('data', chunk => data += chunk);
169
- res.on('end', () => {
170
- try {
171
- resolve(JSON.parse(data));
172
- } catch (e) {
173
- resolve(data);
174
- }
175
- });
176
- });
177
-
178
- req.on('error', reject);
179
- req.on('timeout', () => {
180
- req.destroy();
181
- reject(new Error('Request timeout'));
182
- });
183
-
184
- if (body) {
185
- req.write(JSON.stringify(body));
186
- }
187
- req.end();
188
- });
189
- };
190
-
191
- /**
192
- * Make request to local proxy (Management API endpoints)
193
- */
194
- const managementRequest = (method, endpoint, body = null) => {
195
- return new Promise((resolve, reject) => {
196
- const options = {
197
- hostname: '127.0.0.1',
198
- port: PROXY_PORT,
199
- path: endpoint,
200
- method,
201
- headers: {
202
- 'Authorization': `Bearer ${MANAGEMENT_KEY}`,
203
- 'Content-Type': 'application/json'
204
- },
205
- timeout: 30000
206
- };
207
-
208
- const req = http.request(options, (res) => {
209
- let data = '';
210
- res.on('data', chunk => data += chunk);
211
- res.on('end', () => {
212
- try {
213
- const json = JSON.parse(data);
214
- if (res.statusCode >= 400) {
215
- reject(new Error(json.error || `HTTP ${res.statusCode}`));
216
- } else {
217
- resolve(json);
218
- }
219
- } catch (e) {
220
- if (res.statusCode >= 400) {
221
- reject(new Error(`HTTP ${res.statusCode}: ${data}`));
222
- } else {
223
- resolve(data);
224
- }
225
- }
226
- });
227
- });
228
-
229
- req.on('error', reject);
230
- req.on('timeout', () => {
231
- req.destroy();
232
- reject(new Error('Request timeout'));
233
- });
234
-
235
- if (body) {
236
- req.write(JSON.stringify(body));
237
- }
238
- req.end();
239
- });
240
- };
241
-
242
- /**
243
- * Get OAuth authorization URL for a provider
244
- * @param {string} provider - Provider ID (anthropic, openai, gemini, qwen, iflow)
245
- * @returns {Promise<{url: string, state: string}>}
246
- */
247
- const getAuthUrl = async (provider) => {
248
- await ensureRunning();
249
-
250
- const endpoints = {
251
- anthropic: '/v0/management/anthropic-auth-url?is_webui=true',
252
- openai: '/v0/management/codex-auth-url?is_webui=true',
253
- gemini: '/v0/management/gemini-cli-auth-url?is_webui=true',
254
- qwen: '/v0/management/qwen-auth-url?is_webui=true',
255
- iflow: '/v0/management/iflow-auth-url?is_webui=true'
256
- };
257
-
258
- const endpoint = endpoints[provider];
259
- if (!endpoint) {
260
- throw new Error(`Unknown provider: ${provider}`);
261
- }
262
-
263
- const response = await managementRequest('GET', endpoint);
264
-
265
- if (response.status !== 'ok') {
266
- throw new Error(response.error || 'Failed to get auth URL');
267
- }
268
-
269
- return {
270
- url: response.url,
271
- state: response.state
272
- };
273
- };
274
-
275
- /**
276
- * Submit OAuth callback
277
- * @param {string} callbackUrl - Full callback URL
278
- * @param {string} provider - Provider ID
279
- * @returns {Promise<boolean>}
280
- */
281
- const submitCallback = async (callbackUrl, provider = 'anthropic') => {
282
- let url;
283
- try {
284
- url = new URL(callbackUrl);
285
- } catch (e) {
286
- throw new Error('Invalid callback URL format');
287
- }
288
-
289
- const code = url.searchParams.get('code');
290
- const state = url.searchParams.get('state');
291
-
292
- if (!code || !state) {
293
- throw new Error('Missing code or state in callback URL');
294
- }
295
-
296
- const providerConfig = {
297
- anthropic: { port: 54545, path: '/callback' },
298
- openai: { port: 1455, path: '/auth/callback' },
299
- gemini: { port: 8085, path: '/oauth2callback' },
300
- qwen: { port: 8087, path: '/oauth2callback' },
301
- iflow: { port: 8088, path: '/callback' }
302
- };
303
-
304
- const config = providerConfig[provider] || providerConfig.anthropic;
305
-
306
- return new Promise((resolve, reject) => {
307
- const callbackPath = `${config.path}?${url.searchParams.toString()}`;
308
-
309
- const req = http.request({
310
- hostname: '127.0.0.1',
311
- port: config.port,
312
- path: callbackPath,
313
- method: 'GET',
314
- timeout: 30000
315
- }, (res) => {
316
- let data = '';
317
- res.on('data', chunk => data += chunk);
318
- res.on('end', () => {
319
- if (res.statusCode === 200 || res.statusCode === 302 || res.statusCode === 301) {
320
- resolve(true);
321
- } else {
322
- reject(new Error(`Callback failed with status ${res.statusCode}`));
323
- }
324
- });
325
- });
326
-
327
- req.on('error', (e) => {
328
- reject(new Error(`Failed to submit callback: ${e.message}`));
329
- });
330
-
331
- req.on('timeout', () => {
332
- req.destroy();
333
- reject(new Error('Callback request timeout'));
334
- });
335
-
336
- req.end();
337
- });
338
- };
339
-
340
- /**
341
- * Poll OAuth authentication status
342
- * @param {string} state - OAuth state
343
- * @returns {Promise<{status: string, error?: string}>}
344
- */
345
- const pollAuthStatus = async (state) => {
346
- const response = await managementRequest('GET', `/v0/management/get-auth-status?state=${state}`);
347
- return response;
348
- };
349
-
350
- /**
351
- * Wait for OAuth authentication to complete
352
- * @param {string} state - OAuth state
353
- * @param {number} timeoutMs - Timeout in milliseconds
354
- * @param {Function} onStatus - Status callback
355
- * @returns {Promise<boolean>}
356
- */
357
- const waitForAuth = async (state, timeoutMs = 300000, onStatus = () => {}) => {
358
- const startTime = Date.now();
359
-
360
- while (Date.now() - startTime < timeoutMs) {
361
- const status = await pollAuthStatus(state);
362
-
363
- if (status.status === 'ok') {
364
- return true;
365
- } else if (status.status === 'error') {
366
- throw new Error(status.error || 'Authentication failed');
367
- }
368
-
369
- onStatus('Waiting for authorization...');
370
- await new Promise(resolve => setTimeout(resolve, 2000));
371
- }
372
-
373
- throw new Error('Authentication timeout');
374
- };
375
-
376
- /**
377
- * Get available models from the proxy
378
- * @param {string} [provider] - Optional provider to filter by
379
- * @returns {Promise<Array<string>>}
380
- */
381
- const getModels = async (provider = null) => {
382
- await ensureRunning();
383
-
384
- const response = await proxyRequest('GET', '/v1/models');
385
-
386
- if (response.data && Array.isArray(response.data)) {
387
- let models = response.data;
388
-
389
- if (provider) {
390
- const ownerMap = {
391
- anthropic: 'anthropic',
392
- openai: 'openai',
393
- gemini: 'google',
394
- qwen: 'qwen',
395
- iflow: 'iflow'
396
- };
397
- const owner = ownerMap[provider] || provider;
398
- models = models.filter(m => m.owned_by && m.owned_by.toLowerCase() === owner.toLowerCase());
399
- }
400
-
401
- return models.map(m => m.id || m).filter(Boolean);
402
- }
403
-
404
- return [];
405
- };
406
-
407
- /**
408
- * Get list of authenticated accounts
409
- * @returns {Promise<Array>}
410
- */
411
- const getAuthFiles = async () => {
412
- await ensureRunning();
413
-
414
- try {
415
- const response = await managementRequest('GET', '/v0/management/auth-files');
416
- return response.files || [];
417
- } catch (e) {
418
- return [];
419
- }
420
- };
421
-
422
- /**
423
- * Make chat completion request through proxy
424
- * @param {string} model - Model ID
425
- * @param {Array} messages - Chat messages
426
- * @param {Object} options - Additional options
427
- * @returns {Promise<Object>}
428
- */
429
- const chatCompletion = async (model, messages, options = {}) => {
430
- await ensureRunning();
431
-
432
- const body = {
433
- model,
434
- messages,
435
- ...options
436
- };
437
-
438
- return proxyRequest('POST', '/v1/chat/completions', body);
439
- };
440
-
441
- /**
442
- * Check if user has any connected accounts
443
- */
444
- const hasConnectedAccounts = async () => {
445
- try {
446
- const files = await getAuthFiles();
447
- return files.length > 0;
448
- } catch (e) {
449
- return false;
450
- }
451
- };
452
-
453
- /**
454
- * Get provider name from auth file
455
- */
456
- const getProviderFromAuthFile = (filename) => {
457
- if (filename.includes('claude') || filename.includes('anthropic')) return 'anthropic';
458
- if (filename.includes('openai') || filename.includes('codex')) return 'openai';
459
- if (filename.includes('gemini') || filename.includes('google')) return 'gemini';
460
- if (filename.includes('qwen')) return 'qwen';
461
- if (filename.includes('iflow')) return 'iflow';
462
- return 'unknown';
463
- };
464
-
465
- module.exports = {
466
- // Local OAuth (CLIProxyAPI)
467
- isInstalled,
468
- isRunning,
469
- install,
470
- start,
471
- stop,
472
- ensureRunning,
473
- getAuthUrl,
474
- pollAuthStatus,
475
- waitForAuth,
476
- submitCallback,
477
- getModels,
478
- getAuthFiles,
479
- chatCompletion,
480
- hasConnectedAccounts,
481
- getProviderFromAuthFile,
482
- PROXY_PORT,
483
- PROXY_DIR,
484
- API_KEY,
485
-
486
- // Remote OAuth (cli.hedgequantx.com)
487
- createRemoteSession,
488
- pollRemoteSession,
489
- getRemoteTokens,
490
- waitForRemoteAuth,
491
- isServerEnvironment,
492
- canOpenBrowser,
493
- REMOTE_OAUTH_URL
494
- };
@@ -1,161 +0,0 @@
1
- /**
2
- * @fileoverview Remote OAuth for CLIProxyAPI
3
- *
4
- * For VPS/Server users without browser access
5
- * Uses cli.hedgequantx.com as OAuth relay
6
- */
7
-
8
- const https = require('https');
9
-
10
- const REMOTE_OAUTH_URL = 'https://cli.hedgequantx.com';
11
-
12
- /**
13
- * Make HTTPS request
14
- */
15
- const httpsRequest = (url, options, body = null) => {
16
- return new Promise((resolve, reject) => {
17
- const parsedUrl = new URL(url);
18
- const req = https.request({
19
- hostname: parsedUrl.hostname,
20
- port: 443,
21
- path: parsedUrl.pathname + parsedUrl.search,
22
- method: options.method || 'GET',
23
- headers: options.headers || {}
24
- }, (res) => {
25
- let data = '';
26
- res.on('data', chunk => data += chunk);
27
- res.on('end', () => {
28
- try {
29
- resolve(JSON.parse(data));
30
- } catch (e) {
31
- resolve(data);
32
- }
33
- });
34
- });
35
-
36
- req.on('error', reject);
37
- req.on('timeout', () => {
38
- req.destroy();
39
- reject(new Error('Request timeout'));
40
- });
41
-
42
- if (body) {
43
- req.write(typeof body === 'string' ? body : JSON.stringify(body));
44
- }
45
- req.end();
46
- });
47
- };
48
-
49
- /**
50
- * Create Remote OAuth session
51
- * @param {string} provider - Provider ID (anthropic, openai, gemini)
52
- * @returns {Promise<{sessionId: string, authUrl: string}>}
53
- */
54
- const createRemoteSession = async (provider) => {
55
- const response = await httpsRequest(
56
- `${REMOTE_OAUTH_URL}/oauth/session/create`,
57
- {
58
- method: 'POST',
59
- headers: { 'Content-Type': 'application/json' }
60
- },
61
- JSON.stringify({ provider })
62
- );
63
-
64
- if (response.error) {
65
- throw new Error(response.error);
66
- }
67
-
68
- return {
69
- sessionId: response.sessionId,
70
- authUrl: response.authUrl
71
- };
72
- };
73
-
74
- /**
75
- * Poll Remote OAuth session status
76
- * @param {string} sessionId - Session ID from createRemoteSession
77
- * @returns {Promise<{status: string, error?: string}>}
78
- */
79
- const pollRemoteSession = async (sessionId) => {
80
- const response = await httpsRequest(
81
- `${REMOTE_OAUTH_URL}/oauth/session/${sessionId}/status`,
82
- { method: 'GET' }
83
- );
84
- return response;
85
- };
86
-
87
- /**
88
- * Get tokens from Remote OAuth session
89
- * @param {string} sessionId - Session ID
90
- * @returns {Promise<{provider: string, tokens: Object}>}
91
- */
92
- const getRemoteTokens = async (sessionId) => {
93
- const response = await httpsRequest(
94
- `${REMOTE_OAUTH_URL}/oauth/session/${sessionId}/tokens`,
95
- { method: 'GET' }
96
- );
97
-
98
- if (response.error) {
99
- throw new Error(response.error);
100
- }
101
-
102
- return response;
103
- };
104
-
105
- /**
106
- * Wait for Remote OAuth to complete
107
- * @param {string} sessionId - Session ID
108
- * @param {number} timeoutMs - Timeout in milliseconds
109
- * @param {Function} onStatus - Status callback
110
- * @returns {Promise<{provider: string, tokens: Object}>}
111
- */
112
- const waitForRemoteAuth = async (sessionId, timeoutMs = 300000, onStatus = () => {}) => {
113
- const startTime = Date.now();
114
-
115
- while (Date.now() - startTime < timeoutMs) {
116
- const status = await pollRemoteSession(sessionId);
117
-
118
- if (status.status === 'success') {
119
- return await getRemoteTokens(sessionId);
120
- } else if (status.status === 'error') {
121
- throw new Error(status.error || 'Authentication failed');
122
- }
123
-
124
- onStatus('Waiting for authorization...');
125
- await new Promise(resolve => setTimeout(resolve, 2000));
126
- }
127
-
128
- throw new Error('Authentication timeout');
129
- };
130
-
131
- /**
132
- * Detect if we're running on a server (no display/browser)
133
- * @returns {boolean}
134
- */
135
- const isServerEnvironment = () => {
136
- if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT) return true;
137
- if (process.env.DISPLAY === undefined && process.platform === 'linux') return true;
138
- if (process.env.TERM === 'dumb') return true;
139
- if (process.env.HQX_REMOTE_OAUTH === '1') return true;
140
- return false;
141
- };
142
-
143
- /**
144
- * Detect if browser can be opened
145
- * @returns {boolean}
146
- */
147
- const canOpenBrowser = () => {
148
- if (process.platform === 'darwin' || process.platform === 'win32') return true;
149
- if (process.env.DISPLAY) return true;
150
- return false;
151
- };
152
-
153
- module.exports = {
154
- createRemoteSession,
155
- pollRemoteSession,
156
- getRemoteTokens,
157
- waitForRemoteAuth,
158
- isServerEnvironment,
159
- canOpenBrowser,
160
- REMOTE_OAUTH_URL
161
- };