AG X v2.0.3 - Antigravity fork with multi-provider support

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
This commit is contained in:
admin
2026-05-22 23:20:10 +04:00
Unverified
commit 43e2a2f78f
46 changed files with 7719 additions and 0 deletions

169
dist/providerSettings.js vendored Normal file
View File

@@ -0,0 +1,169 @@
"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();
});
}