feat: consolidate sandbox configurations into a single object (#1154)

This commit is contained in:
Brandon Keiji
2025-06-18 10:01:00 -07:00
committed by GitHub
parent 30d1662128
commit 332512853e
9 changed files with 154 additions and 93 deletions

View File

@@ -28,6 +28,7 @@ import * as dotenv from 'dotenv';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import { loadSandboxConfig } from './sandboxConfig.js';
// Simple console logger for now - replace with actual logger if available
const logger = {
@@ -42,6 +43,7 @@ const logger = {
interface CliArgs {
model: string | undefined;
sandbox: boolean | string | undefined;
'sandbox-image': string | undefined;
debug: boolean | undefined;
prompt: string | undefined;
all_files: boolean | undefined;
@@ -72,6 +74,10 @@ async function parseArguments(): Promise<CliArgs> {
type: 'boolean',
description: 'Run in sandbox?',
})
.option('sandbox-image', {
type: 'string',
description: 'Sandbox image URI.',
})
.option('debug', {
alias: 'd',
type: 'boolean',
@@ -192,11 +198,13 @@ export async function loadCliConfig(
const mcpServers = mergeMcpServers(settings, extensions);
const sandboxConfig = await loadSandboxConfig(settings, argv);
return new Config({
sessionId,
contentGeneratorConfig,
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
sandbox: argv.sandbox ?? settings.sandbox,
sandbox: sandboxConfig,
targetDir: process.cwd(),
debugMode,
question: argv.prompt || '',

View File

@@ -0,0 +1,102 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { SandboxConfig } from '@gemini-cli/core';
import commandExists from 'command-exists';
import * as os from 'node:os';
import { getPackageJson } from '../utils/package.js';
import { Settings } from './settings.js';
// This is a stripped-down version of the CliArgs interface from config.ts
// to avoid circular dependencies.
interface SandboxCliArgs {
sandbox?: boolean | string;
'sandbox-image'?: string;
}
const VALID_SANDBOX_COMMANDS: ReadonlyArray<SandboxConfig['command']> = [
'docker',
'podman',
'sandbox-exec',
];
function isSandboxCommand(value: string): value is SandboxConfig['command'] {
return (VALID_SANDBOX_COMMANDS as readonly string[]).includes(value);
}
function getSandboxCommand(
sandbox?: boolean | string,
): SandboxConfig['command'] | '' {
// note environment variable takes precedence over argument (from command line or settings)
sandbox = process.env.GEMINI_SANDBOX?.toLowerCase().trim() ?? sandbox;
if (sandbox === '1' || sandbox === 'true') sandbox = true;
else if (sandbox === '0' || sandbox === 'false') sandbox = false;
if (sandbox === false) {
return '';
}
if (typeof sandbox === 'string' && sandbox !== '') {
if (!isSandboxCommand(sandbox)) {
console.error(
`ERROR: invalid sandbox command '${sandbox}'. Must be one of ${VALID_SANDBOX_COMMANDS.join(
', ',
)}`,
);
process.exit(1);
}
// confirm that specfied command exists
if (commandExists.sync(sandbox)) {
return sandbox;
}
console.error(
`ERROR: missing sandbox command '${sandbox}' (from GEMINI_SANDBOX)`,
);
process.exit(1);
}
// look for seatbelt, docker, or podman, in that order
// for container-based sandboxing, require sandbox to be enabled explicitly
if (os.platform() === 'darwin' && commandExists.sync('sandbox-exec')) {
return 'sandbox-exec';
} else if (commandExists.sync('docker') && sandbox === true) {
return 'docker';
} else if (commandExists.sync('podman') && sandbox === true) {
return 'podman';
}
// throw an error if user requested sandbox but no command was found
if (sandbox === true) {
console.error(
'ERROR: GEMINI_SANDBOX is true but failed to determine command for sandbox; ' +
'install docker or podman or specify command in GEMINI_SANDBOX',
);
process.exit(1);
}
return '';
}
export async function loadSandboxConfig(
settings: Settings,
argv: SandboxCliArgs,
): Promise<SandboxConfig | undefined> {
const sandboxOption = argv.sandbox ?? settings.sandbox;
const sandboxCommand = getSandboxCommand(sandboxOption);
if (!sandboxCommand) {
return undefined;
}
const packageJson = await getPackageJson();
return {
command: sandboxCommand,
image:
argv['sandbox-image'] ??
process.env.GEMINI_SANDBOX_IMAGE ??
packageJson?.config?.sandboxImageUri ??
'gemini-cli-sandbox',
};
}