Files
ag-x/dist/services/apiProxy.js
admin f7378eceb0 v2.0.5: Fix E2E flow - proxy, welcome screen, provider sync
Critical fixes:
- Translation proxy now uses system Node.js (not Electron binary)
- Removed duplicate proxy start causing port conflicts
- Added port availability check before spawning proxy
- Fixed welcome:choice double resolve()
- Fixed settings.html close using deprecated remote
- Fixed translationProxy /v1 for openai-compat backends
- Proxy no longer detached/unref - properly tracked as child
- SingletonLock cleanup on startup

Verified E2E:
- Welcome screen on first run ✓
- Provider selection works ✓
- Settings save + sync ✓
- Translation proxy starts correctly ✓
- LS connects to proxy ✓
- --ag-reset works ✓
2026-05-23 12:14:04 +04:00

120 lines
4.6 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApiProxy = void 0;
const http = require("http");
const https = require("https");
const child_process = require("child_process");
const path = require("path");
const os = require("os");
const fs = require("fs");
const translationProxy_1 = require("./translationProxy");
/**
* API Proxy for AG X.
*
* All backends now use the built-in Node.js translation proxy.
* No external Python or tools required.
*/
class ApiProxy {
constructor(providerService) {
this.providerService = providerService;
this.server = null;
this.proxyProcess = null;
this.port = providerService.getProxyPort();
}
start() {
if (this.server || this.proxyProcess) {
return;
}
const backendType = this.providerService.getActiveBackendType();
if (backendType === 'gemini-native' || backendType === 'native') {
console.log('[ApiProxy] Native backend, no proxy needed');
return;
}
this.startInternalProxy();
}
/**
* Start the built-in Node.js translation proxy as a child process
*/
startInternalProxy() {
const activeProvider = this.providerService.getActiveProvider();
const config = this.providerService.getProviderConfig(activeProvider);
if (!config) {
console.error('[ApiProxy] No provider config found');
return;
}
// Prepare the proxy config
const configDir = path.join(os.homedir(), '.cache', 'ag-x-proxy');
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true });
}
const models = (config.models || []).map((m) => {
if (typeof m === 'string') {
return { id: m, object: "model", created: 1700000000, owned_by: activeProvider };
}
return m;
});
const proxyConfig = {
port: this.port,
backend_type: config.backendType || 'openai-compat',
target_url: config.apiUrl || '',
api_key: config.apiKey || '',
cc_version: config.ccVersion || '0.26.8',
oauth_provider: config.oauthProvider || '',
reasoning_enabled: config.reasoningEnabled !== undefined ? config.reasoningEnabled : true,
reasoning_effort: config.reasoningEffort || 'medium',
models: models,
};
const configPath = path.join(configDir, `ag-x-proxy-${this.port}.json`);
fs.writeFileSync(configPath, JSON.stringify(proxyConfig, null, 2), 'utf-8');
console.log(`[ApiProxy] Starting built-in Node.js translation proxy on port ${this.port}`);
// Use Electron's Node.js binary to run our translation proxy
const proxyScript = path.join(__dirname, 'translationProxy.js');
// Use system node (not Electron's process.execPath which starts a GUI)
const nodeBin = process.execPath.includes('electron') || process.execPath.includes('AG-X')
? child_process.execSync('which node 2>/dev/null || echo /usr/bin/node').toString().trim()
: process.execPath;
this.proxyProcess = child_process.spawn(nodeBin, [proxyScript], {
stdio: ['ignore', 'pipe', 'pipe'],
detached: false,
env: { ...process.env },
});
this.proxyProcess.stdout?.on('data', (data) => {
const lines = data.toString().trim().split('\n');
for (const line of lines) {
console.log(`[Proxy] ${line}`);
}
});
this.proxyProcess.stderr?.on('data', (data) => {
const lines = data.toString().trim().split('\n');
for (const line of lines) {
console.log(`[Proxy] ${line}`);
}
});
this.proxyProcess.on('error', (err) => {
console.error('[ApiProxy] Proxy error:', err);
});
this.proxyProcess.on('exit', (code, signal) => {
console.log(`[ApiProxy] Proxy exited with code ${code}, signal ${signal}`);
this.proxyProcess = null;
});
this.proxyProcess.unref();
console.log(`[ApiProxy] Built-in Node.js proxy spawned for ${config.backendType} on port ${this.port}`);
}
stop() {
if (this.proxyProcess) {
try {
this.proxyProcess.kill();
}
catch (e) { /* ignore */ }
this.proxyProcess = null;
}
if (this.server) {
this.server.close();
this.server = null;
console.log('[ApiProxy] Stopped');
}
}
}
exports.ApiProxy = ApiProxy;