basant307/AI_Governance_Project
048
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import type { UpdateObject } from '../ui/utils/updateCheck.js';8import type { LoadedSettings } from '../config/settings.js';9import { getInstallationInfo } from './installationInfo.js';10import { updateEventEmitter } from './updateEventEmitter.js';11import type { HistoryItemWithoutId } from '../ui/types.js';12import { MessageType } from '../ui/types.js';13import { spawnWrapper } from './spawnWrapper.js';14import { performStandaloneUpdate } from './standalone-update.js';15import type { spawn } from 'node:child_process';16import os from 'node:os';17 18const UPDATE_SUCCESS_MESSAGE =19 'Update successful! Please restart Qwen Code to use the new version. ' +20 'Switching model providers before restarting may not work correctly.';21const UPDATE_FAILED_MESSAGE =22 'Automatic update failed. Please try updating manually.';23 24export function handleAutoUpdate(25 info: UpdateObject | null,26 settings: LoadedSettings,27 projectRoot: string,28 spawnFn: typeof spawn = spawnWrapper,29) {30 if (!info) {31 return;32 }33 34 // enableAutoUpdate is checked in gemini.tsx before calling this function,35 // so if we get here, auto-update is enabled (or undefined, which defaults to enabled).36 const isAutoUpdateEnabled =37 settings.merged.general?.enableAutoUpdate !== false;38 39 const installationInfo = getInstallationInfo(40 projectRoot,41 isAutoUpdateEnabled,42 );43 44 let combinedMessage = info.message;45 if (installationInfo.updateMessage) {46 combinedMessage += `\n${installationInfo.updateMessage}`;47 }48 49 updateEventEmitter.emit('update-received', {50 message: combinedMessage,51 });52 53 if (54 installationInfo.isStandalone &&55 installationInfo.standaloneDir &&56 isAutoUpdateEnabled57 ) {58 performStandaloneUpdate(installationInfo.standaloneDir, info.update.latest)59 .then((result) => {60 const message =61 result === 'deferred'62 ? 'Update downloaded. It will be applied after you exit this session.'63 : 'Update successful! The new version will be used on your next run.';64 updateEventEmitter.emit('update-success', { message });65 })66 .catch((err: Error) => {67 updateEventEmitter.emit('update-failed', {68 message: `Automatic update failed: ${err.message}. Re-run the installer to update manually.`,69 });70 });71 return;72 }73 74 // Don't automatically run the update if auto-update is disabled or no update command75 if (!installationInfo.updateCommand || !isAutoUpdateEnabled) {76 return;77 }78 const isNightly = info.update.latest.includes('nightly');79 80 const updateCommand = installationInfo.updateCommand.replace(81 '@latest',82 isNightly ? '@nightly' : `@${info.update.latest}`,83 );84 const isWindows = os.platform() === 'win32';85 const shell = isWindows ? 'cmd.exe' : 'bash';86 const shellArgs = isWindows ? ['/c', updateCommand] : ['-c', updateCommand];87 const updateProcess = spawnFn(shell, shellArgs, { stdio: 'pipe' });88 let errorOutput = '';89 updateProcess.stderr.on('data', (data) => {90 errorOutput += data.toString();91 });92 93 updateProcess.on('close', (code) => {94 if (code === 0) {95 updateEventEmitter.emit('update-success', {96 message: UPDATE_SUCCESS_MESSAGE,97 });98 } else {99 updateEventEmitter.emit('update-failed', {100 message: `${UPDATE_FAILED_MESSAGE} (command: ${updateCommand}, stderr: ${errorOutput.trim()})`,101 });102 }103 });104 105 updateProcess.on('error', (err) => {106 updateEventEmitter.emit('update-failed', {107 message: `${UPDATE_FAILED_MESSAGE} (error: ${err.message})`,108 });109 });110 return updateProcess;111}112 113export function setUpdateHandler(114 addItem: (item: HistoryItemWithoutId, timestamp: number) => void,115 setUpdateInfo: (info: UpdateObject | null) => void,116 isIdleRef: { current: boolean } = { current: true },117) {118 let successfullyInstalled = false;119 const pendingNotifications: HistoryItemWithoutId[] = [];120 121 const addItemOrDefer = (item: HistoryItemWithoutId) => {122 if (isIdleRef.current) {123 addItem(item, Date.now());124 } else {125 pendingNotifications.push(item);126 }127 };128 129 const handleUpdateRecieved = (info: UpdateObject) => {130 setUpdateInfo(info);131 const savedMessage = info.message;132 setTimeout(() => {133 if (!successfullyInstalled) {134 addItemOrDefer({135 type: MessageType.INFO,136 text: savedMessage,137 });138 }139 setUpdateInfo(null);140 }, 60000);141 };142 143 const handleUpdateFailed = (data?: { message?: string }) => {144 setUpdateInfo(null);145 addItemOrDefer({146 type: MessageType.ERROR,147 text: data?.message ?? UPDATE_FAILED_MESSAGE,148 });149 };150 151 const handleUpdateSuccess = (data?: { message?: string }) => {152 successfullyInstalled = true;153 setUpdateInfo(null);154 addItemOrDefer({155 type: MessageType.INFO,156 text: data?.message ?? UPDATE_SUCCESS_MESSAGE,157 });158 };159 160 const handleUpdateInfo = (data: { message: string }) => {161 addItemOrDefer({162 type: MessageType.INFO,163 text: data.message,164 });165 };166 167 updateEventEmitter.on('update-received', handleUpdateRecieved);168 updateEventEmitter.on('update-failed', handleUpdateFailed);169 updateEventEmitter.on('update-success', handleUpdateSuccess);170 updateEventEmitter.on('update-info', handleUpdateInfo);171 172 const cleanup = () => {173 updateEventEmitter.off('update-received', handleUpdateRecieved);174 updateEventEmitter.off('update-failed', handleUpdateFailed);175 updateEventEmitter.off('update-success', handleUpdateSuccess);176 updateEventEmitter.off('update-info', handleUpdateInfo);177 pendingNotifications.length = 0;178 };179 180 const flush = () => {181 while (pendingNotifications.length > 0) {182 const item = pendingNotifications.shift()!;183 addItem(item, Date.now());184 }185 };186 187 return { cleanup, flush };188}189 