/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { spawn } from 'node:child_process'; import { RELAUNCH_EXIT_CODE, getSpawnConfig, getScriptArgs, } from './processUtils.js'; import { writeToStderr, type AdminControlsSettings, } from '@google/gemini-cli-core'; export async function relaunchOnExitCode(runner: () => Promise) { while (true) { try { const exitCode = await runner(); if (exitCode !== RELAUNCH_EXIT_CODE) { process.exit(exitCode); } } catch (error) { process.stdin.resume(); const errorMessage = error instanceof Error ? (error.stack ?? error.message) : String(error); writeToStderr( `Fatal error: Failed to relaunch the CLI process.\n${errorMessage}\n`, ); process.exit(1); } } } export async function relaunchAppInChildProcess( additionalNodeArgs: string[], additionalScriptArgs: string[], remoteAdminSettings?: AdminControlsSettings, ) { if (process.env['GEMINI_CLI_NO_RELAUNCH']) { return; } let latestAdminSettings = remoteAdminSettings; const runner = () => { const scriptArgs = getScriptArgs(); const { spawnArgs, env: newEnv } = getSpawnConfig(additionalNodeArgs, [ ...additionalScriptArgs, ...scriptArgs, ]); // The parent process should not be reading from stdin while the child is running. process.stdin.pause(); const child = spawn(process.execPath, spawnArgs, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env: newEnv, }); if (latestAdminSettings) { child.send({ type: 'admin-settings', settings: latestAdminSettings }); } child.on('message', (msg: { type?: string; settings?: unknown }) => { if (msg.type === 'admin-settings-update' && msg.settings) { latestAdminSettings = msg.settings as AdminControlsSettings; } }); return new Promise((resolve, reject) => { child.on('error', reject); child.on('close', (code) => { // Resume stdin before the parent process exits. process.stdin.resume(); resolve(code ?? 1); }); }); }; await relaunchOnExitCode(runner); }