mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-13 19:40:28 -07:00
fix(cli): pass node arguments via NODE_OPTIONS during relaunch to support SEA (#26130)
This commit is contained in:
@@ -29,3 +29,101 @@ export async function relaunchApp(): Promise<void> {
|
||||
await runExitCleanup();
|
||||
process.exit(RELAUNCH_EXIT_CODE);
|
||||
}
|
||||
|
||||
export interface ProcessWithSea extends NodeJS.Process {
|
||||
isSea?: () => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current process is a "standard" SEA (Single Executable Application)
|
||||
* where the user arguments start at index 1 instead of index 2.
|
||||
* A relaunched SEA child will have process.argv[0] === process.argv[1] (because we inject execPath),
|
||||
* so it will return false here and correctly slice from index 2.
|
||||
*/
|
||||
export function isStandardSea(): boolean {
|
||||
return (
|
||||
process.argv[0] !== process.argv[1] &&
|
||||
(process.env['IS_BINARY'] === 'true' ||
|
||||
(process as ProcessWithSea).isSea?.() === true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the user-provided script arguments from process.argv,
|
||||
* accounting for the differences in SEA execution modes.
|
||||
*/
|
||||
export function getScriptArgs(): string[] {
|
||||
return process.argv.slice(isStandardSea() ? 1 : 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current process is running in any SEA environment
|
||||
* (either the initial launch or a relaunched child).
|
||||
*/
|
||||
export function isSeaEnvironment(): boolean {
|
||||
return (
|
||||
process.env['IS_BINARY'] === 'true' ||
|
||||
(process as ProcessWithSea).isSea?.() === true ||
|
||||
process.argv[0] === process.argv[1]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the arguments and environment for spawning a child process during relaunch.
|
||||
* Handles differences between standard Node and SEA binary modes.
|
||||
*/
|
||||
export function getSpawnConfig(
|
||||
nodeArgs: string[],
|
||||
scriptArgs: string[],
|
||||
): {
|
||||
spawnArgs: string[];
|
||||
env: NodeJS.ProcessEnv;
|
||||
} {
|
||||
const isBinary = isSeaEnvironment();
|
||||
const newEnv: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
GEMINI_CLI_NO_RELAUNCH: 'true',
|
||||
};
|
||||
|
||||
const finalSpawnArgs: string[] = [];
|
||||
|
||||
if (isBinary) {
|
||||
// In SEA mode, Node flags must be passed via NODE_OPTIONS, as the binary
|
||||
// passes all CLI arguments directly to the application.
|
||||
// We only need to append the *new* nodeArgs (e.g., memory flags).
|
||||
// Existing execArgv are inherited via the environment or baked into the binary.
|
||||
if (nodeArgs.length > 0) {
|
||||
for (const arg of nodeArgs) {
|
||||
if (/[\s"'\\]/.test(arg)) {
|
||||
throw new Error(
|
||||
`Unsupported node argument for SEA relaunch: ${arg}. Complex escaping is not supported.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const existingNodeOptions = process.env['NODE_OPTIONS'] || '';
|
||||
// nodeArgs in our codebase are simple flags like --max-old-space-size=X
|
||||
// that do not contain spaces and do not require complex escaping.
|
||||
newEnv['NODE_OPTIONS'] =
|
||||
`${existingNodeOptions} ${nodeArgs.join(' ')}`.trim();
|
||||
}
|
||||
// Binary is its own entry point. To maintain the [node, script, ...args]
|
||||
// structure expected by the application (which uses slice(2)),
|
||||
// we must provide a placeholder for the script path.
|
||||
// We explicitly use process.execPath to break the cycle and prevent
|
||||
// compounding argument duplication on subsequent relaunches.
|
||||
finalSpawnArgs.push(process.execPath, ...scriptArgs);
|
||||
} else {
|
||||
// Standard Node mode: pass all flags via command line.
|
||||
finalSpawnArgs.push(
|
||||
...process.execArgv,
|
||||
...nodeArgs,
|
||||
process.argv[1],
|
||||
...scriptArgs,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
spawnArgs: finalSpawnArgs,
|
||||
env: newEnv,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user