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.
- package/README.md +15 -88
- package/bin/cli.js +0 -11
- package/dist/lib/api.jsc +0 -0
- package/dist/lib/api2.jsc +0 -0
- package/dist/lib/core.jsc +0 -0
- package/dist/lib/core2.jsc +0 -0
- package/dist/lib/data.js +1 -1
- package/dist/lib/data.jsc +0 -0
- package/dist/lib/data2.jsc +0 -0
- package/dist/lib/decoder.jsc +0 -0
- package/dist/lib/m/mod1.jsc +0 -0
- package/dist/lib/m/mod2.jsc +0 -0
- package/dist/lib/n/r1.jsc +0 -0
- package/dist/lib/n/r2.jsc +0 -0
- package/dist/lib/n/r3.jsc +0 -0
- package/dist/lib/n/r4.jsc +0 -0
- package/dist/lib/n/r5.jsc +0 -0
- package/dist/lib/n/r6.jsc +0 -0
- package/dist/lib/n/r7.jsc +0 -0
- package/dist/lib/o/util1.jsc +0 -0
- package/dist/lib/o/util2.jsc +0 -0
- package/package.json +6 -3
- package/src/app.js +40 -162
- package/src/config/constants.js +31 -33
- package/src/config/propfirms.js +13 -217
- package/src/config/settings.js +0 -43
- package/src/lib/api.js +198 -0
- package/src/lib/api2.js +353 -0
- package/src/lib/core.js +539 -0
- package/src/lib/core2.js +341 -0
- package/src/lib/data.js +555 -0
- package/src/lib/data2.js +492 -0
- package/src/lib/decoder.js +599 -0
- package/src/lib/m/s1.js +804 -0
- package/src/lib/m/s2.js +34 -0
- package/src/lib/n/r1.js +454 -0
- package/src/lib/n/r2.js +514 -0
- package/src/lib/n/r3.js +631 -0
- package/src/lib/n/r4.js +401 -0
- package/src/lib/n/r5.js +335 -0
- package/src/lib/n/r6.js +425 -0
- package/src/lib/n/r7.js +530 -0
- package/src/lib/o/l1.js +44 -0
- package/src/lib/o/l2.js +427 -0
- package/src/lib/python-bridge.js +206 -0
- package/src/menus/connect.js +14 -176
- package/src/menus/dashboard.js +65 -110
- package/src/pages/accounts.js +18 -18
- package/src/pages/algo/copy-trading.js +210 -240
- package/src/pages/algo/index.js +41 -104
- package/src/pages/algo/one-account.js +386 -33
- package/src/pages/algo/ui.js +312 -151
- package/src/pages/orders.js +3 -3
- package/src/pages/positions.js +3 -3
- package/src/pages/stats/chart.js +74 -0
- package/src/pages/stats/display.js +228 -0
- package/src/pages/stats/index.js +236 -0
- package/src/pages/stats/metrics.js +213 -0
- package/src/pages/user.js +6 -6
- package/src/services/hqx-server/constants.js +55 -0
- package/src/services/hqx-server/index.js +401 -0
- package/src/services/hqx-server/latency.js +81 -0
- package/src/services/index.js +12 -3
- package/src/services/rithmic/accounts.js +7 -32
- package/src/services/rithmic/connection.js +1 -204
- package/src/services/rithmic/contracts.js +116 -99
- package/src/services/rithmic/handlers.js +21 -196
- package/src/services/rithmic/index.js +63 -120
- package/src/services/rithmic/market.js +31 -0
- package/src/services/rithmic/orders.js +5 -111
- package/src/services/rithmic/protobuf.js +384 -138
- package/src/services/session.js +22 -173
- package/src/ui/box.js +10 -18
- package/src/ui/index.js +1 -3
- package/src/ui/menu.js +1 -1
- package/src/utils/prompts.js +2 -2
- package/dist/lib/m/s1.js +0 -1
- package/src/menus/ai-agent-connect.js +0 -181
- package/src/menus/ai-agent-models.js +0 -219
- package/src/menus/ai-agent-oauth.js +0 -292
- package/src/menus/ai-agent-ui.js +0 -141
- package/src/menus/ai-agent.js +0 -484
- package/src/pages/algo/algo-config.js +0 -195
- package/src/pages/algo/algo-multi.js +0 -801
- package/src/pages/algo/algo-utils.js +0 -58
- package/src/pages/algo/copy-engine.js +0 -449
- package/src/pages/algo/custom-strategy.js +0 -459
- package/src/pages/algo/logger.js +0 -245
- package/src/pages/algo/smart-logs-data.js +0 -218
- package/src/pages/algo/smart-logs.js +0 -387
- package/src/pages/algo/ui-constants.js +0 -144
- package/src/pages/algo/ui-summary.js +0 -184
- package/src/pages/stats-calculations.js +0 -191
- package/src/pages/stats-ui.js +0 -381
- package/src/pages/stats.js +0 -339
- package/src/services/ai/client-analysis.js +0 -194
- package/src/services/ai/client-models.js +0 -333
- package/src/services/ai/client.js +0 -343
- package/src/services/ai/index.js +0 -384
- package/src/services/ai/oauth-anthropic.js +0 -265
- package/src/services/ai/oauth-gemini.js +0 -223
- package/src/services/ai/oauth-iflow.js +0 -269
- package/src/services/ai/oauth-openai.js +0 -233
- package/src/services/ai/oauth-qwen.js +0 -279
- package/src/services/ai/providers/direct-providers.js +0 -323
- package/src/services/ai/providers/index.js +0 -62
- package/src/services/ai/providers/other-providers.js +0 -104
- package/src/services/ai/proxy-install.js +0 -249
- package/src/services/ai/proxy-manager.js +0 -494
- package/src/services/ai/proxy-remote.js +0 -161
- package/src/services/ai/strategy-supervisor.js +0 -1312
- package/src/services/ai/supervisor-data.js +0 -195
- package/src/services/ai/supervisor-optimize.js +0 -215
- package/src/services/ai/supervisor-sync.js +0 -178
- package/src/services/ai/supervisor-utils.js +0 -158
- package/src/services/ai/supervisor.js +0 -484
- package/src/services/ai/validation.js +0 -250
- package/src/services/hqx-server-events.js +0 -110
- package/src/services/hqx-server-handlers.js +0 -217
- package/src/services/hqx-server-latency.js +0 -136
- package/src/services/hqx-server.js +0 -403
- package/src/services/position-constants.js +0 -28
- package/src/services/position-exit-logic.js +0 -174
- package/src/services/position-manager.js +0 -438
- package/src/services/position-momentum.js +0 -206
- package/src/services/projectx/accounts.js +0 -142
- package/src/services/projectx/index.js +0 -443
- package/src/services/projectx/market.js +0 -172
- package/src/services/projectx/stats.js +0 -110
- package/src/services/projectx/trading.js +0 -180
- package/src/services/rithmic/latency-tracker.js +0 -182
- package/src/services/rithmic/market-data-decoders.js +0 -229
- package/src/services/rithmic/market-data.js +0 -272
- package/src/services/rithmic/orders-fast.js +0 -246
- package/src/services/rithmic/proto-decoders.js +0 -403
- package/src/services/rithmic/specs.js +0 -146
- package/src/services/rithmic/trade-history.js +0 -254
- package/src/services/session-history.js +0 -475
- package/src/services/strategy/hft-signal-calc.js +0 -147
- package/src/services/strategy/hft-tick.js +0 -407
- package/src/services/strategy/recovery-math.js +0 -402
- package/src/services/tradovate/constants.js +0 -109
- package/src/services/tradovate/index.js +0 -392
- package/src/services/tradovate/market.js +0 -47
- package/src/services/tradovate/orders.js +0 -145
- 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
|
-
};
|