From 829c532703787350425911f7f27c2be3701641b9 Mon Sep 17 00:00:00 2001 From: Adarsh Pandey <178260003+apfine@users.noreply.github.com> Date: Fri, 13 Mar 2026 00:58:36 +0530 Subject: [PATCH] fix(core/ide): add Antigravity CLI fallbacks (#22030) --- packages/core/src/ide/ide-installer.test.ts | 103 ++++++++++++++++++-- packages/core/src/ide/ide-installer.ts | 33 +++++-- 2 files changed, 122 insertions(+), 14 deletions(-) diff --git a/packages/core/src/ide/ide-installer.test.ts b/packages/core/src/ide/ide-installer.test.ts index 0347fd892f..72c54027a3 100644 --- a/packages/core/src/ide/ide-installer.test.ts +++ b/packages/core/src/ide/ide-installer.test.ts @@ -281,15 +281,105 @@ describe('AntigravityInstaller', () => { ); }); - it('returns a failure message if the alias is not set', async () => { + it('ignores an unsafe alias and falls back to safe commands', async () => { + vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'agy;malicious_command'); + const { installer } = setup(); + vi.mocked(child_process.execSync).mockImplementationOnce(() => 'agy'); + + const result = await installer.install(); + + expect(result.success).toBe(true); + expect(child_process.execSync).toHaveBeenCalledTimes(1); + expect(child_process.execSync).toHaveBeenCalledWith('command -v agy', { + stdio: 'ignore', + }); + expect(child_process.spawnSync).toHaveBeenCalledWith( + 'agy', + [ + '--install-extension', + 'google.gemini-cli-vscode-ide-companion', + '--force', + ], + { stdio: 'pipe', shell: false }, + ); + }); + + it('falls back to antigravity when agy is unavailable on linux', async () => { + vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'agy'); + const { installer } = setup(); + vi.mocked(child_process.execSync) + .mockImplementationOnce(() => { + throw new Error('Command not found'); + }) + .mockImplementationOnce(() => 'antigravity'); + + const result = await installer.install(); + + expect(result.success).toBe(true); + expect(child_process.execSync).toHaveBeenNthCalledWith( + 1, + 'command -v agy', + { + stdio: 'ignore', + }, + ); + expect(child_process.execSync).toHaveBeenNthCalledWith( + 2, + 'command -v antigravity', + { stdio: 'ignore' }, + ); + expect(child_process.spawnSync).toHaveBeenCalledWith( + 'antigravity', + [ + '--install-extension', + 'google.gemini-cli-vscode-ide-companion', + '--force', + ], + { stdio: 'pipe', shell: false }, + ); + }); + + it('falls back to antigravity.cmd when agy.cmd is unavailable on windows', async () => { + vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'agy.cmd'); + const { installer } = setup({ + platform: 'win32', + }); + vi.mocked(child_process.execSync) + .mockImplementationOnce(() => { + throw new Error('Command not found'); + }) + .mockImplementationOnce( + () => 'C:\\Program Files\\Antigravity\\bin\\antigravity.cmd', + ); + + const result = await installer.install(); + + expect(result.success).toBe(true); + expect(child_process.execSync).toHaveBeenNthCalledWith( + 1, + 'where.exe agy.cmd', + ); + expect(child_process.execSync).toHaveBeenNthCalledWith( + 2, + 'where.exe antigravity.cmd', + ); + expect(child_process.spawnSync).toHaveBeenCalledWith( + 'C:\\Program Files\\Antigravity\\bin\\antigravity.cmd', + [ + '--install-extension', + 'google.gemini-cli-vscode-ide-companion', + '--force', + ], + { stdio: 'pipe', shell: true }, + ); + }); + + it('falls back to default commands if the alias is not set', async () => { vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', ''); const { installer } = setup({}); const result = await installer.install(); - expect(result.success).toBe(false); - expect(result.message).toContain( - 'ANTIGRAVITY_CLI_ALIAS environment variable not set', - ); + expect(result.success).toBe(true); }); it('returns a failure message if the command is not found', async () => { @@ -302,6 +392,7 @@ describe('AntigravityInstaller', () => { const result = await installer.install(); expect(result.success).toBe(false); - expect(result.message).toContain('not-a-command not found'); + expect(result.message).toContain('Antigravity CLI not found'); + expect(result.message).toContain('agy, antigravity'); }); }); diff --git a/packages/core/src/ide/ide-installer.ts b/packages/core/src/ide/ide-installer.ts index 886670d4f8..9aeb7739df 100644 --- a/packages/core/src/ide/ide-installer.ts +++ b/packages/core/src/ide/ide-installer.ts @@ -252,19 +252,36 @@ class AntigravityInstaller implements IdeInstaller { ) {} async install(): Promise { - const command = process.env['ANTIGRAVITY_CLI_ALIAS']; - if (!command) { - return { - success: false, - message: 'ANTIGRAVITY_CLI_ALIAS environment variable not set.', - }; + const envCommand = process.env['ANTIGRAVITY_CLI_ALIAS']; + const safeCommandPattern = /^[a-zA-Z0-9.\-_/\\]+$/; + const sanitizedEnvCommand = + envCommand && safeCommandPattern.test(envCommand) + ? envCommand + : undefined; + const fallbackCommands = + this.platform === 'win32' + ? ['agy.cmd', 'antigravity.cmd'] + : ['agy', 'antigravity']; + const commands = [ + ...(sanitizedEnvCommand ? [sanitizedEnvCommand] : []), + ...fallbackCommands, + ].filter( + (command, index, allCommands) => allCommands.indexOf(command) === index, + ); + + let commandPath: string | null = null; + for (const command of commands) { + commandPath = await findCommand(command, this.platform); + if (commandPath) { + break; + } } - const commandPath = await findCommand(command, this.platform); if (!commandPath) { + const supportedCommands = fallbackCommands.join(', '); return { success: false, - message: `${command} not found. Please ensure it is in your system's PATH.`, + message: `Antigravity CLI not found. Please ensure one of these commands is in your system's PATH: ${supportedCommands}.`, }; }