Features: - Welcome screen on first run (provider choice before LS starts) - 15+ AI providers (Google Gemini, OpenAI, Anthropic, DeepSeek, Ollama, etc.) - Provider config syncs to endpoints.json for translation proxy - Built-in Node.js translation proxy for non-native backends - Auto-update support, tray integration, URI scheme handler
170 lines
7.0 KiB
JavaScript
170 lines
7.0 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.openProviderSettings = openProviderSettings;
|
|
exports.closeProviderSettings = closeProviderSettings;
|
|
const electron_1 = require("electron");
|
|
const path_1 = require("path");
|
|
let settingsWindow = null;
|
|
function openProviderSettings(providerService) {
|
|
if (settingsWindow) {
|
|
settingsWindow.show();
|
|
settingsWindow.focus();
|
|
return;
|
|
}
|
|
settingsWindow = new electron_1.BrowserWindow({
|
|
width: 820,
|
|
height: 720,
|
|
title: 'AI Provider Settings',
|
|
resizable: true,
|
|
minimizable: true,
|
|
maximizable: true,
|
|
backgroundColor: '#1a1a2e',
|
|
titleBarStyle: 'hidden',
|
|
titleBarOverlay: {
|
|
color: '#16213e',
|
|
symbolColor: '#e0e0e0',
|
|
height: 36,
|
|
},
|
|
webPreferences: {
|
|
nodeIntegration: true,
|
|
contextIsolation: false,
|
|
enableRemoteModule: false,
|
|
},
|
|
});
|
|
settingsWindow.loadFile(path_1.join(__dirname, 'provider', 'settings.html'));
|
|
settingsWindow.on('closed', () => {
|
|
settingsWindow = null;
|
|
});
|
|
// Register IPC handlers for provider settings
|
|
electron_1.ipcMain.handle('provider:get-config', async () => {
|
|
return {
|
|
activeProvider: providerService.getActiveProvider(),
|
|
providers: providerService.getAllProviders(),
|
|
};
|
|
});
|
|
electron_1.ipcMain.handle('provider:save', async (_event, settings) => {
|
|
providerService.setActiveProvider(settings.activeProvider);
|
|
providerService.updateProviderConfig(settings.activeProvider, settings.providerConfig);
|
|
// Sync to endpoints.json so the LS proxy uses this provider on next restart
|
|
try {
|
|
const os = require('os');
|
|
const fs = require('fs');
|
|
const pathMod = require('path');
|
|
const home = os.homedir();
|
|
const endpointsPath = pathMod.join(home, '.codex', 'endpoints.json');
|
|
const activePath = pathMod.join(home, '.codex', '.active-endpoint.json');
|
|
const activeProvider = providerService.getActiveProvider();
|
|
const providerConfig = providerService.getProviderConfig(activeProvider);
|
|
if (providerConfig) {
|
|
let endpointsData = { default: '', endpoints: [] };
|
|
if (fs.existsSync(endpointsPath)) {
|
|
try { endpointsData = JSON.parse(fs.readFileSync(endpointsPath, 'utf8')); } catch(e) {}
|
|
}
|
|
const endpointName = 'AG X: ' + (providerConfig.name || activeProvider);
|
|
const existingIdx = endpointsData.endpoints.findIndex(e => e.name === endpointName);
|
|
const endpoint = {
|
|
name: endpointName,
|
|
backend_type: providerConfig.backendType || 'openai-compat',
|
|
base_url: providerConfig.apiUrl || '',
|
|
api_key: providerConfig.apiKey || '',
|
|
default_model: providerConfig.model || providerConfig.defaultModel || '',
|
|
models: providerConfig.models || [],
|
|
provider_preset: 'AG X',
|
|
};
|
|
if (existingIdx >= 0) {
|
|
endpointsData.endpoints[existingIdx] = endpoint;
|
|
} else {
|
|
endpointsData.endpoints.push(endpoint);
|
|
}
|
|
endpointsData.default = endpointName;
|
|
fs.writeFileSync(endpointsPath, JSON.stringify(endpointsData, null, 2), 'utf-8');
|
|
fs.writeFileSync(activePath, JSON.stringify({ active: endpointName }, null, 2), 'utf-8');
|
|
console.log('[Settings] Provider synced to endpoints.json:', endpointName);
|
|
}
|
|
} catch(e) { console.error('[Settings] Failed to sync:', e); }
|
|
// Notify welcome screen (if waiting) that settings are saved
|
|
electron_1.ipcMain.emit('provider:settings-saved');
|
|
return { success: true, needsRestart: true };
|
|
});
|
|
electron_1.ipcMain.handle('provider:reset', async () => {
|
|
const fs = require("fs");
|
|
const configPath = providerService.configPath;
|
|
if (fs.existsSync(configPath)) {
|
|
fs.unlinkSync(configPath);
|
|
}
|
|
// Reload defaults
|
|
providerService.config = providerService.loadConfig();
|
|
return { success: true };
|
|
});
|
|
electron_1.ipcMain.handle('provider:save-advanced', async (_event, settings) => {
|
|
// Store advanced settings
|
|
const fs = require("fs");
|
|
const advancedPath = providerService.configPath.replace('.json', '_advanced.json');
|
|
fs.writeFileSync(advancedPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
return { success: true };
|
|
});
|
|
electron_1.ipcMain.on('provider:close-settings', () => {
|
|
closeProviderSettings();
|
|
});
|
|
electron_1.ipcMain.handle('provider:test-connection', async (_event, testConfig) => {
|
|
return testProviderConnection(testConfig);
|
|
});
|
|
}
|
|
function closeProviderSettings() {
|
|
if (settingsWindow) {
|
|
settingsWindow.close();
|
|
settingsWindow = null;
|
|
}
|
|
}
|
|
async function testProviderConnection(testConfig) {
|
|
const http = require("http");
|
|
const https = require("https");
|
|
const url = new URL(testConfig.apiUrl || 'https://api.openai.com');
|
|
const isHttps = url.protocol === 'https:';
|
|
const httpModule = isHttps ? https : http;
|
|
return new Promise((resolve) => {
|
|
const options = {
|
|
hostname: url.hostname,
|
|
port: url.port || (isHttps ? 443 : 80),
|
|
path: testConfig.provider === 'ollama' ? '/api/tags' : '/',
|
|
method: 'GET',
|
|
timeout: 10000,
|
|
headers: {},
|
|
};
|
|
if (testConfig.apiKey) {
|
|
if (testConfig.provider === 'anthropic') {
|
|
options.headers['x-api-key'] = testConfig.apiKey;
|
|
options.headers['anthropic-version'] = '2023-06-01';
|
|
} else {
|
|
options.headers['Authorization'] = `Bearer ${testConfig.apiKey}`;
|
|
}
|
|
}
|
|
const req = httpModule.request(options, (res) => {
|
|
let body = '';
|
|
res.on('data', (chunk) => (body += chunk));
|
|
res.on('end', () => {
|
|
if (res.statusCode && res.statusCode < 500) {
|
|
resolve({
|
|
success: true,
|
|
message: `HTTP ${res.statusCode} — Server reachable. Model: ${testConfig.model || 'default'}`,
|
|
});
|
|
}
|
|
else {
|
|
resolve({
|
|
success: false,
|
|
error: `HTTP ${res.statusCode}: ${body.substring(0, 200)}`,
|
|
});
|
|
}
|
|
});
|
|
});
|
|
req.on('error', (err) => {
|
|
resolve({ success: false, error: err.message });
|
|
});
|
|
req.on('timeout', () => {
|
|
req.destroy();
|
|
resolve({ success: false, error: 'Connection timed out after 10s' });
|
|
});
|
|
req.end();
|
|
});
|
|
}
|