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

269
dist/utils.js vendored Normal file
View File

@@ -0,0 +1,269 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SleepBlocker = exports.showOrCreateWindow = exports.showQuitConfirmation = void 0;
exports.setShowQuitConfirmation = setShowQuitConfirmation;
exports.isMacOS = isMacOS;
exports.createWindow = createWindow;
exports.getNodeWrapperPaths = getNodeWrapperPaths;
exports.setupNodeWrapper = setupNodeWrapper;
const electron_1 = require("electron");
const constants_1 = require("./constants");
const keybindings_1 = require("./keybindings");
const path_1 = __importDefault(require("path"));
const fs = __importStar(require("fs"));
const paths_1 = require("./paths");
const loadingOverlay_1 = require("./loadingOverlay");
exports.showQuitConfirmation = false;
function setShowQuitConfirmation(value) {
exports.showQuitConfirmation = value;
}
function isMacOS() {
return process.platform === 'darwin';
}
/**
* Reads the user's theme preference from the settings file.
*/
function getThemeMode() {
try {
const filePath = (0, paths_1.getSettingsPbPath)();
if (!fs.existsSync(filePath)) {
return 'DARK';
}
const content = fs.readFileSync(filePath, 'utf-8');
const config = JSON.parse(content);
const themeMode = config?.userSettings?.themeMode;
if (themeMode && themeMode.includes('INHERIT')) {
return electron_1.nativeTheme.shouldUseDarkColors ? 'DARK' : 'LIGHT';
}
if (themeMode && themeMode.includes('LIGHT')) {
return 'LIGHT';
}
return 'DARK';
}
catch (e) {
console.error('Error reading theme mode:', e);
return 'DARK';
}
}
/**
* Ensures the app is visible in the dock for MacOS with the icon set.
* When refocusing the app after being hidden in the dock, the icon is sometimes lost.
* This ensures the icon is always visible.
*/
function ensureAppIsInDock() {
void electron_1.app.dock?.show();
if (isMacOS() && electron_1.app.dock) {
const iconPath = path_1.default.join(__dirname, '..', 'icon.png');
electron_1.app.dock.setIcon(electron_1.nativeImage.createFromPath(iconPath));
}
}
// ---------------------------------------------------------------------------
// Window Management
// ---------------------------------------------------------------------------
/**
* Creates and returns a new BrowserWindow pointed at `url`.
* Uses a hidden title bar with native traffic lights on macOS.
* Node integration is disabled and context isolation is enabled for security.
*/
function createWindow(url) {
ensureAppIsInDock();
const theme = getThemeMode().toUpperCase();
const isLight = theme.includes('LIGHT');
const backgroundColor = isLight ? '#FAFAFA' : '#131313';
const foregroundColor = isLight ? '#383A42' : '#FAFAFA';
const win = new electron_1.BrowserWindow({
width: 1400,
height: 900,
title: electron_1.app.getName(),
icon: path_1.default.join(__dirname, '..', 'icon.png'),
titleBarStyle: 'hidden',
titleBarOverlay: isMacOS()
? false
: {
color: backgroundColor,
symbolColor: foregroundColor,
height: 30,
},
backgroundColor,
trafficLightPosition: { x: 12, y: 12 },
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path_1.default.join(__dirname, 'preload.js'),
},
});
win.webContents.setWindowOpenHandler((details) => {
void electron_1.shell.openExternal(details.url);
return { action: 'deny' };
});
(0, loadingOverlay_1.attachLoadingOverlay)(win, foregroundColor, backgroundColor);
(0, keybindings_1.registerKeybindings)(win, {
createNewWindow: () => {
void createWindow(url);
},
onQuitRequested: () => {
exports.showQuitConfirmation = true;
electron_1.app.quit();
},
});
void win.loadURL(url);
return win;
}
/**
* Focuses a window if it exists, or creates a new one.
*/
const showOrCreateWindow = (port) => {
const wins = electron_1.BrowserWindow.getAllWindows();
if (wins.length > 0) {
wins[0].show();
wins[0].focus();
}
else {
createWindow(`${constants_1.WINDOW_ORIGIN}:${port}/`);
}
};
exports.showOrCreateWindow = showOrCreateWindow;
/**
* Manages the power save blocker to keep the computer awake.
*/
class SleepBlocker {
constructor() {
this.currentBlockerId = null;
}
static getInstance() {
if (!SleepBlocker.instance) {
SleepBlocker.instance = new SleepBlocker();
}
return SleepBlocker.instance;
}
shouldKeepComputerAwake(keep) {
if (keep) {
if (this.currentBlockerId === null) {
this.currentBlockerId = electron_1.powerSaveBlocker.start('prevent-display-sleep');
console.log('Power save blocker started:', this.currentBlockerId);
}
}
else {
if (this.currentBlockerId !== null) {
electron_1.powerSaveBlocker.stop(this.currentBlockerId);
console.log('Power save blocker stopped:', this.currentBlockerId);
this.currentBlockerId = null;
}
}
}
}
exports.SleepBlocker = SleepBlocker;
function getNodeWrapperPaths(envPath, os, isPackaged, userDataPath, baseDir) {
const delimiter = os === 'win32' ? ';' : ':';
if (!isPackaged) {
const devBinPath = path_1.default.join(baseDir, '..', 'node_modules', '.bin');
return {
newEnvPath: `${devBinPath}${delimiter}${envPath || ''}`,
nodeWrapperPath: undefined,
binPath: undefined,
};
}
const binPath = path_1.default.join(userDataPath, 'bin');
const nodeWrapperPath = path_1.default.join(binPath, os === 'win32' ? 'agy-node.cmd' : 'agy-node');
return {
newEnvPath: `${binPath}${delimiter}${envPath || ''}`,
nodeWrapperPath,
binPath,
};
}
/**
* Sets up a wrapper script for Node.js that runs Electron as Node.
* This allows running standard Node scripts using the Electron binary.
*/
function setupNodeWrapper(env) {
const userDataPath = electron_1.app.isPackaged ? electron_1.app.getPath('userData') : '';
// Windows environment variables are case-insensitive, but when copying process.env
// into a plain object, we might get 'Path' instead of 'PATH'. We need to find
// the actual key used to avoid creating case-duplicate keys (e.g. 'Path' and 'PATH')
// which can confuse child_process.spawn on Windows.
const isWindows = process.platform === 'win32';
const pathKey = isWindows
? Object.keys(env).find((k) => k.toUpperCase() === 'PATH') || 'PATH'
: 'PATH';
const { newEnvPath, nodeWrapperPath, binPath } = getNodeWrapperPaths(env[pathKey], process.platform, electron_1.app.isPackaged, userDataPath, __dirname);
env[pathKey] = newEnvPath;
// In non-packaged dev mode, we don't create a wrapper and it'll just use machine node
if (!nodeWrapperPath || !binPath) {
return;
}
if (!fs.existsSync(binPath)) {
fs.mkdirSync(binPath, { recursive: true });
}
let nodeWrapperContent = '';
switch (process.platform) {
case 'win32':
nodeWrapperContent = `@echo off\nset ELECTRON_RUN_AS_NODE=1\n"${process.execPath}" %*\n`;
break;
case 'darwin': {
// Use the Helper app instead of the main executable to prevent macOS
// from bouncing a new Dock icon when this script is executed. The Helper
// has LSUIElement=true in its Info.plist, running it invisibly.
const appName = path_1.default.basename(process.execPath);
let electronBinary = process.execPath;
const helperPath = path_1.default.join(path_1.default.dirname(process.execPath), '..', 'Frameworks', `${appName} Helper.app`, 'Contents', 'MacOS', `${appName} Helper`);
if (fs.existsSync(helperPath)) {
electronBinary = helperPath;
}
nodeWrapperContent = `#!/bin/sh\nELECTRON_RUN_AS_NODE=1 exec "${electronBinary}" "$@"\n`;
break;
}
default: // linux, etc.
nodeWrapperContent = `#!/bin/sh\nELECTRON_RUN_AS_NODE=1 exec "${process.execPath}" "$@"\n`;
break;
}
try {
const existingContent = fs.existsSync(nodeWrapperPath)
? fs.readFileSync(nodeWrapperPath, 'utf-8')
: '';
if (existingContent !== nodeWrapperContent) {
fs.writeFileSync(nodeWrapperPath, nodeWrapperContent);
if (process.platform !== 'win32') {
fs.chmodSync(nodeWrapperPath, 0o755);
}
}
}
catch (err) {
console.error(`Failed to create node wrapper: ${err}`);
}
}