feat: complete rebranding to Gemini Nexus with improved UI, proxies and documentation
This commit is contained in:
1 parent
e524a340cb
commit
433dccbaa9
108 files changed
+1004
-1139
No files matched your search
@@ -130,7 +130,7 @@ export async function addAccountSnapshot(): Promise<Account> {
|
||||
const info = getCurrentAccountInfo();
|
||||
if (!info.isAuthenticated) {
|
||||
const errorMsg =
|
||||
'No authenticated account found. Please ensure Antigravity is running and you are logged in.';
|
||||
'No authenticated account found. Please ensure Gemini Nexus is running and you are logged in.';
|
||||
logger.error(errorMsg);
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ export class AuthServer {
|
||||
<html>
|
||||
<body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
|
||||
<h1>Login Successful</h1>
|
||||
<p>You can close this window and return to Antigravity Manager.</p>
|
||||
<p>You can close this window and return to Gemini Nexus.</p>
|
||||
<script>
|
||||
setTimeout(() => window.close(), 3000);
|
||||
</script>
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
readCurrentDeviceProfile,
|
||||
saveGlobalOriginalProfile,
|
||||
} from '../../ipc/device/handler';
|
||||
import { getAntigravityDbPaths } from '../../utils/paths';
|
||||
import { getGeminiNexusDbPaths } from '../../utils/paths';
|
||||
import { runWithSwitchGuard } from '../../ipc/switchGuard';
|
||||
import { executeSwitchFlow } from '../../ipc/switchFlow';
|
||||
import type { DeviceProfile, DeviceProfilesSnapshot } from '../../types/account';
|
||||
@@ -37,7 +37,7 @@ function notifyTrayUpdate(account: CloudAccount) {
|
||||
|
||||
const ACTIVE_OAUTH_CLIENT_KEY_SETTING = 'active_oauth_client_key';
|
||||
const OAUTH_CLIENT_KEY_BACKFILL_DONE_SETTING = 'oauth_client_key_backfill_v1_done';
|
||||
const ENTERPRISE_OAUTH_CLIENT_KEY = 'antigravity_enterprise';
|
||||
const ENTERPRISE_OAUTH_CLIENT_KEY = 'geminiNexus_enterprise';
|
||||
|
||||
function isEnterpriseClient(clientKey?: string): boolean {
|
||||
if (!clientKey) {
|
||||
@@ -548,7 +548,7 @@ export async function switchCloudAccount(accountId: string): Promise<void> {
|
||||
processExitTimeoutMs: 10000,
|
||||
performSwitch: async () => {
|
||||
// 3. Backup Database (Optimized to avoid race conditions)
|
||||
const dbPaths = getAntigravityDbPaths();
|
||||
const dbPaths = getGeminiNexusDbPaths();
|
||||
for (const dbPath of dbPaths) {
|
||||
try {
|
||||
const backupPath = `${dbPath}.backup`;
|
||||
|
||||
@@ -86,7 +86,7 @@ export function toSyncLocalAccountORPCError(error: unknown): ORPCError<string, u
|
||||
normalized.includes('unauthenticated') ||
|
||||
normalized.includes('unauthorized') ||
|
||||
normalized.includes('token may be expired') ||
|
||||
normalized.includes('re-login in antigravity ide')
|
||||
normalized.includes('re-login in geminiNexus ide')
|
||||
) {
|
||||
return new ORPCError('UNAUTHORIZED', { message });
|
||||
}
|
||||
@@ -94,7 +94,7 @@ export function toSyncLocalAccountORPCError(error: unknown): ORPCError<string, u
|
||||
if (
|
||||
normalized.includes('no cloud account found in ide') ||
|
||||
normalized.includes('no oauth token found in ide state') ||
|
||||
normalized.includes('antigravity database not found')
|
||||
normalized.includes('geminiNexus database not found')
|
||||
) {
|
||||
return new ORPCError('BAD_REQUEST', { message });
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { desc, eq } from 'drizzle-orm';
|
||||
import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
||||
import { isNumber, isObjectLike, isPlainObject, isString } from 'lodash-es';
|
||||
import { getCloudAccountsDbPath, getAntigravityDbPaths } from '../../utils/paths';
|
||||
import { getCloudAccountsDbPath, getGeminiNexusDbPaths } from '../../utils/paths';
|
||||
import { logger } from '../../utils/logger';
|
||||
import {
|
||||
CloudAccount,
|
||||
@@ -18,7 +18,7 @@ import { ItemTableValueRowSchema, TableInfoRowSchema } from '../../types/db';
|
||||
import { decryptWithMigration, encrypt, type KeySource } from '../../utils/security';
|
||||
import { ProtobufUtils } from '../../utils/protobuf';
|
||||
import { GoogleAPIService } from '../../services/GoogleAPIService';
|
||||
import { getAntigravityVersion, isNewVersion } from '../../utils/antigravityVersion';
|
||||
import { getGeminiNexusVersion, isNewVersion } from '../../utils/geminiNexusVersion';
|
||||
import { parseRow, parseRows } from '../../utils/sqlite';
|
||||
import { configureDatabase, openDrizzleConnection } from './dbConnection';
|
||||
import { accounts, itemTable, settings } from './schema';
|
||||
@@ -1037,9 +1037,9 @@ export class CloudAccountRepo {
|
||||
apiKey: account.token.access_token,
|
||||
};
|
||||
|
||||
this.upsertItemValue(db, 'antigravityAuthStatus', JSON.stringify(authStatus));
|
||||
this.upsertItemValue(db, 'antigravityOnboarding', 'true');
|
||||
db.delete(itemTable).where(eq(itemTable.key, 'google.antigravity')).run();
|
||||
this.upsertItemValue(db, 'geminiNexusAuthStatus', JSON.stringify(authStatus));
|
||||
this.upsertItemValue(db, 'geminiNexusOnboarding', 'true');
|
||||
db.delete(itemTable).where(eq(itemTable.key, 'google.geminiNexus')).run();
|
||||
}
|
||||
|
||||
private static getItemValue(db: DrizzleExecutor, key: string, context: string): string | null {
|
||||
@@ -1070,8 +1070,8 @@ export class CloudAccountRepo {
|
||||
const normalizedProjectId = account.token.project_id?.trim();
|
||||
|
||||
orm.transaction((tx) => {
|
||||
this.upsertItemValue(tx, 'antigravityUnifiedStateSync.oauthToken', oauthToken);
|
||||
this.upsertItemValue(tx, 'antigravityUnifiedStateSync.userStatus', userStatusEntry);
|
||||
this.upsertItemValue(tx, 'geminiNexusUnifiedStateSync.oauthToken', oauthToken);
|
||||
this.upsertItemValue(tx, 'geminiNexusUnifiedStateSync.userStatus', userStatusEntry);
|
||||
if (normalizedProjectId) {
|
||||
const projectPayload = ProtobufUtils.createStringValuePayload(normalizedProjectId);
|
||||
const projectEntry = ProtobufUtils.createUnifiedStateEntry(
|
||||
@@ -1080,13 +1080,13 @@ export class CloudAccountRepo {
|
||||
);
|
||||
this.upsertItemValue(
|
||||
tx,
|
||||
'antigravityUnifiedStateSync.enterprisePreferences',
|
||||
'geminiNexusUnifiedStateSync.enterprisePreferences',
|
||||
projectEntry,
|
||||
);
|
||||
} else {
|
||||
tx
|
||||
.delete(itemTable)
|
||||
.where(eq(itemTable.key, 'antigravityUnifiedStateSync.enterprisePreferences'))
|
||||
.where(eq(itemTable.key, 'geminiNexusUnifiedStateSync.enterprisePreferences'))
|
||||
.run();
|
||||
}
|
||||
this.writeAuthStatusAndCleanup(tx, account);
|
||||
@@ -1145,8 +1145,8 @@ export class CloudAccountRepo {
|
||||
private static detectFormatCapability(db: DrizzleExecutor): 'new' | 'old' | null {
|
||||
const unifiedValue = this.getItemValue(
|
||||
db,
|
||||
'antigravityUnifiedStateSync.oauthToken',
|
||||
'ide.itemTable.antigravityUnifiedStateSync.oauthToken',
|
||||
'geminiNexusUnifiedStateSync.oauthToken',
|
||||
'ide.itemTable.geminiNexusUnifiedStateSync.oauthToken',
|
||||
);
|
||||
if (unifiedValue) {
|
||||
return 'new';
|
||||
@@ -1169,7 +1169,7 @@ export class CloudAccountRepo {
|
||||
reason: string;
|
||||
} {
|
||||
try {
|
||||
const version = getAntigravityVersion();
|
||||
const version = getGeminiNexusVersion();
|
||||
return {
|
||||
name: isNewVersion(version) ? 'new' : 'old',
|
||||
reason: `version:${version.shortVersion}`,
|
||||
@@ -1253,16 +1253,16 @@ export class CloudAccountRepo {
|
||||
}
|
||||
|
||||
static injectCloudToken(account: CloudAccount): void {
|
||||
const dbPaths = getAntigravityDbPaths();
|
||||
const dbPaths = getGeminiNexusDbPaths();
|
||||
const dbPath = dbPaths.find((p) => fs.existsSync(p)) ?? null;
|
||||
|
||||
if (!dbPath) {
|
||||
throw new Error(`Antigravity database not found. Checked paths: ${dbPaths.join(', ')}`);
|
||||
throw new Error(`Gemini Nexus database not found. Checked paths: ${dbPaths.join(', ')}`);
|
||||
}
|
||||
|
||||
const result = this.injectWithRetry(dbPath, account);
|
||||
logger.info(
|
||||
`Successfully injected cloud token and identity for ${account.email} into Antigravity database at ${dbPath} (strategy=${result.strategy}, attempts=${result.attempts}).`,
|
||||
`Successfully injected cloud token and identity for ${account.email} into Gemini Nexus database at ${dbPath} (strategy=${result.strategy}, attempts=${result.attempts}).`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1312,8 +1312,8 @@ export class CloudAccountRepo {
|
||||
const enterpriseProjectId = this.readEnterpriseProjectIdFromDb(db);
|
||||
const unifiedValue = this.getItemValue(
|
||||
db,
|
||||
'antigravityUnifiedStateSync.oauthToken',
|
||||
'ide.itemTable.antigravityUnifiedStateSync.oauthToken',
|
||||
'geminiNexusUnifiedStateSync.oauthToken',
|
||||
'ide.itemTable.geminiNexusUnifiedStateSync.oauthToken',
|
||||
);
|
||||
|
||||
let tokenInfo: { accessToken: string; refreshToken: string } | null = null;
|
||||
@@ -1336,7 +1336,7 @@ export class CloudAccountRepo {
|
||||
|
||||
if (!value) {
|
||||
const errorMsg =
|
||||
'No cloud account found in IDE. Please login to a Google account in Antigravity IDE first.';
|
||||
'No cloud account found in IDE. Please login to a Google account in Gemini Nexus IDE first.';
|
||||
logger.warn(`SyncLocal: ${errorMsg}`);
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
@@ -1348,7 +1348,7 @@ export class CloudAccountRepo {
|
||||
|
||||
if (!tokenInfo) {
|
||||
const errorMsg =
|
||||
'No OAuth token found in IDE state. Please login to a Google account in Antigravity IDE first.';
|
||||
'No OAuth token found in IDE state. Please login to a Google account in Gemini Nexus IDE first.';
|
||||
logger.warn(`SyncLocal: ${errorMsg}`);
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
@@ -1387,8 +1387,8 @@ export class CloudAccountRepo {
|
||||
private static readEnterpriseProjectIdFromDb(db: DrizzleExecutor): string | undefined {
|
||||
const enterprisePreferencesValue = this.getItemValue(
|
||||
db,
|
||||
'antigravityUnifiedStateSync.enterprisePreferences',
|
||||
'ide.itemTable.antigravityUnifiedStateSync.enterprisePreferences',
|
||||
'geminiNexusUnifiedStateSync.enterprisePreferences',
|
||||
'ide.itemTable.geminiNexusUnifiedStateSync.enterprisePreferences',
|
||||
);
|
||||
if (!enterprisePreferencesValue) {
|
||||
return undefined;
|
||||
@@ -1421,7 +1421,7 @@ export class CloudAccountRepo {
|
||||
|
||||
static async syncFromIDE(): Promise<CloudAccount | null> {
|
||||
// Try all possible database paths
|
||||
const dbPaths = getAntigravityDbPaths();
|
||||
const dbPaths = getGeminiNexusDbPaths();
|
||||
logger.info(`SyncLocal: Checking database paths: ${JSON.stringify(dbPaths)}`);
|
||||
|
||||
const dbPath =
|
||||
@@ -1431,12 +1431,12 @@ export class CloudAccountRepo {
|
||||
}) ?? null;
|
||||
|
||||
if (!dbPath) {
|
||||
const errorMsg = `Antigravity database not found. Please ensure Antigravity IDE is installed. Checked paths: ${dbPaths.join(', ')}`;
|
||||
const errorMsg = `Gemini Nexus database not found. Please ensure Gemini Nexus IDE is installed. Checked paths: ${dbPaths.join(', ')}`;
|
||||
logger.error(errorMsg);
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
logger.info(`SyncLocal: Using Antigravity database at: ${dbPath}`);
|
||||
logger.info(`SyncLocal: Using Gemini Nexus database at: ${dbPath}`);
|
||||
try {
|
||||
const tokenInfo = this.readTokenInfoWithRetry(dbPath);
|
||||
|
||||
@@ -1446,7 +1446,7 @@ export class CloudAccountRepo {
|
||||
try {
|
||||
userInfo = await GoogleAPIService.getUserInfo(tokenInfo.accessToken);
|
||||
} catch (apiError: any) {
|
||||
const errorMsg = `Failed to validate token with Google API. The token may be expired. Please re-login in Antigravity IDE. Error: ${apiError.message}`;
|
||||
const errorMsg = `Failed to validate token with Google API. The token may be expired. Please re-login in Gemini Nexus IDE. Error: ${apiError.message}`;
|
||||
logger.error(`SyncLocal: ${errorMsg}`, apiError);
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
+17
-17
@@ -6,15 +6,15 @@ import { isString } from 'lodash-es';
|
||||
import { AccountBackupData, AccountInfo } from '../../types/account';
|
||||
import { ItemTableValueRowSchema, type ItemTableKey } from '../../types/db';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { getAntigravityDbPaths } from '../../utils/paths';
|
||||
import { getGeminiNexusDbPaths } from '../../utils/paths';
|
||||
import { parseRow } from '../../utils/sqlite';
|
||||
import { openDrizzleConnection } from './dbConnection';
|
||||
import { itemTable } from './schema';
|
||||
|
||||
const KEYS_TO_BACKUP: ItemTableKey[] = [
|
||||
'antigravityAuthStatus',
|
||||
'geminiNexusAuthStatus',
|
||||
'jetskiStateSync.agentManagerInitState',
|
||||
'antigravityUnifiedStateSync.oauthToken',
|
||||
'geminiNexusUnifiedStateSync.oauthToken',
|
||||
];
|
||||
|
||||
function openIdeDb(dbPath: string, readOnly = false): ReturnType<typeof openDrizzleConnection> {
|
||||
@@ -31,7 +31,7 @@ function openIdeDb(dbPath: string, readOnly = false): ReturnType<typeof openDriz
|
||||
*/
|
||||
export function initDatabase(): void {
|
||||
try {
|
||||
const dbPaths = getAntigravityDbPaths();
|
||||
const dbPaths = getGeminiNexusDbPaths();
|
||||
if (dbPaths.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -86,10 +86,10 @@ function ensureDatabaseExists(dbPath: string): void {
|
||||
* @returns {ReturnType<typeof openDrizzleConnection>} The database connection.
|
||||
*/
|
||||
export function getDatabaseConnection(dbPath?: string): ReturnType<typeof openDrizzleConnection> {
|
||||
const targetPath = dbPath || getAntigravityDbPaths()[0];
|
||||
const targetPath = dbPath || getGeminiNexusDbPaths()[0];
|
||||
|
||||
if (!targetPath) {
|
||||
throw new Error('No Antigravity database path found');
|
||||
throw new Error('No Gemini Nexus database path found');
|
||||
}
|
||||
|
||||
ensureDatabaseExists(targetPath);
|
||||
@@ -99,7 +99,7 @@ export function getDatabaseConnection(dbPath?: string): ReturnType<typeof openDr
|
||||
} catch (error: unknown) {
|
||||
const err = error as { code?: string };
|
||||
if (err.code === 'SQLITE_BUSY' || err.code === 'SQLITE_LOCKED') {
|
||||
throw new Error('Database is locked. Please close Antigravity before proceeding.');
|
||||
throw new Error('Database is locked. Please close Gemini Nexus before proceeding.');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -133,8 +133,8 @@ export function getCurrentAccountInfo(): AccountInfo {
|
||||
// Query for auth status
|
||||
const authValue = readItemValue(
|
||||
orm,
|
||||
'antigravityAuthStatus',
|
||||
'ide.itemTable.antigravityAuthStatus',
|
||||
'geminiNexusAuthStatus',
|
||||
'ide.itemTable.geminiNexusAuthStatus',
|
||||
);
|
||||
let authStatus = null;
|
||||
if (authValue) {
|
||||
@@ -160,11 +160,11 @@ export function getCurrentAccountInfo(): AccountInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// Query for google.antigravity
|
||||
// Query for google.geminiNexus
|
||||
const googleValue = readItemValue(
|
||||
orm,
|
||||
'google.antigravity',
|
||||
'ide.itemTable.google.antigravity',
|
||||
'google.geminiNexus',
|
||||
'ide.itemTable.google.geminiNexus',
|
||||
);
|
||||
let googleState = null;
|
||||
if (googleValue) {
|
||||
@@ -175,11 +175,11 @@ export function getCurrentAccountInfo(): AccountInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// Query for antigravityUserSettings.allUserSettings
|
||||
// Query for geminiNexusUserSettings.allUserSettings
|
||||
const settingsValue = readItemValue(
|
||||
orm,
|
||||
'antigravityUserSettings.allUserSettings',
|
||||
'ide.itemTable.antigravityUserSettings.allUserSettings',
|
||||
'geminiNexusUserSettings.allUserSettings',
|
||||
'ide.itemTable.geminiNexusUserSettings.allUserSettings',
|
||||
);
|
||||
let settingsState = null;
|
||||
if (settingsValue) {
|
||||
@@ -273,9 +273,9 @@ export function backupAccount(account: AccountBackupData['account']): AccountBac
|
||||
* @throws {Error} If the backup data cannot be restored.
|
||||
*/
|
||||
export function restoreAccount(backup: AccountBackupData): void {
|
||||
const dbPaths = getAntigravityDbPaths();
|
||||
const dbPaths = getGeminiNexusDbPaths();
|
||||
if (dbPaths.length === 0) {
|
||||
throw new Error('No Antigravity database paths found');
|
||||
throw new Error('No Gemini Nexus database paths found');
|
||||
}
|
||||
|
||||
let successCount = 0;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createHash, randomBytes } from 'crypto';
|
||||
import { isBoolean, isNumber, isObjectLike, isString } from 'lodash-es';
|
||||
import { type DeviceProfile } from '../../types/account';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { getAgentDir, getAntigravityDbPaths, getAntigravityStoragePaths } from '../../utils/paths';
|
||||
import { getAgentDir, getGeminiNexusDbPaths, getGeminiNexusStoragePaths } from '../../utils/paths';
|
||||
|
||||
const GLOBAL_BASELINE_FILE = 'device_original.json';
|
||||
const SQLITE_RETRY_COUNT = 3;
|
||||
@@ -508,7 +508,7 @@ export function isIdentityProfileApplyEnabled(): boolean {
|
||||
}
|
||||
|
||||
export function getStoragePath(): string {
|
||||
const storagePath = resolveExistingPath(getAntigravityStoragePaths());
|
||||
const storagePath = resolveExistingPath(getGeminiNexusStoragePaths());
|
||||
if (!storagePath) {
|
||||
throw new Error('storage_json_not_found');
|
||||
}
|
||||
@@ -594,7 +594,7 @@ export function generateDeviceProfile(): DeviceProfile {
|
||||
}
|
||||
|
||||
export function syncStateServiceMachineIdValue(serviceMachineId: string, dbPath?: string): void {
|
||||
const targetDbPath = dbPath || resolveExistingPath(getAntigravityDbPaths());
|
||||
const targetDbPath = dbPath || resolveExistingPath(getGeminiNexusDbPaths());
|
||||
if (!targetDbPath) {
|
||||
throw new Error('state_vscdb_not_found');
|
||||
}
|
||||
|
||||
+70
-70
@@ -2,7 +2,7 @@ import { exec, execSync, spawn } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import findProcess, { ProcessInfo } from 'find-process';
|
||||
import { isNumber } from 'lodash-es';
|
||||
import { getAntigravityExecutablePath, isWsl } from '../../utils/paths';
|
||||
import { getGeminiNexusExecutablePath, isWsl } from '../../utils/paths';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
@@ -61,24 +61,24 @@ function isPgrepNoMatchError(error: unknown): boolean {
|
||||
}
|
||||
|
||||
const message = error.message.toLowerCase();
|
||||
const hasPgrep = message.includes('pgrep') && message.includes('antigravity');
|
||||
const hasPgrep = message.includes('pgrep') && message.includes('geminiNexus');
|
||||
const code = (error as { code?: number }).code;
|
||||
return hasPgrep && code === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Antigravity process is running.
|
||||
* Checks if the Gemini Nexus process is running.
|
||||
* Uses find-process package for robust cross-platform process detection.
|
||||
* @returns {boolean} True if the Antigravity process is running, false otherwise.
|
||||
* @returns {boolean} True if the Gemini Nexus process is running, false otherwise.
|
||||
*/
|
||||
export async function isProcessRunning(): Promise<boolean> {
|
||||
try {
|
||||
const platform = process.platform;
|
||||
const currentPid = process.pid;
|
||||
|
||||
// Use find-process to search for Antigravity processes
|
||||
// Use find-process to search for Gemini Nexus processes
|
||||
const allMatches: ProcessInfo[] = [];
|
||||
const searchNames = ['Antigravity', 'antigravity'];
|
||||
const searchNames = ['Gemini Nexus', 'geminiNexus'];
|
||||
if (platform === 'linux') {
|
||||
searchNames.push('electron');
|
||||
}
|
||||
@@ -106,10 +106,10 @@ export async function isProcessRunning(): Promise<boolean> {
|
||||
|
||||
const processes = Array.from(processMap.values());
|
||||
if (processes.length === 0 && sawNoMatch) {
|
||||
logger.debug('No Antigravity process found (pgrep returned 1)');
|
||||
logger.debug('No Gemini Nexus process found (pgrep returned 1)');
|
||||
}
|
||||
|
||||
logger.debug(`Found ${processes.length} processes matching 'Antigravity/antigravity'`);
|
||||
logger.debug(`Found ${processes.length} processes matching 'GeminiNexus/geminiNexus'`);
|
||||
|
||||
for (const proc of processes) {
|
||||
// Skip self
|
||||
@@ -124,7 +124,7 @@ export async function isProcessRunning(): Promise<boolean> {
|
||||
if (
|
||||
name.includes('manager') ||
|
||||
cmd.includes('manager') ||
|
||||
cmd.includes('antigravity-manager')
|
||||
cmd.includes('gemini-nexus')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -135,22 +135,22 @@ export async function isProcessRunning(): Promise<boolean> {
|
||||
}
|
||||
|
||||
if (platform === 'darwin') {
|
||||
// macOS: Check for Antigravity.app in path
|
||||
if (cmd.includes('antigravity.app')) {
|
||||
// macOS: Check for Gemini Nexus.app in path
|
||||
if (cmd.includes('geminiNexus.app')) {
|
||||
logger.debug(
|
||||
`Found Antigravity process: PID=${proc.pid}, name=${name}, cmd=${cmd.substring(0, 100)}`,
|
||||
`Found Gemini Nexus process: PID=${proc.pid}, name=${name}, cmd=${cmd.substring(0, 100)}`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
// Also check if the process name is exactly 'Antigravity' (main process)
|
||||
if (name === 'antigravity' && !isHelperProcess(name, cmd)) {
|
||||
logger.debug(`Found Antigravity process: PID=${proc.pid}, name=${name}`);
|
||||
// Also check if the process name is exactly 'Gemini Nexus' (main process)
|
||||
if (name === 'geminiNexus' && !isHelperProcess(name, cmd)) {
|
||||
logger.debug(`Found Gemini Nexus process: PID=${proc.pid}, name=${name}`);
|
||||
return true;
|
||||
}
|
||||
} else if (platform === 'win32') {
|
||||
// Windows: Check for Antigravity.exe
|
||||
if (name === 'antigravity.exe' || name === 'antigravity') {
|
||||
logger.debug(`Found Antigravity process: PID=${proc.pid}, name=${name}`);
|
||||
// Windows: Check for Gemini Nexus.exe
|
||||
if (name === 'geminiNexus.exe' || name === 'geminiNexus') {
|
||||
logger.debug(`Found Gemini Nexus process: PID=${proc.pid}, name=${name}`);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -159,28 +159,28 @@ export async function isProcessRunning(): Promise<boolean> {
|
||||
|
||||
if (nameLower === 'electron') {
|
||||
// Stricter check for AUR/Electron wrapper:
|
||||
// Must include antigravity in command line but NOT manager or tools
|
||||
const isAntigravityApp =
|
||||
(cmdLower.includes('/antigravity') ||
|
||||
cmdLower.includes(' antigravity') ||
|
||||
cmdLower.endsWith('antigravity')) &&
|
||||
// Must include geminiNexus in command line but NOT manager or tools
|
||||
const isGeminiNexusApp =
|
||||
(cmdLower.includes('/geminiNexus') ||
|
||||
cmdLower.includes(' geminiNexus') ||
|
||||
cmdLower.endsWith('geminiNexus')) &&
|
||||
!cmdLower.includes('manager') &&
|
||||
!cmdLower.includes('tools');
|
||||
|
||||
if (isAntigravityApp) {
|
||||
if (isGeminiNexusApp) {
|
||||
logger.debug(
|
||||
`Found Antigravity (AUR/electron) process: PID=${proc.pid}, name=${name}, cmd=${cmd.substring(0, 100)}`,
|
||||
`Found Gemini Nexus (AUR/electron) process: PID=${proc.pid}, name=${name}, cmd=${cmd.substring(0, 100)}`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(name.includes('antigravity') || cmd.includes('/antigravity')) &&
|
||||
(name.includes('geminiNexus') || cmd.includes('/geminiNexus')) &&
|
||||
!name.includes('tools')
|
||||
) {
|
||||
logger.debug(
|
||||
`Found Antigravity process: PID=${proc.pid}, name=${name}, cmd=${cmd.substring(0, 100)}`,
|
||||
`Found Gemini Nexus process: PID=${proc.pid}, name=${name}, cmd=${cmd.substring(0, 100)}`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -195,11 +195,11 @@ export async function isProcessRunning(): Promise<boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the Antigravity process.
|
||||
* @returns {boolean} True if the Antigravity process is running, false otherwise.
|
||||
* Closes the Gemini Nexus process.
|
||||
* @returns {boolean} True if the Gemini Nexus process is running, false otherwise.
|
||||
*/
|
||||
export async function closeAntigravity(): Promise<void> {
|
||||
logger.info('Closing Antigravity...');
|
||||
export async function closeGeminiNexus(): Promise<void> {
|
||||
logger.info('Closing Gemini Nexus...');
|
||||
const platform = process.platform;
|
||||
|
||||
try {
|
||||
@@ -208,7 +208,7 @@ export async function closeAntigravity(): Promise<void> {
|
||||
// macOS: Use AppleScript to quit gracefully
|
||||
try {
|
||||
logger.info('Attempting graceful exit via AppleScript...');
|
||||
execSync('osascript -e \'tell application "Antigravity" to quit\'', {
|
||||
execSync('osascript -e \'tell application "Gemini Nexus" to quit\'', {
|
||||
stdio: 'ignore',
|
||||
timeout: 3000,
|
||||
});
|
||||
@@ -223,7 +223,7 @@ export async function closeAntigravity(): Promise<void> {
|
||||
logger.info('Attempting graceful exit via taskkill...');
|
||||
// /T = Tree (child processes), /IM = Image Name
|
||||
// We do not wait long here.
|
||||
execSync('taskkill /IM "Antigravity.exe" /T', {
|
||||
execSync('taskkill /IM "GeminiNexus.exe" /T', {
|
||||
stdio: 'ignore',
|
||||
timeout: 2000,
|
||||
});
|
||||
@@ -243,7 +243,7 @@ export async function closeAntigravity(): Promise<void> {
|
||||
let output = '';
|
||||
if (platform === 'win32') {
|
||||
const psCommand = (cmdlet: string) =>
|
||||
`powershell -NoProfile -Command "${cmdlet} Win32_Process -Filter \\"Name like 'Antigravity%'\\" | Select-Object ProcessId, Name, CommandLine | ConvertTo-Csv -NoTypeInformation"`;
|
||||
`powershell -NoProfile -Command "${cmdlet} Win32_Process -Filter \\"Name like 'GeminiNexus%'\\" | Select-Object ProcessId, Name, CommandLine | ConvertTo-Csv -NoTypeInformation"`;
|
||||
|
||||
try {
|
||||
output = execSync(psCommand('Get-CimInstance'), {
|
||||
@@ -304,7 +304,7 @@ export async function closeAntigravity(): Promise<void> {
|
||||
const pid = parseInt(parts[0]);
|
||||
if (isNaN(pid)) continue;
|
||||
const rest = parts.slice(1).join(' ');
|
||||
if (rest.includes('Antigravity') || rest.includes('antigravity')) {
|
||||
if (rest.includes('Gemini Nexus') || rest.includes('geminiNexus')) {
|
||||
processList.push({ pid, name: parts[1], cmd: rest });
|
||||
}
|
||||
}
|
||||
@@ -321,31 +321,31 @@ export async function closeAntigravity(): Promise<void> {
|
||||
if (p.pid === currentPid) {
|
||||
return false;
|
||||
}
|
||||
// Exclude this electron app (if named Antigravity Manager or antigravity-manager)
|
||||
if (p.cmd.includes('Antigravity Manager') || p.cmd.includes('antigravity-manager')) {
|
||||
// Exclude this electron app (if named Gemini Nexus or gemini-nexus)
|
||||
if (p.cmd.includes('Gemini Nexus') || p.cmd.includes('gemini-nexus')) {
|
||||
return false;
|
||||
}
|
||||
// Match Antigravity (but not manager)
|
||||
// Match Gemini Nexus (but not manager)
|
||||
if (platform === 'win32') {
|
||||
return (
|
||||
p.cmd.includes('Antigravity.exe') ||
|
||||
(p.cmd.includes('antigravity') && !p.cmd.includes('manager'))
|
||||
p.cmd.includes('GeminiNexus.exe') ||
|
||||
(p.cmd.includes('geminiNexus') && !p.cmd.includes('manager'))
|
||||
);
|
||||
} else {
|
||||
// Explicit !manager check for Linux/macOS to be defensive
|
||||
return (
|
||||
(p.cmd.includes('Antigravity') || p.cmd.includes('antigravity')) &&
|
||||
(p.cmd.includes('Gemini Nexus') || p.cmd.includes('geminiNexus')) &&
|
||||
!p.cmd.includes('manager')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (targetProcessList.length === 0) {
|
||||
logger.info('No Antigravity processes found running.');
|
||||
logger.info('No Gemini Nexus processes found running.');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`Found ${targetProcessList.length} remaining Antigravity processes. Killing...`);
|
||||
logger.info(`Found ${targetProcessList.length} remaining Gemini Nexus processes. Killing...`);
|
||||
|
||||
for (const p of targetProcessList) {
|
||||
try {
|
||||
@@ -355,13 +355,13 @@ export async function closeAntigravity(): Promise<void> {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error closing Antigravity', error);
|
||||
logger.error('Error closing Gemini Nexus', error);
|
||||
// Fallback to simple kill if everything fails
|
||||
try {
|
||||
if (platform === 'win32') {
|
||||
execSync('taskkill /F /IM "Antigravity.exe" /T', { stdio: 'ignore' });
|
||||
execSync('taskkill /F /IM "GeminiNexus.exe" /T', { stdio: 'ignore' });
|
||||
} else {
|
||||
execSync('pkill -9 -f Antigravity', { stdio: 'ignore' });
|
||||
execSync('pkill -9 -f Gemini Nexus', { stdio: 'ignore' });
|
||||
}
|
||||
} catch {
|
||||
// Ignore
|
||||
@@ -370,7 +370,7 @@ export async function closeAntigravity(): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the Antigravity process to exit.
|
||||
* Waits for the Gemini Nexus process to exit.
|
||||
* @param timeoutMs {number} The timeout in milliseconds.
|
||||
* @returns {Promise<void>} A promise that resolves when the process exits.
|
||||
*/
|
||||
@@ -385,7 +385,7 @@ export async function _waitForProcessExit(
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
||||
}
|
||||
throw new Error(`Antigravity process did not exit within ${timeoutMs}ms`);
|
||||
throw new Error(`Gemini Nexus process did not exit within ${timeoutMs}ms`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,7 +418,7 @@ async function openUri(uri: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForAntigravityStartup(
|
||||
async function waitForGeminiNexusStartup(
|
||||
timeoutMs = PROCESS_STARTUP_TIMEOUT_MS,
|
||||
pollIntervalMs = PROCESS_STARTUP_POLL_INTERVAL_MS,
|
||||
): Promise<boolean> {
|
||||
@@ -441,15 +441,15 @@ function shouldUseLinuxGpuSafeLaunchArgs(): boolean {
|
||||
return enableLinuxGpuRaw !== '1' && enableLinuxGpuRaw !== 'true';
|
||||
}
|
||||
|
||||
async function startAntigravityByExecutable(execPath: string): Promise<void> {
|
||||
async function startGeminiNexusByExecutable(execPath: string): Promise<void> {
|
||||
if (process.platform === 'darwin') {
|
||||
await execAsync(`open -a Antigravity`);
|
||||
await execAsync(`open -a Gemini Nexus`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
if (!execPath) {
|
||||
throw new Error('Unable to locate Antigravity executable path');
|
||||
throw new Error('Unable to locate Gemini Nexus executable path');
|
||||
}
|
||||
// Use start command to detach
|
||||
await execAsync(`start "" "${execPath}"`);
|
||||
@@ -458,7 +458,7 @@ async function startAntigravityByExecutable(execPath: string): Promise<void> {
|
||||
|
||||
if (isWsl()) {
|
||||
if (!execPath) {
|
||||
throw new Error('Unable to locate Antigravity executable path');
|
||||
throw new Error('Unable to locate Gemini Nexus executable path');
|
||||
}
|
||||
// In WSL, convert path and use cmd.exe
|
||||
const winPath = execPath
|
||||
@@ -470,7 +470,7 @@ async function startAntigravityByExecutable(execPath: string): Promise<void> {
|
||||
}
|
||||
|
||||
if (!execPath) {
|
||||
throw new Error('Unable to locate Antigravity executable path');
|
||||
throw new Error('Unable to locate Gemini Nexus executable path');
|
||||
}
|
||||
|
||||
const launchArgs = shouldUseLinuxGpuSafeLaunchArgs() ? [...LINUX_GPU_SAFE_LAUNCH_ARGS] : [];
|
||||
@@ -486,36 +486,36 @@ async function startAntigravityByExecutable(execPath: string): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Antigravity process.
|
||||
* @param useUri {boolean} Whether to use the URI protocol to start Antigravity.
|
||||
* Starts the Gemini Nexus process.
|
||||
* @param useUri {boolean} Whether to use the URI protocol to start Gemini Nexus.
|
||||
* @returns {Promise<void>} A promise that resolves when the process starts.
|
||||
*/
|
||||
export async function startAntigravity(useUri = true): Promise<void> {
|
||||
logger.info('Starting Antigravity...');
|
||||
export async function startGeminiNexus(useUri = true): Promise<void> {
|
||||
logger.info('Starting Gemini Nexus...');
|
||||
|
||||
if (await isProcessRunning()) {
|
||||
logger.info('Antigravity is already running');
|
||||
logger.info('Gemini Nexus is already running');
|
||||
return;
|
||||
}
|
||||
|
||||
if (useUri) {
|
||||
logger.info('Using URI protocol to start...');
|
||||
const uri = 'antigravity://oauth-success';
|
||||
const uri = 'geminiNexus://oauth-success';
|
||||
|
||||
if (await openUri(uri)) {
|
||||
logger.info('Antigravity URI launch command sent');
|
||||
logger.info('Gemini Nexus URI launch command sent');
|
||||
|
||||
if (process.platform !== 'linux' || isWsl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await waitForAntigravityStartup()) {
|
||||
logger.info('Antigravity process detected after URI launch');
|
||||
if (await waitForGeminiNexusStartup()) {
|
||||
logger.info('Gemini Nexus process detected after URI launch');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
'URI launch did not keep Antigravity running on Linux. Falling back to executable launch.',
|
||||
'URI launch did not keep Gemini Nexus running on Linux. Falling back to executable launch.',
|
||||
);
|
||||
} else {
|
||||
logger.warn('URI launch failed, trying executable path...');
|
||||
@@ -524,22 +524,22 @@ export async function startAntigravity(useUri = true): Promise<void> {
|
||||
|
||||
// Fallback to executable path
|
||||
logger.info('Using executable path to start...');
|
||||
const execPath = getAntigravityExecutablePath();
|
||||
const execPath = getGeminiNexusExecutablePath();
|
||||
|
||||
try {
|
||||
await startAntigravityByExecutable(execPath);
|
||||
logger.info('Antigravity launch command sent');
|
||||
await startGeminiNexusByExecutable(execPath);
|
||||
logger.info('Gemini Nexus launch command sent');
|
||||
|
||||
if (process.platform === 'linux' && !isWsl()) {
|
||||
const started = await waitForAntigravityStartup();
|
||||
const started = await waitForGeminiNexusStartup();
|
||||
if (!started) {
|
||||
logger.warn(
|
||||
'Antigravity launch command completed, but process startup could not be confirmed on Linux.',
|
||||
'Gemini Nexus launch command completed, but process startup could not be confirmed on Linux.',
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to start Antigravity via executable', error);
|
||||
logger.error('Failed to start Gemini Nexus via executable', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -9,7 +9,7 @@ import { gatewayRouter } from './gateway/router';
|
||||
|
||||
import { os } from '@orpc/server';
|
||||
import { z } from 'zod';
|
||||
import { isProcessRunning, closeAntigravity, startAntigravity } from './process/handler';
|
||||
import { isProcessRunning, closeGeminiNexus, startGeminiNexus } from './process/handler';
|
||||
import { systemHandler } from './system/handler';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
@@ -41,11 +41,11 @@ export const router = os.use(logMiddleware).router({
|
||||
isProcessRunning: os.output(z.boolean()).handler(async () => {
|
||||
return await isProcessRunning();
|
||||
}),
|
||||
closeAntigravity: os.output(z.void()).handler(async () => {
|
||||
await closeAntigravity();
|
||||
closeGeminiNexus: os.output(z.void()).handler(async () => {
|
||||
await closeGeminiNexus();
|
||||
}),
|
||||
startAntigravity: os.output(z.void()).handler(async () => {
|
||||
await startAntigravity();
|
||||
startGeminiNexus: os.output(z.void()).handler(async () => {
|
||||
await startGeminiNexus();
|
||||
}),
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type DeviceProfile } from '../types/account';
|
||||
import { logger } from '../utils/logger';
|
||||
import { closeAntigravity, startAntigravity, _waitForProcessExit } from './process/handler';
|
||||
import { closeGeminiNexus, startGeminiNexus, _waitForProcessExit } from './process/handler';
|
||||
import { applyDeviceProfile } from './device/handler';
|
||||
import {
|
||||
type SwitchFailureReason,
|
||||
@@ -55,7 +55,7 @@ export async function executeSwitchFlow(options: SwitchFlowOptions): Promise<voi
|
||||
|
||||
let stage = 'close';
|
||||
try {
|
||||
await closeAntigravity();
|
||||
await closeGeminiNexus();
|
||||
try {
|
||||
await _waitForProcessExit(processExitTimeoutMs);
|
||||
} catch (error) {
|
||||
@@ -78,7 +78,7 @@ export async function executeSwitchFlow(options: SwitchFlowOptions): Promise<voi
|
||||
stage = 'switch';
|
||||
await performSwitch();
|
||||
stage = 'start';
|
||||
await startAntigravity();
|
||||
await startGeminiNexus();
|
||||
recordSwitchSuccess(scope);
|
||||
} catch (error) {
|
||||
const reason = toSwitchFailureReason(stage, error);
|
||||
|
||||
@@ -69,7 +69,7 @@ export function initTray(mainWindow: BrowserWindow) {
|
||||
}
|
||||
|
||||
tray = new Tray(icon);
|
||||
tray.setToolTip('Antigravity Manager');
|
||||
tray.setToolTip('Gemini Nexus');
|
||||
|
||||
tray.on('double-click', () => {
|
||||
if (globalMainWindow) {
|
||||
|
||||
Reference in new issue
Block a user