hedgequantx 2.6.102 → 2.6.104

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.6.102",
3
+ "version": "2.6.104",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -27,6 +27,7 @@ const PROXY_BIN = path.join(PROXY_DIR, process.platform === 'win32' ? 'cli-proxy
27
27
  const PROXY_CONFIG = path.join(PROXY_DIR, 'config.yaml');
28
28
  const PROXY_AUTH_DIR = path.join(PROXY_DIR, 'auths');
29
29
  const API_KEY = 'hqx-local-key';
30
+ const MANAGEMENT_KEY = 'hqx-mgmt-key';
30
31
 
31
32
  // GitHub release URLs
32
33
  const getDownloadUrl = () => {
@@ -202,6 +203,9 @@ const install = async (onProgress = () => {}) => {
202
203
  auth-dir: "${PROXY_AUTH_DIR}"
203
204
  api-keys:
204
205
  - "${API_KEY}"
206
+ remote-management:
207
+ secret-key: "${MANAGEMENT_KEY}"
208
+ allow-remote-management: false
205
209
  request-retry: 3
206
210
  quota-exceeded:
207
211
  switch-project: true
@@ -267,18 +271,92 @@ const stop = async () => {
267
271
  });
268
272
  };
269
273
 
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
+
270
332
  /**
271
333
  * Ensure CLIProxyAPI is installed and running
272
334
  * @param {Function} onProgress - Progress callback
273
335
  */
274
336
  const ensureRunning = async (onProgress = () => {}) => {
337
+ // Check if config needs to be fixed (e.g., has hashed key instead of plain text)
338
+ const configValid = isConfigValid();
339
+
275
340
  if (await isRunning()) {
341
+ if (!configValid) {
342
+ // Config is invalid but proxy is running - need to restart with correct config
343
+ onProgress('Updating proxy configuration...');
344
+ rewriteConfig();
345
+ await stop();
346
+ await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for process to fully stop
347
+ onProgress('Restarting proxy with updated config...');
348
+ await start();
349
+ }
276
350
  return true;
277
351
  }
278
352
 
279
353
  if (!isInstalled()) {
280
354
  onProgress('Installing AI proxy (one-time setup)...');
281
355
  await install(onProgress);
356
+ } else if (!configValid) {
357
+ // Binary exists but config is invalid - fix it
358
+ onProgress('Fixing proxy configuration...');
359
+ rewriteConfig();
282
360
  }
283
361
 
284
362
  onProgress('Starting AI proxy...');
@@ -288,7 +366,7 @@ const ensureRunning = async (onProgress = () => {}) => {
288
366
  };
289
367
 
290
368
  /**
291
- * Make request to local proxy
369
+ * Make request to local proxy (API endpoints)
292
370
  */
293
371
  const proxyRequest = (method, endpoint, body = null) => {
294
372
  return new Promise((resolve, reject) => {
@@ -329,6 +407,58 @@ const proxyRequest = (method, endpoint, body = null) => {
329
407
  });
330
408
  };
331
409
 
410
+ /**
411
+ * Make request to local proxy (Management API endpoints)
412
+ * Uses management key for authentication
413
+ */
414
+ const managementRequest = (method, endpoint, body = null) => {
415
+ return new Promise((resolve, reject) => {
416
+ const options = {
417
+ hostname: '127.0.0.1',
418
+ port: PROXY_PORT,
419
+ path: endpoint,
420
+ method,
421
+ headers: {
422
+ 'Authorization': `Bearer ${MANAGEMENT_KEY}`,
423
+ 'Content-Type': 'application/json'
424
+ },
425
+ timeout: 30000
426
+ };
427
+
428
+ const req = http.request(options, (res) => {
429
+ let data = '';
430
+ res.on('data', chunk => data += chunk);
431
+ res.on('end', () => {
432
+ try {
433
+ const json = JSON.parse(data);
434
+ if (res.statusCode >= 400) {
435
+ reject(new Error(json.error || `HTTP ${res.statusCode}`));
436
+ } else {
437
+ resolve(json);
438
+ }
439
+ } catch (e) {
440
+ if (res.statusCode >= 400) {
441
+ reject(new Error(`HTTP ${res.statusCode}: ${data}`));
442
+ } else {
443
+ resolve(data);
444
+ }
445
+ }
446
+ });
447
+ });
448
+
449
+ req.on('error', reject);
450
+ req.on('timeout', () => {
451
+ req.destroy();
452
+ reject(new Error('Request timeout'));
453
+ });
454
+
455
+ if (body) {
456
+ req.write(JSON.stringify(body));
457
+ }
458
+ req.end();
459
+ });
460
+ };
461
+
332
462
  /**
333
463
  * Get OAuth authorization URL for a provider
334
464
  * @param {string} provider - Provider ID (anthropic, openai, gemini, qwen, iflow)
@@ -350,7 +480,7 @@ const getAuthUrl = async (provider) => {
350
480
  throw new Error(`Unknown provider: ${provider}`);
351
481
  }
352
482
 
353
- const response = await proxyRequest('GET', endpoint);
483
+ const response = await managementRequest('GET', endpoint);
354
484
 
355
485
  if (response.status !== 'ok') {
356
486
  throw new Error(response.error || 'Failed to get auth URL');
@@ -368,7 +498,7 @@ const getAuthUrl = async (provider) => {
368
498
  * @returns {Promise<{status: string, error?: string}>}
369
499
  */
370
500
  const pollAuthStatus = async (state) => {
371
- const response = await proxyRequest('GET', `/v0/management/get-auth-status?state=${state}`);
501
+ const response = await managementRequest('GET', `/v0/management/get-auth-status?state=${state}`);
372
502
  return response;
373
503
  };
374
504
 
@@ -422,7 +552,7 @@ const getAuthFiles = async () => {
422
552
  await ensureRunning();
423
553
 
424
554
  try {
425
- const response = await proxyRequest('GET', '/v0/management/auth-files');
555
+ const response = await managementRequest('GET', '/v0/management/auth-files');
426
556
  return response.files || [];
427
557
  } catch (e) {
428
558
  return [];