diff options
| author | Brandon Keiji <[email protected]> | 2025-06-18 10:01:00 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-06-18 17:01:00 +0000 |
| commit | 332512853e40e6c9b826b60057a389e9d34453fd (patch) | |
| tree | 7ee45ec5d7f27cb5664c353379580133b223da77 /packages/cli/src/config/sandboxConfig.ts | |
| parent | 30d1662128e688bf94653a0144ef96c311fae40b (diff) | |
feat: consolidate sandbox configurations into a single object (#1154)
Diffstat (limited to 'packages/cli/src/config/sandboxConfig.ts')
| -rw-r--r-- | packages/cli/src/config/sandboxConfig.ts | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/packages/cli/src/config/sandboxConfig.ts b/packages/cli/src/config/sandboxConfig.ts new file mode 100644 index 00000000..69a54900 --- /dev/null +++ b/packages/cli/src/config/sandboxConfig.ts @@ -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', + }; +} |
