diff --git a/integration-tests/file-system.test.ts b/integration-tests/file-system.test.ts index 5a7028e0..6b150be5 100644 --- a/integration-tests/file-system.test.ts +++ b/integration-tests/file-system.test.ts @@ -86,7 +86,7 @@ describe('file-system', () => { ).toBeTruthy(); // Log success info if verbose - if (process.env.VERBOSE === 'true') { + if (process.env['VERBOSE'] === 'true') { console.log('File written successfully with hello message.'); } }); diff --git a/integration-tests/mcp_server_cyclic_schema.test.ts b/integration-tests/mcp_server_cyclic_schema.test.ts index 18c1bcde..7136287c 100644 --- a/integration-tests/mcp_server_cyclic_schema.test.ts +++ b/integration-tests/mcp_server_cyclic_schema.test.ts @@ -27,7 +27,7 @@ const readline = require('readline'); const fs = require('fs'); // Debug logging to stderr (only when MCP_DEBUG or VERBOSE is set) -const debugEnabled = process.env.MCP_DEBUG === 'true' || process.env.VERBOSE === 'true'; +const debugEnabled = process.env['MCP_DEBUG'] === 'true' || process.env['VERBOSE'] === 'true'; function debug(msg) { if (debugEnabled) { fs.writeSync(2, \`[MCP-DEBUG] \${msg}\\n\`); diff --git a/integration-tests/replace.test.ts b/integration-tests/replace.test.ts index 3a2d979b..a04025e7 100644 --- a/integration-tests/replace.test.ts +++ b/integration-tests/replace.test.ts @@ -56,7 +56,8 @@ describe('replace', () => { expect(newFileContent).toBe(expectedContent); // Log success info if verbose - if (process.env.VERBOSE === 'true') { + vi.stubEnv('VERBOSE', 'true'); + if (process.env['VERBOSE'] === 'true') { console.log('File replaced successfully. New content:', newFileContent); } }); diff --git a/integration-tests/simple-mcp-server.test.ts b/integration-tests/simple-mcp-server.test.ts index 98c81f16..b57502cf 100644 --- a/integration-tests/simple-mcp-server.test.ts +++ b/integration-tests/simple-mcp-server.test.ts @@ -28,7 +28,7 @@ const readline = require('readline'); const fs = require('fs'); // Debug logging to stderr (only when MCP_DEBUG or VERBOSE is set) -const debugEnabled = process.env.MCP_DEBUG === 'true' || process.env.VERBOSE === 'true'; +const debugEnabled = process.env['MCP_DEBUG'] === 'true' || process.env['VERBOSE'] === 'true'; function debug(msg) { if (debugEnabled) { fs.writeSync(2, \`[MCP-DEBUG] \${msg}\\n\`); diff --git a/integration-tests/write_file.test.ts b/integration-tests/write_file.test.ts index 3fe26af6..49cdda38 100644 --- a/integration-tests/write_file.test.ts +++ b/integration-tests/write_file.test.ts @@ -58,7 +58,8 @@ describe('write_file', () => { expect(newFileContent).not.toBe(''); // Log success info if verbose - if (process.env.VERBOSE === 'true') { + vi.stubEnv('VERBOSE', 'true'); + if (process.env['VERBOSE'] === 'true') { console.log( 'File created successfully with content:', newFileContent.substring(0, 100) + '...', diff --git a/packages/cli/src/commands/mcp/add.ts b/packages/cli/src/commands/mcp/add.ts index 67c44572..304338b0 100644 --- a/packages/cli/src/commands/mcp/add.ts +++ b/packages/cli/src/commands/mcp/add.ts @@ -197,25 +197,25 @@ export const addCommand: CommandModule = { .middleware((argv) => { // Handle -- separator args as server args if present if (argv['--']) { - const existingArgs = (argv.args as Array) || []; - argv.args = [...existingArgs, ...(argv['--'] as string[])]; + const existingArgs = (argv['args'] as Array) || []; + argv['args'] = [...existingArgs, ...(argv['--'] as string[])]; } }), handler: async (argv) => { await addMcpServer( - argv.name as string, - argv.commandOrUrl as string, - argv.args as Array, + argv['name'] as string, + argv['commandOrUrl'] as string, + argv['args'] as Array, { - scope: argv.scope as string, - transport: argv.transport as string, - env: argv.env as string[], - header: argv.header as string[], - timeout: argv.timeout as number | undefined, - trust: argv.trust as boolean | undefined, - description: argv.description as string | undefined, - includeTools: argv.includeTools as string[] | undefined, - excludeTools: argv.excludeTools as string[] | undefined, + scope: argv['scope'] as string, + transport: argv['transport'] as string, + env: argv['env'] as string[], + header: argv['header'] as string[], + timeout: argv['timeout'] as number | undefined, + trust: argv['trust'] as boolean | undefined, + description: argv['description'] as string | undefined, + includeTools: argv['includeTools'] as string[] | undefined, + excludeTools: argv['excludeTools'] as string[] | undefined, }, ); }, diff --git a/packages/cli/src/commands/mcp/remove.ts b/packages/cli/src/commands/mcp/remove.ts index 80d66234..e05478e3 100644 --- a/packages/cli/src/commands/mcp/remove.ts +++ b/packages/cli/src/commands/mcp/remove.ts @@ -53,8 +53,8 @@ export const removeCommand: CommandModule = { choices: ['user', 'project'], }), handler: async (argv) => { - await removeMcpServer(argv.name as string, { - scope: argv.scope as string, + await removeMcpServer(argv['name'] as string, { + scope: argv['scope'] as string, }); }, }; diff --git a/packages/cli/src/config/auth.test.ts b/packages/cli/src/config/auth.test.ts index 96defb1e..ddfed236 100644 --- a/packages/cli/src/config/auth.test.ts +++ b/packages/cli/src/config/auth.test.ts @@ -34,7 +34,7 @@ describe('validateAuthMethod', () => { describe('USE_GEMINI', () => { it('should return null if GEMINI_API_KEY is set', () => { - process.env.GEMINI_API_KEY = 'test-key'; + process.env['GEMINI_API_KEY'] = 'test-key'; expect(validateAuthMethod(AuthType.USE_GEMINI)).toBeNull(); }); @@ -47,13 +47,13 @@ describe('validateAuthMethod', () => { describe('USE_VERTEX_AI', () => { it('should return null if GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION are set', () => { - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; - process.env.GOOGLE_CLOUD_LOCATION = 'test-location'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'test-project'; + process.env['GOOGLE_CLOUD_LOCATION'] = 'test-location'; expect(validateAuthMethod(AuthType.USE_VERTEX_AI)).toBeNull(); }); it('should return null if GOOGLE_API_KEY is set', () => { - process.env.GOOGLE_API_KEY = 'test-api-key'; + process.env['GOOGLE_API_KEY'] = 'test-api-key'; expect(validateAuthMethod(AuthType.USE_VERTEX_AI)).toBeNull(); }); diff --git a/packages/cli/src/config/auth.ts b/packages/cli/src/config/auth.ts index 91d4eee3..4676bb2f 100644 --- a/packages/cli/src/config/auth.ts +++ b/packages/cli/src/config/auth.ts @@ -17,7 +17,7 @@ export const validateAuthMethod = (authMethod: string): string | null => { } if (authMethod === AuthType.USE_GEMINI) { - if (!process.env.GEMINI_API_KEY) { + if (!process.env['GEMINI_API_KEY']) { return 'GEMINI_API_KEY environment variable not found. Add that to your environment and try again (no reload needed if using .env)!'; } return null; @@ -25,8 +25,9 @@ export const validateAuthMethod = (authMethod: string): string | null => { if (authMethod === AuthType.USE_VERTEX_AI) { const hasVertexProjectLocationConfig = - !!process.env.GOOGLE_CLOUD_PROJECT && !!process.env.GOOGLE_CLOUD_LOCATION; - const hasGoogleApiKey = !!process.env.GOOGLE_API_KEY; + !!process.env['GOOGLE_CLOUD_PROJECT'] && + !!process.env['GOOGLE_CLOUD_LOCATION']; + const hasGoogleApiKey = !!process.env['GOOGLE_API_KEY']; if (!hasVertexProjectLocationConfig && !hasGoogleApiKey) { return ( 'When using Vertex AI, you must specify either:\n' + diff --git a/packages/cli/src/config/config.integration.test.ts b/packages/cli/src/config/config.integration.test.ts index 45ed6d82..6d77740d 100644 --- a/packages/cli/src/config/config.integration.test.ts +++ b/packages/cli/src/config/config.integration.test.ts @@ -53,19 +53,17 @@ vi.mock('@google/gemini-cli-core', async () => { describe('Configuration Integration Tests', () => { let tempDir: string; - let originalEnv: NodeJS.ProcessEnv; beforeEach(() => { server.resetHandlers(http.post(CLEARCUT_URL, () => HttpResponse.text())); tempDir = fs.mkdtempSync(path.join(tmpdir(), 'gemini-cli-test-')); - originalEnv = { ...process.env }; - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); vi.clearAllMocks(); }); afterEach(() => { - process.env = originalEnv; + vi.unstubAllEnvs(); if (fs.existsSync(tempDir)) { fs.rmSync(tempDir, { recursive: true }); } diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts index e70fc3b3..727f6fe1 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -252,17 +252,16 @@ describe('parseArguments', () => { describe('loadCliConfig', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; // Ensure API key is set for tests + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -339,7 +338,7 @@ describe('loadCliConfig', () => { ]; testCases.forEach(({ input, expected }) => { it(`should set proxy to ${expected} according to environment variable [${input.env_name}]`, async () => { - process.env[input.env_name] = input.proxy_url; + vi.stubEnv(input.env_name, input.proxy_url); process.argv = ['node', 'script.js']; const argv = await parseArguments(); const settings: Settings = {}; @@ -357,7 +356,7 @@ describe('loadCliConfig', () => { }); it('should prioritize CLI flag over environment variable for proxy (CLI http://localhost:7890, environment variable http://localhost:7891)', async () => { - process.env['http_proxy'] = 'http://localhost:7891'; + vi.stubEnv('http_proxy', 'http://localhost:7891'); process.argv = ['node', 'script.js', '--proxy', 'http://localhost:7890']; const argv = await parseArguments(); const settings: Settings = {}; @@ -368,17 +367,16 @@ describe('loadCliConfig', () => { describe('loadCliConfig telemetry', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1113,17 +1111,16 @@ describe('Approval mode tool exclusion logic', () => { describe('loadCliConfig with allowed-mcp-server-names', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1363,17 +1360,16 @@ describe('loadCliConfig model selection', () => { describe('loadCliConfig folderTrustFeature', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1396,17 +1392,16 @@ describe('loadCliConfig folderTrustFeature', () => { describe('loadCliConfig folderTrust', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1477,12 +1472,11 @@ vi.mock('fs', async () => { describe('loadCliConfig with includeDirectories', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); vi.spyOn(process, 'cwd').mockReturnValue( path.resolve(path.sep, 'home', 'user', 'project'), ); @@ -1490,7 +1484,7 @@ describe('loadCliConfig with includeDirectories', () => { afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1530,17 +1524,16 @@ describe('loadCliConfig with includeDirectories', () => { describe('loadCliConfig chatCompression', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1569,20 +1562,19 @@ describe('loadCliConfig chatCompression', () => { describe('loadCliConfig tool exclusions', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; const originalIsTTY = process.stdin.isTTY; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); process.stdin.isTTY = true; }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; process.stdin.isTTY = originalIsTTY; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1629,20 +1621,19 @@ describe('loadCliConfig tool exclusions', () => { describe('loadCliConfig interactive', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; const originalIsTTY = process.stdin.isTTY; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); process.stdin.isTTY = true; }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; process.stdin.isTTY = originalIsTTY; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1681,18 +1672,17 @@ describe('loadCliConfig interactive', () => { describe('loadCliConfig approval mode', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); process.argv = ['node', 'script.js']; // Reset argv for each test }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); @@ -1759,18 +1749,17 @@ describe('loadCliConfig approval mode', () => { describe('loadCliConfig trustedFolder', () => { const originalArgv = process.argv; - const originalEnv = { ...process.env }; beforeEach(() => { vi.resetAllMocks(); vi.mocked(os.homedir).mockReturnValue('/mock/home/user'); - process.env.GEMINI_API_KEY = 'test-api-key'; + vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); process.argv = ['node', 'script.js']; // Reset argv for each test }); afterEach(() => { process.argv = originalArgv; - process.env = originalEnv; + vi.unstubAllEnvs(); vi.restoreAllMocks(); }); diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index a943f641..b66afcc8 100644 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -87,7 +87,7 @@ export async function parseArguments(): Promise { alias: 'm', type: 'string', description: `Model`, - default: process.env.GEMINI_MODEL, + default: process.env['GEMINI_MODEL'], }) .option('prompt', { alias: 'p', @@ -230,12 +230,12 @@ export async function parseArguments(): Promise { dirs.flatMap((dir) => dir.split(',').map((d) => d.trim())), }) .check((argv) => { - if (argv.prompt && argv.promptInteractive) { + if (argv.prompt && argv['promptInteractive']) { throw new Error( 'Cannot use both --prompt (-p) and --prompt-interactive (-i) together', ); } - if (argv.yolo && argv.approvalMode) { + if (argv.yolo && argv['approvalMode']) { throw new Error( 'Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.', ); @@ -317,7 +317,7 @@ export async function loadCliConfig( ): Promise { const debugMode = argv.debug || - [process.env.DEBUG, process.env.DEBUG_MODE].some( + [process.env['DEBUG'], process.env['DEBUG_MODE']].some( (v) => v === 'true' || v === '1', ) || false; @@ -496,7 +496,7 @@ export async function loadCliConfig( settings.telemetry?.target) as TelemetryTarget, otlpEndpoint: argv.telemetryOtlpEndpoint ?? - process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? + process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] ?? settings.telemetry?.otlpEndpoint, otlpProtocol: (['grpc', 'http'] as const).find( (p) => @@ -517,10 +517,10 @@ export async function loadCliConfig( checkpointing: argv.checkpointing || settings.checkpointing?.enabled, proxy: argv.proxy || - process.env.HTTPS_PROXY || - process.env.https_proxy || - process.env.HTTP_PROXY || - process.env.http_proxy, + process.env['HTTPS_PROXY'] || + process.env['https_proxy'] || + process.env['HTTP_PROXY'] || + process.env['http_proxy'], cwd, fileDiscoveryService: fileService, bugCommand: settings.bugCommand, @@ -531,7 +531,7 @@ export async function loadCliConfig( listExtensions: argv.listExtensions || false, extensions: allExtensions, blockedMcpServers, - noBrowser: !!process.env.NO_BROWSER, + noBrowser: !!process.env['NO_BROWSER'], summarizeToolOutput: settings.summarizeToolOutput, ideMode, chatCompression: settings.chatCompression, diff --git a/packages/cli/src/config/sandboxConfig.ts b/packages/cli/src/config/sandboxConfig.ts index c4ae50c4..4311b148 100644 --- a/packages/cli/src/config/sandboxConfig.ts +++ b/packages/cli/src/config/sandboxConfig.ts @@ -31,13 +31,13 @@ function getSandboxCommand( sandbox?: boolean | string, ): SandboxConfig['command'] | '' { // If the SANDBOX env var is set, we're already inside the sandbox. - if (process.env.SANDBOX) { + if (process.env['SANDBOX']) { return ''; } // note environment variable takes precedence over argument (from command line or settings) const environmentConfiguredSandbox = - process.env.GEMINI_SANDBOX?.toLowerCase().trim() ?? ''; + process.env['GEMINI_SANDBOX']?.toLowerCase().trim() ?? ''; sandbox = environmentConfiguredSandbox?.length > 0 ? environmentConfiguredSandbox @@ -100,7 +100,7 @@ export async function loadSandboxConfig( const packageJson = await getPackageJson(); const image = argv.sandboxImage ?? - process.env.GEMINI_SANDBOX_IMAGE ?? + process.env['GEMINI_SANDBOX_IMAGE'] ?? packageJson?.config?.sandboxImageUri; return command && image ? { command, image } : undefined; diff --git a/packages/cli/src/config/settings.test.ts b/packages/cli/src/config/settings.test.ts index 353a5783..d537eeb1 100644 --- a/packages/cli/src/config/settings.test.ts +++ b/packages/cli/src/config/settings.test.ts @@ -892,7 +892,7 @@ describe('Settings Loading and Merging', () => { }); it('should resolve environment variables in user settings', () => { - process.env.TEST_API_KEY = 'user_api_key_from_env'; + process.env['TEST_API_KEY'] = 'user_api_key_from_env'; const userSettingsContent = { apiKey: '$TEST_API_KEY', someUrl: 'https://test.com/${TEST_API_KEY}', @@ -917,11 +917,11 @@ describe('Settings Loading and Merging', () => { ); // @ts-expect-error: dynamic property for test expect(settings.merged.apiKey).toBe('user_api_key_from_env'); - delete process.env.TEST_API_KEY; + delete process.env['TEST_API_KEY']; }); it('should resolve environment variables in workspace settings', () => { - process.env.WORKSPACE_ENDPOINT = 'workspace_endpoint_from_env'; + process.env['WORKSPACE_ENDPOINT'] = 'workspace_endpoint_from_env'; const workspaceSettingsContent = { endpoint: '${WORKSPACE_ENDPOINT}/api', nested: { value: '$WORKSPACE_ENDPOINT' }, @@ -946,7 +946,7 @@ describe('Settings Loading and Merging', () => { ); // @ts-expect-error: dynamic property for test expect(settings.merged.endpoint).toBe('workspace_endpoint_from_env/api'); - delete process.env.WORKSPACE_ENDPOINT; + delete process.env['WORKSPACE_ENDPOINT']; }); it('should prioritize user env variables over workspace env variables if keys clash after resolution', () => { @@ -954,18 +954,18 @@ describe('Settings Loading and Merging', () => { const workspaceSettingsContent = { configValue: '$SHARED_VAR' }; (mockFsExistsSync as Mock).mockReturnValue(true); - const originalSharedVar = process.env.SHARED_VAR; + const originalSharedVar = process.env['SHARED_VAR']; // Temporarily delete to ensure a clean slate for the test's specific manipulations - delete process.env.SHARED_VAR; + delete process.env['SHARED_VAR']; (fs.readFileSync as Mock).mockImplementation( (p: fs.PathOrFileDescriptor) => { if (p === USER_SETTINGS_PATH) { - process.env.SHARED_VAR = 'user_value_for_user_read'; // Set for user settings read + process.env['SHARED_VAR'] = 'user_value_for_user_read'; // Set for user settings read return JSON.stringify(userSettingsContent); } if (p === MOCK_WORKSPACE_SETTINGS_PATH) { - process.env.SHARED_VAR = 'workspace_value_for_workspace_read'; // Set for workspace settings read + process.env['SHARED_VAR'] = 'workspace_value_for_workspace_read'; // Set for workspace settings read return JSON.stringify(workspaceSettingsContent); } return '{}'; @@ -990,9 +990,9 @@ describe('Settings Loading and Merging', () => { // Restore original environment variable state if (originalSharedVar !== undefined) { - process.env.SHARED_VAR = originalSharedVar; + process.env['SHARED_VAR'] = originalSharedVar; } else { - delete process.env.SHARED_VAR; // Ensure it's deleted if it wasn't there before + delete process.env['SHARED_VAR']; // Ensure it's deleted if it wasn't there before } }); @@ -1001,18 +1001,18 @@ describe('Settings Loading and Merging', () => { const workspaceSettingsContent = { configValue: '$SHARED_VAR' }; (mockFsExistsSync as Mock).mockReturnValue(true); - const originalSharedVar = process.env.SHARED_VAR; + const originalSharedVar = process.env['SHARED_VAR']; // Temporarily delete to ensure a clean slate for the test's specific manipulations - delete process.env.SHARED_VAR; + delete process.env['SHARED_VAR']; (fs.readFileSync as Mock).mockImplementation( (p: fs.PathOrFileDescriptor) => { if (p === USER_SETTINGS_PATH) { - process.env.SHARED_VAR = 'user_value_for_user_read'; // Set for user settings read + process.env['SHARED_VAR'] = 'user_value_for_user_read'; // Set for user settings read return JSON.stringify(userSettingsContent); } if (p === MOCK_WORKSPACE_SETTINGS_PATH) { - process.env.SHARED_VAR = 'workspace_value_for_workspace_read'; // Set for workspace settings read + process.env['SHARED_VAR'] = 'workspace_value_for_workspace_read'; // Set for workspace settings read return JSON.stringify(workspaceSettingsContent); } return '{}'; @@ -1034,9 +1034,9 @@ describe('Settings Loading and Merging', () => { // Restore original environment variable state if (originalSharedVar !== undefined) { - process.env.SHARED_VAR = originalSharedVar; + process.env['SHARED_VAR'] = originalSharedVar; } else { - delete process.env.SHARED_VAR; // Ensure it's deleted if it wasn't there before + delete process.env['SHARED_VAR']; // Ensure it's deleted if it wasn't there before } }); @@ -1045,18 +1045,18 @@ describe('Settings Loading and Merging', () => { const systemSettingsContent = { configValue: '$SHARED_VAR' }; (mockFsExistsSync as Mock).mockReturnValue(true); - const originalSharedVar = process.env.SHARED_VAR; + const originalSharedVar = process.env['SHARED_VAR']; // Temporarily delete to ensure a clean slate for the test's specific manipulations - delete process.env.SHARED_VAR; + delete process.env['SHARED_VAR']; (fs.readFileSync as Mock).mockImplementation( (p: fs.PathOrFileDescriptor) => { if (p === getSystemSettingsPath()) { - process.env.SHARED_VAR = 'system_value_for_system_read'; // Set for system settings read + process.env['SHARED_VAR'] = 'system_value_for_system_read'; // Set for system settings read return JSON.stringify(systemSettingsContent); } if (p === MOCK_WORKSPACE_SETTINGS_PATH) { - process.env.SHARED_VAR = 'workspace_value_for_workspace_read'; // Set for workspace settings read + process.env['SHARED_VAR'] = 'workspace_value_for_workspace_read'; // Set for workspace settings read return JSON.stringify(workspaceSettingsContent); } return '{}'; @@ -1079,9 +1079,9 @@ describe('Settings Loading and Merging', () => { // Restore original environment variable state if (originalSharedVar !== undefined) { - process.env.SHARED_VAR = originalSharedVar; + process.env['SHARED_VAR'] = originalSharedVar; } else { - delete process.env.SHARED_VAR; // Ensure it's deleted if it wasn't there before + delete process.env['SHARED_VAR']; // Ensure it's deleted if it wasn't there before } }); @@ -1146,8 +1146,8 @@ describe('Settings Loading and Merging', () => { }); it('should resolve multiple environment variables in a single string', () => { - process.env.VAR_A = 'valueA'; - process.env.VAR_B = 'valueB'; + process.env['VAR_A'] = 'valueA'; + process.env['VAR_B'] = 'valueB'; const userSettingsContent = { path: '/path/$VAR_A/${VAR_B}/end' }; (mockFsExistsSync as Mock).mockImplementation( (p: fs.PathLike) => p === USER_SETTINGS_PATH, @@ -1161,13 +1161,13 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); expect(settings.user.settings.path).toBe('/path/valueA/valueB/end'); - delete process.env.VAR_A; - delete process.env.VAR_B; + delete process.env['VAR_A']; + delete process.env['VAR_B']; }); it('should resolve environment variables in arrays', () => { - process.env.ITEM_1 = 'item1_env'; - process.env.ITEM_2 = 'item2_env'; + process.env['ITEM_1'] = 'item1_env'; + process.env['ITEM_2'] = 'item2_env'; const userSettingsContent = { list: ['$ITEM_1', '${ITEM_2}', 'literal'] }; (mockFsExistsSync as Mock).mockImplementation( (p: fs.PathLike) => p === USER_SETTINGS_PATH, @@ -1185,13 +1185,13 @@ describe('Settings Loading and Merging', () => { 'item2_env', 'literal', ]); - delete process.env.ITEM_1; - delete process.env.ITEM_2; + delete process.env['ITEM_1']; + delete process.env['ITEM_2']; }); it('should correctly pass through null, boolean, and number types, and handle undefined properties', () => { - process.env.MY_ENV_STRING = 'env_string_value'; - process.env.MY_ENV_STRING_NESTED = 'env_string_nested_value'; + process.env['MY_ENV_STRING'] = 'env_string_value'; + process.env['MY_ENV_STRING_NESTED'] = 'env_string_nested_value'; const userSettingsContent = { nullVal: null, @@ -1236,13 +1236,13 @@ describe('Settings Loading and Merging', () => { 'env_string_nested_value', ); - delete process.env.MY_ENV_STRING; - delete process.env.MY_ENV_STRING_NESTED; + delete process.env['MY_ENV_STRING']; + delete process.env['MY_ENV_STRING_NESTED']; }); it('should resolve multiple concatenated environment variables in a single string value', () => { - process.env.TEST_HOST = 'myhost'; - process.env.TEST_PORT = '9090'; + process.env['TEST_HOST'] = 'myhost'; + process.env['TEST_PORT'] = '9090'; const userSettingsContent = { serverAddress: '${TEST_HOST}:${TEST_PORT}/api', }; @@ -1260,20 +1260,20 @@ describe('Settings Loading and Merging', () => { const settings = loadSettings(MOCK_WORKSPACE_DIR); expect(settings.user.settings.serverAddress).toBe('myhost:9090/api'); - delete process.env.TEST_HOST; - delete process.env.TEST_PORT; + delete process.env['TEST_HOST']; + delete process.env['TEST_PORT']; }); describe('when GEMINI_CLI_SYSTEM_SETTINGS_PATH is set', () => { const MOCK_ENV_SYSTEM_SETTINGS_PATH = '/mock/env/system/settings.json'; beforeEach(() => { - process.env.GEMINI_CLI_SYSTEM_SETTINGS_PATH = + process.env['GEMINI_CLI_SYSTEM_SETTINGS_PATH'] = MOCK_ENV_SYSTEM_SETTINGS_PATH; }); afterEach(() => { - delete process.env.GEMINI_CLI_SYSTEM_SETTINGS_PATH; + delete process.env['GEMINI_CLI_SYSTEM_SETTINGS_PATH']; }); it('should load system settings from the path specified in the environment variable', () => { diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts index 36fd50f1..524eb16c 100644 --- a/packages/cli/src/config/settings.ts +++ b/packages/cli/src/config/settings.ts @@ -25,8 +25,8 @@ export const USER_SETTINGS_PATH = path.join(USER_SETTINGS_DIR, 'settings.json'); export const DEFAULT_EXCLUDED_ENV_VARS = ['DEBUG', 'DEBUG_MODE']; export function getSystemSettingsPath(): string { - if (process.env.GEMINI_CLI_SYSTEM_SETTINGS_PATH) { - return process.env.GEMINI_CLI_SYSTEM_SETTINGS_PATH; + if (process.env['GEMINI_CLI_SYSTEM_SETTINGS_PATH']) { + return process.env['GEMINI_CLI_SYSTEM_SETTINGS_PATH']; } if (platform() === 'darwin') { return '/Library/Application Support/GeminiCli/settings.json'; @@ -236,16 +236,16 @@ export function setUpCloudShellEnvironment(envFilePath: string | null): void { if (envFilePath && fs.existsSync(envFilePath)) { const envFileContent = fs.readFileSync(envFilePath); const parsedEnv = dotenv.parse(envFileContent); - if (parsedEnv.GOOGLE_CLOUD_PROJECT) { + if (parsedEnv['GOOGLE_CLOUD_PROJECT']) { // .env file takes precedence in Cloud Shell - process.env.GOOGLE_CLOUD_PROJECT = parsedEnv.GOOGLE_CLOUD_PROJECT; + process.env['GOOGLE_CLOUD_PROJECT'] = parsedEnv['GOOGLE_CLOUD_PROJECT']; } else { // If not in .env, set to default and override global - process.env.GOOGLE_CLOUD_PROJECT = 'cloudshell-gca'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'cloudshell-gca'; } } else { // If no .env file, set to default and override global - process.env.GOOGLE_CLOUD_PROJECT = 'cloudshell-gca'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'cloudshell-gca'; } } @@ -253,7 +253,7 @@ export function loadEnvironment(settings?: Settings): void { const envFilePath = findEnvFile(process.cwd()); // Cloud Shell environment variable handling - if (process.env.CLOUD_SHELL === 'true') { + if (process.env['CLOUD_SHELL'] === 'true') { setUpCloudShellEnvironment(envFilePath); } diff --git a/packages/cli/src/gemini.test.tsx b/packages/cli/src/gemini.test.tsx index c8bb45ab..c4de8308 100644 --- a/packages/cli/src/gemini.test.tsx +++ b/packages/cli/src/gemini.test.tsx @@ -93,10 +93,10 @@ describe('gemini.tsx main function', () => { loadSettingsMock = vi.mocked(loadSettings); // Store and clear sandbox-related env variables to ensure a consistent test environment - originalEnvGeminiSandbox = process.env.GEMINI_SANDBOX; - originalEnvSandbox = process.env.SANDBOX; - delete process.env.GEMINI_SANDBOX; - delete process.env.SANDBOX; + originalEnvGeminiSandbox = process.env['GEMINI_SANDBOX']; + originalEnvSandbox = process.env['SANDBOX']; + delete process.env['GEMINI_SANDBOX']; + delete process.env['SANDBOX']; consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); initialUnhandledRejectionListeners = @@ -106,14 +106,14 @@ describe('gemini.tsx main function', () => { afterEach(() => { // Restore original env variables if (originalEnvGeminiSandbox !== undefined) { - process.env.GEMINI_SANDBOX = originalEnvGeminiSandbox; + process.env['GEMINI_SANDBOX'] = originalEnvGeminiSandbox; } else { - delete process.env.GEMINI_SANDBOX; + delete process.env['GEMINI_SANDBOX']; } if (originalEnvSandbox !== undefined) { - process.env.SANDBOX = originalEnvSandbox; + process.env['SANDBOX'] = originalEnvSandbox; } else { - delete process.env.SANDBOX; + delete process.env['SANDBOX']; } const currentListeners = process.listeners('unhandledRejection'); diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx index 64bb5c84..f7089e28 100644 --- a/packages/cli/src/gemini.tsx +++ b/packages/cli/src/gemini.tsx @@ -80,7 +80,7 @@ function getNodeMemoryArgs(config: Config): string[] { ); } - if (process.env.GEMINI_CLI_NO_RELAUNCH) { + if (process.env['GEMINI_CLI_NO_RELAUNCH']) { return []; } @@ -141,7 +141,7 @@ export async function main() { if (settings.errors.length > 0) { for (const error of settings.errors) { let errorMessage = `Error in ${error.path}: ${error.message}`; - if (!process.env.NO_COLOR) { + if (!process.env['NO_COLOR']) { errorMessage = `\x1b[31m${errorMessage}\x1b[0m`; } console.error(errorMessage); @@ -187,7 +187,7 @@ export async function main() { // Set a default auth type if one isn't set. if (!settings.merged.selectedAuthType) { - if (process.env.CLOUD_SHELL === 'true') { + if (process.env['CLOUD_SHELL'] === 'true') { settings.setValue( SettingScope.User, 'selectedAuthType', @@ -217,7 +217,7 @@ export async function main() { } // hop into sandbox if we are outside and sandboxing is enabled - if (!process.env.SANDBOX) { + if (!process.env['SANDBOX']) { const memoryArgs = settings.merged.autoConfigureMaxOldSpaceSize ? getNodeMemoryArgs(config) : []; @@ -338,7 +338,9 @@ export async function main() { function setWindowTitle(title: string, settings: LoadedSettings) { if (!settings.merged.hideWindowTitle) { - const windowTitle = (process.env.CLI_TITLE || `Gemini - ${title}`).replace( + const windowTitle = ( + process.env['CLI_TITLE'] || `Gemini - ${title}` + ).replace( // eslint-disable-next-line no-control-regex /[\x00-\x1F\x7F]/g, '', diff --git a/packages/cli/src/services/McpPromptLoader.ts b/packages/cli/src/services/McpPromptLoader.ts index e912fb3e..c2862911 100644 --- a/packages/cli/src/services/McpPromptLoader.ts +++ b/packages/cli/src/services/McpPromptLoader.ts @@ -115,15 +115,15 @@ export class McpPromptLoader implements ICommandLoader { } const result = await prompt.invoke(promptInputs); - if (result.error) { + if (result['error']) { return { type: 'message', messageType: 'error', - content: `Error invoking prompt: ${result.error}`, + content: `Error invoking prompt: ${result['error']}`, }; } - if (!result.messages?.[0]?.content?.text) { + if (!result.messages?.[0]?.content?.['text']) { return { type: 'message', messageType: 'error', diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 9773971b..e0dbcba2 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -1089,7 +1089,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { alignItems={isNarrow ? 'flex-start' : 'center'} > - {process.env.GEMINI_SYSTEM_MD && ( + {process.env['GEMINI_SYSTEM_MD'] && ( |⌐■_■| )} {ctrlCPressedOnce ? ( diff --git a/packages/cli/src/ui/IdeIntegrationNudge.tsx b/packages/cli/src/ui/IdeIntegrationNudge.tsx index 906947f2..861728fa 100644 --- a/packages/cli/src/ui/IdeIntegrationNudge.tsx +++ b/packages/cli/src/ui/IdeIntegrationNudge.tsx @@ -41,8 +41,8 @@ export function IdeIntegrationNudge({ const { displayName: ideName } = getIdeInfo(ide); // Assume extension is already installed if the env variables are set. const isExtensionPreInstalled = - !!process.env.GEMINI_CLI_IDE_SERVER_PORT && - !!process.env.GEMINI_CLI_IDE_WORKSPACE_PATH; + !!process.env['GEMINI_CLI_IDE_SERVER_PORT'] && + !!process.env['GEMINI_CLI_IDE_WORKSPACE_PATH']; const OPTIONS: Array> = [ { diff --git a/packages/cli/src/ui/commands/aboutCommand.test.ts b/packages/cli/src/ui/commands/aboutCommand.test.ts index 27e96755..34f11e80 100644 --- a/packages/cli/src/ui/commands/aboutCommand.test.ts +++ b/packages/cli/src/ui/commands/aboutCommand.test.ts @@ -44,7 +44,7 @@ describe('aboutCommand', () => { vi.spyOn(mockContext.services.config!, 'getModel').mockReturnValue( 'test-model', ); - process.env.GOOGLE_CLOUD_PROJECT = 'test-gcp-project'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'test-gcp-project'; Object.defineProperty(process, 'platform', { value: 'test-os', }); @@ -68,7 +68,7 @@ describe('aboutCommand', () => { }); it('should call addItem with all version info', async () => { - process.env.SANDBOX = ''; + process.env['SANDBOX'] = ''; if (!aboutCommand.action) { throw new Error('The about command must have an action.'); } @@ -91,7 +91,7 @@ describe('aboutCommand', () => { }); it('should show the correct sandbox environment variable', async () => { - process.env.SANDBOX = 'gemini-sandbox'; + process.env['SANDBOX'] = 'gemini-sandbox'; if (!aboutCommand.action) { throw new Error('The about command must have an action.'); } @@ -107,8 +107,8 @@ describe('aboutCommand', () => { }); it('should show sandbox-exec profile when applicable', async () => { - process.env.SANDBOX = 'sandbox-exec'; - process.env.SEATBELT_PROFILE = 'test-profile'; + process.env['SANDBOX'] = 'sandbox-exec'; + process.env['SEATBELT_PROFILE'] = 'test-profile'; if (!aboutCommand.action) { throw new Error('The about command must have an action.'); } diff --git a/packages/cli/src/ui/commands/aboutCommand.ts b/packages/cli/src/ui/commands/aboutCommand.ts index 47b1b495..2512fead 100644 --- a/packages/cli/src/ui/commands/aboutCommand.ts +++ b/packages/cli/src/ui/commands/aboutCommand.ts @@ -16,18 +16,18 @@ export const aboutCommand: SlashCommand = { action: async (context) => { const osVersion = process.platform; let sandboxEnv = 'no sandbox'; - if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') { - sandboxEnv = process.env.SANDBOX; - } else if (process.env.SANDBOX === 'sandbox-exec') { + if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') { + sandboxEnv = process.env['SANDBOX']; + } else if (process.env['SANDBOX'] === 'sandbox-exec') { sandboxEnv = `sandbox-exec (${ - process.env.SEATBELT_PROFILE || 'unknown' + process.env['SEATBELT_PROFILE'] || 'unknown' })`; } const modelVersion = context.services.config?.getModel() || 'Unknown'; const cliVersion = await getCliVersion(); const selectedAuthType = context.services.settings.merged.selectedAuthType || ''; - const gcpProject = process.env.GOOGLE_CLOUD_PROJECT || ''; + const gcpProject = process.env['GOOGLE_CLOUD_PROJECT'] || ''; const ideClient = context.services.config?.getIdeClient()?.getDetectedIdeDisplayName() || ''; diff --git a/packages/cli/src/ui/commands/bugCommand.ts b/packages/cli/src/ui/commands/bugCommand.ts index 528b2b4c..8529e12b 100644 --- a/packages/cli/src/ui/commands/bugCommand.ts +++ b/packages/cli/src/ui/commands/bugCommand.ts @@ -27,11 +27,11 @@ export const bugCommand: SlashCommand = { const osVersion = `${process.platform} ${process.version}`; let sandboxEnv = 'no sandbox'; - if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') { - sandboxEnv = process.env.SANDBOX.replace(/^gemini-(?:code-)?/, ''); - } else if (process.env.SANDBOX === 'sandbox-exec') { + if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') { + sandboxEnv = process.env['SANDBOX'].replace(/^gemini-(?:code-)?/, ''); + } else if (process.env['SANDBOX'] === 'sandbox-exec') { sandboxEnv = `sandbox-exec (${ - process.env.SEATBELT_PROFILE || 'unknown' + process.env['SEATBELT_PROFILE'] || 'unknown' })`; } const modelVersion = config?.getModel() || 'Unknown'; diff --git a/packages/cli/src/ui/commands/docsCommand.test.ts b/packages/cli/src/ui/commands/docsCommand.test.ts index 73b7396a..58e2e173 100644 --- a/packages/cli/src/ui/commands/docsCommand.test.ts +++ b/packages/cli/src/ui/commands/docsCommand.test.ts @@ -56,7 +56,7 @@ describe('docsCommand', () => { } // Simulate a sandbox environment - process.env.SANDBOX = 'gemini-sandbox'; + vi.stubEnv('SANDBOX', 'gemini-sandbox'); const docsUrl = 'https://goo.gle/gemini-cli-docs'; await docsCommand.action(mockContext, ''); @@ -79,7 +79,7 @@ describe('docsCommand', () => { } // Simulate the specific 'sandbox-exec' environment - process.env.SANDBOX = 'sandbox-exec'; + vi.stubEnv('SANDBOX', 'sandbox-exec'); const docsUrl = 'https://goo.gle/gemini-cli-docs'; await docsCommand.action(mockContext, ''); diff --git a/packages/cli/src/ui/commands/docsCommand.ts b/packages/cli/src/ui/commands/docsCommand.ts index 922b236a..3ff160a7 100644 --- a/packages/cli/src/ui/commands/docsCommand.ts +++ b/packages/cli/src/ui/commands/docsCommand.ts @@ -20,7 +20,7 @@ export const docsCommand: SlashCommand = { action: async (context: CommandContext): Promise => { const docsUrl = 'https://goo.gle/gemini-cli-docs'; - if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') { + if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') { context.ui.addItem( { type: MessageType.INFO, diff --git a/packages/cli/src/ui/commands/mcpCommand.test.ts b/packages/cli/src/ui/commands/mcpCommand.test.ts index 0f339665..6e48c2f9 100644 --- a/packages/cli/src/ui/commands/mcpCommand.test.ts +++ b/packages/cli/src/ui/commands/mcpCommand.test.ts @@ -73,7 +73,7 @@ describe('mcpCommand', () => { vi.clearAllMocks(); // Set up default mock environment - delete process.env.SANDBOX; + vi.unstubAllEnvs(); // Default mock implementations vi.mocked(getMCPServerStatus).mockReturnValue(MCPServerStatus.CONNECTED); diff --git a/packages/cli/src/ui/components/AuthDialog.test.tsx b/packages/cli/src/ui/components/AuthDialog.test.tsx index 3efc3c01..38c6972a 100644 --- a/packages/cli/src/ui/components/AuthDialog.test.tsx +++ b/packages/cli/src/ui/components/AuthDialog.test.tsx @@ -17,8 +17,8 @@ describe('AuthDialog', () => { beforeEach(() => { originalEnv = { ...process.env }; - process.env.GEMINI_API_KEY = ''; - process.env.GEMINI_DEFAULT_AUTH_TYPE = ''; + process.env['GEMINI_API_KEY'] = ''; + process.env['GEMINI_DEFAULT_AUTH_TYPE'] = ''; vi.clearAllMocks(); }); @@ -27,7 +27,7 @@ describe('AuthDialog', () => { }); it('should show an error if the initial auth type is invalid', () => { - process.env.GEMINI_API_KEY = ''; + process.env['GEMINI_API_KEY'] = ''; const settings: LoadedSettings = new LoadedSettings( { @@ -62,7 +62,7 @@ describe('AuthDialog', () => { describe('GEMINI_API_KEY environment variable', () => { it('should detect GEMINI_API_KEY environment variable', () => { - process.env.GEMINI_API_KEY = 'foobar'; + process.env['GEMINI_API_KEY'] = 'foobar'; const settings: LoadedSettings = new LoadedSettings( { @@ -94,8 +94,8 @@ describe('AuthDialog', () => { }); it('should not show the GEMINI_API_KEY message if GEMINI_DEFAULT_AUTH_TYPE is set to something else', () => { - process.env.GEMINI_API_KEY = 'foobar'; - process.env.GEMINI_DEFAULT_AUTH_TYPE = AuthType.LOGIN_WITH_GOOGLE; + process.env['GEMINI_API_KEY'] = 'foobar'; + process.env['GEMINI_DEFAULT_AUTH_TYPE'] = AuthType.LOGIN_WITH_GOOGLE; const settings: LoadedSettings = new LoadedSettings( { @@ -127,8 +127,8 @@ describe('AuthDialog', () => { }); it('should show the GEMINI_API_KEY message if GEMINI_DEFAULT_AUTH_TYPE is set to use api key', () => { - process.env.GEMINI_API_KEY = 'foobar'; - process.env.GEMINI_DEFAULT_AUTH_TYPE = AuthType.USE_GEMINI; + process.env['GEMINI_API_KEY'] = 'foobar'; + process.env['GEMINI_DEFAULT_AUTH_TYPE'] = AuthType.USE_GEMINI; const settings: LoadedSettings = new LoadedSettings( { @@ -162,7 +162,7 @@ describe('AuthDialog', () => { describe('GEMINI_DEFAULT_AUTH_TYPE environment variable', () => { it('should select the auth type specified by GEMINI_DEFAULT_AUTH_TYPE', () => { - process.env.GEMINI_DEFAULT_AUTH_TYPE = AuthType.LOGIN_WITH_GOOGLE; + process.env['GEMINI_DEFAULT_AUTH_TYPE'] = AuthType.LOGIN_WITH_GOOGLE; const settings: LoadedSettings = new LoadedSettings( { @@ -222,7 +222,7 @@ describe('AuthDialog', () => { }); it('should show an error and fall back to default if GEMINI_DEFAULT_AUTH_TYPE is invalid', () => { - process.env.GEMINI_DEFAULT_AUTH_TYPE = 'invalid-auth-type'; + process.env['GEMINI_DEFAULT_AUTH_TYPE'] = 'invalid-auth-type'; const settings: LoadedSettings = new LoadedSettings( { diff --git a/packages/cli/src/ui/components/AuthDialog.tsx b/packages/cli/src/ui/components/AuthDialog.tsx index c353727c..fb3d116b 100644 --- a/packages/cli/src/ui/components/AuthDialog.tsx +++ b/packages/cli/src/ui/components/AuthDialog.tsx @@ -42,18 +42,18 @@ export function AuthDialog({ } const defaultAuthType = parseDefaultAuthType( - process.env.GEMINI_DEFAULT_AUTH_TYPE, + process.env['GEMINI_DEFAULT_AUTH_TYPE'], ); - if (process.env.GEMINI_DEFAULT_AUTH_TYPE && defaultAuthType === null) { + if (process.env['GEMINI_DEFAULT_AUTH_TYPE'] && defaultAuthType === null) { return ( - `Invalid value for GEMINI_DEFAULT_AUTH_TYPE: "${process.env.GEMINI_DEFAULT_AUTH_TYPE}". ` + + `Invalid value for GEMINI_DEFAULT_AUTH_TYPE: "${process.env['GEMINI_DEFAULT_AUTH_TYPE']}". ` + `Valid values are: ${Object.values(AuthType).join(', ')}.` ); } if ( - process.env.GEMINI_API_KEY && + process.env['GEMINI_API_KEY'] && (!defaultAuthType || defaultAuthType === AuthType.USE_GEMINI) ) { return 'Existing API key detected (GEMINI_API_KEY). Select "Gemini API Key" option to use it.'; @@ -65,7 +65,7 @@ export function AuthDialog({ label: 'Login with Google', value: AuthType.LOGIN_WITH_GOOGLE, }, - ...(process.env.CLOUD_SHELL === 'true' + ...(process.env['CLOUD_SHELL'] === 'true' ? [ { label: 'Use Cloud Shell user credentials', @@ -86,13 +86,13 @@ export function AuthDialog({ } const defaultAuthType = parseDefaultAuthType( - process.env.GEMINI_DEFAULT_AUTH_TYPE, + process.env['GEMINI_DEFAULT_AUTH_TYPE'], ); if (defaultAuthType) { return item.value === defaultAuthType; } - if (process.env.GEMINI_API_KEY) { + if (process.env['GEMINI_API_KEY']) { return item.value === AuthType.USE_GEMINI; } diff --git a/packages/cli/src/ui/components/Footer.tsx b/packages/cli/src/ui/components/Footer.tsx index 09b94ec1..ee644bc5 100644 --- a/packages/cli/src/ui/components/Footer.tsx +++ b/packages/cli/src/ui/components/Footer.tsx @@ -103,15 +103,16 @@ export const Footer: React.FC = ({ > {isTrustedFolder === false ? ( untrusted - ) : process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec' ? ( + ) : process.env['SANDBOX'] && + process.env['SANDBOX'] !== 'sandbox-exec' ? ( - {process.env.SANDBOX.replace(/^gemini-(?:cli-)?/, '')} + {process.env['SANDBOX'].replace(/^gemini-(?:cli-)?/, '')} - ) : process.env.SANDBOX === 'sandbox-exec' ? ( + ) : process.env['SANDBOX'] === 'sandbox-exec' ? ( macOS Seatbelt{' '} - ({process.env.SEATBELT_PROFILE}) + ({process.env['SEATBELT_PROFILE']}) ) : ( diff --git a/packages/cli/src/ui/components/shared/MaxSizedBox.tsx b/packages/cli/src/ui/components/shared/MaxSizedBox.tsx index 346472bf..0a51e2fd 100644 --- a/packages/cli/src/ui/components/shared/MaxSizedBox.tsx +++ b/packages/cli/src/ui/components/shared/MaxSizedBox.tsx @@ -321,7 +321,7 @@ function visitBoxRow(element: React.ReactNode): Row { const segment: StyledText = { text, props: parentProps ?? {} }; // Check the 'wrap' property from the merged props to decide the segment type. - if (parentProps === undefined || parentProps.wrap === 'wrap') { + if (parentProps === undefined || parentProps['wrap'] === 'wrap') { hasSeenWrapped = true; row.segments.push(segment); } else { diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.ts b/packages/cli/src/ui/hooks/atCommandProcessor.ts index cef2f811..50b8fbbd 100644 --- a/packages/cli/src/ui/hooks/atCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/atCommandProcessor.ts @@ -362,20 +362,20 @@ export async function handleAtCommand({ // Inform user about ignored paths const totalIgnored = - ignoredByReason.git.length + - ignoredByReason.gemini.length + - ignoredByReason.both.length; + ignoredByReason['git'].length + + ignoredByReason['gemini'].length + + ignoredByReason['both'].length; if (totalIgnored > 0) { const messages = []; - if (ignoredByReason.git.length) { - messages.push(`Git-ignored: ${ignoredByReason.git.join(', ')}`); + if (ignoredByReason['git'].length) { + messages.push(`Git-ignored: ${ignoredByReason['git'].join(', ')}`); } - if (ignoredByReason.gemini.length) { - messages.push(`Gemini-ignored: ${ignoredByReason.gemini.join(', ')}`); + if (ignoredByReason['gemini'].length) { + messages.push(`Gemini-ignored: ${ignoredByReason['gemini'].join(', ')}`); } - if (ignoredByReason.both.length) { - messages.push(`Ignored by both: ${ignoredByReason.both.join(', ')}`); + if (ignoredByReason['both'].length) { + messages.push(`Ignored by both: ${ignoredByReason['both'].join(', ')}`); } const message = `Ignored ${totalIgnored} files:\n${messages.join('\n')}`; diff --git a/packages/cli/src/ui/hooks/useKeypress.test.ts b/packages/cli/src/ui/hooks/useKeypress.test.ts index b804eb90..138c927c 100644 --- a/packages/cli/src/ui/hooks/useKeypress.test.ts +++ b/packages/cli/src/ui/hooks/useKeypress.test.ts @@ -116,7 +116,7 @@ describe('useKeypress', () => { }); originalNodeVersion = process.versions.node; - delete process.env['PASTE_WORKAROUND']; + vi.unstubAllEnvs(); }); afterEach(() => { @@ -197,7 +197,7 @@ describe('useKeypress', () => { description: 'Workaround Env Var', setup: () => { setNodeVersion('20.0.0'); - process.env['PASTE_WORKAROUND'] = 'true'; + vi.stubEnv('PASTE_WORKAROUND', 'true'); }, isLegacy: true, }, diff --git a/packages/cli/src/ui/hooks/useThemeCommand.ts b/packages/cli/src/ui/hooks/useThemeCommand.ts index 6c9e60d8..cf881f53 100644 --- a/packages/cli/src/ui/hooks/useThemeCommand.ts +++ b/packages/cli/src/ui/hooks/useThemeCommand.ts @@ -39,7 +39,7 @@ export const useThemeCommand = ( }, [loadedSettings.merged.theme, setThemeError]); const openThemeDialog = useCallback(() => { - if (process.env.NO_COLOR) { + if (process.env['NO_COLOR']) { addItem( { type: MessageType.INFO, diff --git a/packages/cli/src/ui/themes/color-utils.test.ts b/packages/cli/src/ui/themes/color-utils.test.ts index cafc28dd..dfb1be83 100644 --- a/packages/cli/src/ui/themes/color-utils.test.ts +++ b/packages/cli/src/ui/themes/color-utils.test.ts @@ -124,18 +124,18 @@ describe('Color Utils', () => { describe('CSS_NAME_TO_HEX_MAP', () => { it('should contain expected CSS color mappings', () => { - expect(CSS_NAME_TO_HEX_MAP.darkkhaki).toBe('#bdb76b'); - expect(CSS_NAME_TO_HEX_MAP.coral).toBe('#ff7f50'); - expect(CSS_NAME_TO_HEX_MAP.teal).toBe('#008080'); - expect(CSS_NAME_TO_HEX_MAP.tomato).toBe('#ff6347'); - expect(CSS_NAME_TO_HEX_MAP.turquoise).toBe('#40e0d0'); + expect(CSS_NAME_TO_HEX_MAP['darkkhaki']).toBe('#bdb76b'); + expect(CSS_NAME_TO_HEX_MAP['coral']).toBe('#ff7f50'); + expect(CSS_NAME_TO_HEX_MAP['teal']).toBe('#008080'); + expect(CSS_NAME_TO_HEX_MAP['tomato']).toBe('#ff6347'); + expect(CSS_NAME_TO_HEX_MAP['turquoise']).toBe('#40e0d0'); }); it('should not contain Ink-supported color names', () => { - expect(CSS_NAME_TO_HEX_MAP.black).toBeUndefined(); - expect(CSS_NAME_TO_HEX_MAP.red).toBeUndefined(); - expect(CSS_NAME_TO_HEX_MAP.green).toBeUndefined(); - expect(CSS_NAME_TO_HEX_MAP.blue).toBeUndefined(); + expect(CSS_NAME_TO_HEX_MAP['black']).toBeUndefined(); + expect(CSS_NAME_TO_HEX_MAP['red']).toBeUndefined(); + expect(CSS_NAME_TO_HEX_MAP['green']).toBeUndefined(); + expect(CSS_NAME_TO_HEX_MAP['blue']).toBeUndefined(); }); }); diff --git a/packages/cli/src/ui/themes/theme-manager.test.ts b/packages/cli/src/ui/themes/theme-manager.test.ts index 0b2c17c0..91e51bf9 100644 --- a/packages/cli/src/ui/themes/theme-manager.test.ts +++ b/packages/cli/src/ui/themes/theme-manager.test.ts @@ -5,8 +5,8 @@ */ // Patch: Unset NO_COLOR at the very top before any imports -if (process.env.NO_COLOR !== undefined) { - delete process.env.NO_COLOR; +if (process.env['NO_COLOR'] !== undefined) { + delete process.env['NO_COLOR']; } import { describe, it, expect, beforeEach } from 'vitest'; @@ -87,13 +87,13 @@ describe('ThemeManager', () => { }); it('should return NoColorTheme if NO_COLOR is set', () => { - const original = process.env.NO_COLOR; - process.env.NO_COLOR = '1'; + const original = process.env['NO_COLOR']; + process.env['NO_COLOR'] = '1'; expect(themeManager.getActiveTheme().name).toBe('NoColor'); if (original === undefined) { - delete process.env.NO_COLOR; + delete process.env['NO_COLOR']; } else { - process.env.NO_COLOR = original; + process.env['NO_COLOR'] = original; } }); }); diff --git a/packages/cli/src/ui/themes/theme-manager.ts b/packages/cli/src/ui/themes/theme-manager.ts index b19b06a9..5258bf76 100644 --- a/packages/cli/src/ui/themes/theme-manager.ts +++ b/packages/cli/src/ui/themes/theme-manager.ts @@ -125,7 +125,7 @@ class ThemeManager { * @returns The active theme. */ getActiveTheme(): Theme { - if (process.env.NO_COLOR) { + if (process.env['NO_COLOR']) { return NoColorTheme; } // Ensure the active theme is always valid (fall back to default if not) diff --git a/packages/cli/src/ui/utils/CodeColorizer.tsx b/packages/cli/src/ui/utils/CodeColorizer.tsx index b183d556..775ffadc 100644 --- a/packages/cli/src/ui/utils/CodeColorizer.tsx +++ b/packages/cli/src/ui/utils/CodeColorizer.tsx @@ -38,7 +38,7 @@ function renderHastNode( // Handle Element Nodes: Determine color and pass it down, don't wrap if (node.type === 'element') { const nodeClasses: string[] = - (node.properties?.className as string[]) || []; + (node.properties?.['className'] as string[]) || []; let elementColor: string | undefined = undefined; // Find color defined specifically for this element's class diff --git a/packages/cli/src/ui/utils/terminalSetup.ts b/packages/cli/src/ui/utils/terminalSetup.ts index 7f944847..d41d2075 100644 --- a/packages/cli/src/ui/utils/terminalSetup.ts +++ b/packages/cli/src/ui/utils/terminalSetup.ts @@ -52,22 +52,24 @@ type SupportedTerminal = 'vscode' | 'cursor' | 'windsurf'; // Terminal detection async function detectTerminal(): Promise { - const termProgram = process.env.TERM_PROGRAM; + const termProgram = process.env['TERM_PROGRAM']; // Check VS Code and its forks - check forks first to avoid false positives // Check for Cursor-specific indicators if ( - process.env.CURSOR_TRACE_ID || - process.env.VSCODE_GIT_ASKPASS_MAIN?.toLowerCase().includes('cursor') + process.env['CURSOR_TRACE_ID'] || + process.env['VSCODE_GIT_ASKPASS_MAIN']?.toLowerCase().includes('cursor') ) { return 'cursor'; } // Check for Windsurf-specific indicators - if (process.env.VSCODE_GIT_ASKPASS_MAIN?.toLowerCase().includes('windsurf')) { + if ( + process.env['VSCODE_GIT_ASKPASS_MAIN']?.toLowerCase().includes('windsurf') + ) { return 'windsurf'; } // Check VS Code last since forks may also set VSCODE env vars - if (termProgram === 'vscode' || process.env.VSCODE_GIT_IPC_HANDLE) { + if (termProgram === 'vscode' || process.env['VSCODE_GIT_IPC_HANDLE']) { return 'vscode'; } @@ -118,10 +120,10 @@ function getVSCodeStyleConfigDir(appName: string): string | null { 'User', ); } else if (platform === 'win32') { - if (!process.env.APPDATA) { + if (!process.env['APPDATA']) { return null; } - return path.join(process.env.APPDATA, appName, 'User'); + return path.join(process.env['APPDATA'], appName, 'User'); } else { return path.join(os.homedir(), '.config', appName, 'User'); } diff --git a/packages/cli/src/ui/utils/updateCheck.test.ts b/packages/cli/src/ui/utils/updateCheck.test.ts index c2b56a03..c7214e8b 100644 --- a/packages/cli/src/ui/utils/updateCheck.test.ts +++ b/packages/cli/src/ui/utils/updateCheck.test.ts @@ -22,7 +22,7 @@ describe('checkForUpdates', () => { vi.useFakeTimers(); vi.resetAllMocks(); // Clear DEV environment variable before each test - delete process.env.DEV; + delete process.env['DEV']; }); afterEach(() => { @@ -31,7 +31,7 @@ describe('checkForUpdates', () => { }); it('should return null when running from source (DEV=true)', async () => { - process.env.DEV = 'true'; + process.env['DEV'] = 'true'; getPackageJson.mockResolvedValue({ name: 'test-package', version: '1.0.0', diff --git a/packages/cli/src/ui/utils/updateCheck.ts b/packages/cli/src/ui/utils/updateCheck.ts index f4c18586..bb6c507c 100644 --- a/packages/cli/src/ui/utils/updateCheck.ts +++ b/packages/cli/src/ui/utils/updateCheck.ts @@ -41,7 +41,7 @@ function getBestAvailableUpdate( export async function checkForUpdates(): Promise { try { // Skip update check when running from source (development mode) - if (process.env.DEV === 'true') { + if (process.env['DEV'] === 'true') { return null; } const packageJson = await getPackageJson(); diff --git a/packages/cli/src/utils/sandbox.ts b/packages/cli/src/utils/sandbox.ts index dfd2065f..dac34778 100644 --- a/packages/cli/src/utils/sandbox.ts +++ b/packages/cli/src/utils/sandbox.ts @@ -63,7 +63,7 @@ const BUILTIN_SEATBELT_PROFILES = [ * @returns {Promise} A promise that resolves to true if the current user's UID/GID should be used, false otherwise. */ async function shouldUseCurrentUserInSandbox(): Promise { - const envVar = process.env.SANDBOX_SET_UID_GID?.toLowerCase().trim(); + const envVar = process.env['SANDBOX_SET_UID_GID']?.toLowerCase().trim(); if (envVar === '1' || envVar === 'true') { return true; @@ -108,7 +108,7 @@ function parseImageName(image: string): string { } function ports(): string[] { - return (process.env.SANDBOX_PORTS ?? '') + return (process.env['SANDBOX_PORTS'] ?? '') .split(',') .filter((p) => p.trim()) .map((p) => p.trim()); @@ -121,8 +121,8 @@ function entrypoint(workdir: string): string[] { const pathSeparator = isWindows ? ';' : ':'; let pathSuffix = ''; - if (process.env.PATH) { - const paths = process.env.PATH.split(pathSeparator); + if (process.env['PATH']) { + const paths = process.env['PATH'].split(pathSeparator); for (const p of paths) { const containerPath = getContainerPath(p); if ( @@ -137,8 +137,8 @@ function entrypoint(workdir: string): string[] { } let pythonPathSuffix = ''; - if (process.env.PYTHONPATH) { - const paths = process.env.PYTHONPATH.split(pathSeparator); + if (process.env['PYTHONPATH']) { + const paths = process.env['PYTHONPATH'].split(pathSeparator); for (const p of paths) { const containerPath = getContainerPath(p); if ( @@ -168,12 +168,12 @@ function entrypoint(workdir: string): string[] { const cliArgs = process.argv.slice(2).map((arg) => quote([arg])); const cliCmd = - process.env.NODE_ENV === 'development' - ? process.env.DEBUG + process.env['NODE_ENV'] === 'development' + ? process.env['DEBUG'] ? 'npm run debug --' : 'npm rebuild && npm run start --' - : process.env.DEBUG - ? `node --inspect-brk=0.0.0.0:${process.env.DEBUG_PORT || '9229'} $(which gemini)` + : process.env['DEBUG'] + ? `node --inspect-brk=0.0.0.0:${process.env['DEBUG_PORT'] || '9229'} $(which gemini)` : 'gemini'; const args = [...shellCmds, cliCmd, ...cliArgs]; @@ -187,7 +187,7 @@ export async function start_sandbox( cliConfig?: Config, ) { const patcher = new ConsolePatcher({ - debugMode: cliConfig?.getDebugMode() || !!process.env.DEBUG, + debugMode: cliConfig?.getDebugMode() || !!process.env['DEBUG'], stderr: true, }); patcher.patch(); @@ -195,11 +195,11 @@ export async function start_sandbox( try { if (config.command === 'sandbox-exec') { // disallow BUILD_SANDBOX - if (process.env.BUILD_SANDBOX) { + if (process.env['BUILD_SANDBOX']) { console.error('ERROR: cannot BUILD_SANDBOX when using macOS Seatbelt'); process.exit(1); } - const profile = (process.env.SEATBELT_PROFILE ??= 'permissive-open'); + const profile = (process.env['SEATBELT_PROFILE'] ??= 'permissive-open'); let profileFile = new URL(`sandbox-macos-${profile}.sb`, import.meta.url) .pathname; // if profile name is not recognized, then look for file under project settings directory @@ -219,7 +219,7 @@ export async function start_sandbox( console.error(`using macos seatbelt (profile: ${profile}) ...`); // if DEBUG is set, convert to --inspect-brk in NODE_OPTIONS const nodeOptions = [ - ...(process.env.DEBUG ? ['--inspect-brk'] : []), + ...(process.env['DEBUG'] ? ['--inspect-brk'] : []), ...nodeArgs, ].join(' '); @@ -275,22 +275,22 @@ export async function start_sandbox( ].join(' '), ); // start and set up proxy if GEMINI_SANDBOX_PROXY_COMMAND is set - const proxyCommand = process.env.GEMINI_SANDBOX_PROXY_COMMAND; + const proxyCommand = process.env['GEMINI_SANDBOX_PROXY_COMMAND']; let proxyProcess: ChildProcess | undefined = undefined; let sandboxProcess: ChildProcess | undefined = undefined; const sandboxEnv = { ...process.env }; if (proxyCommand) { const proxy = - process.env.HTTPS_PROXY || - process.env.https_proxy || - process.env.HTTP_PROXY || - process.env.http_proxy || + process.env['HTTPS_PROXY'] || + process.env['https_proxy'] || + process.env['HTTP_PROXY'] || + process.env['http_proxy'] || 'http://localhost:8877'; sandboxEnv['HTTPS_PROXY'] = proxy; sandboxEnv['https_proxy'] = proxy; // lower-case can be required, e.g. for curl sandboxEnv['HTTP_PROXY'] = proxy; sandboxEnv['http_proxy'] = proxy; - const noProxy = process.env.NO_PROXY || process.env.no_proxy; + const noProxy = process.env['NO_PROXY'] || process.env['no_proxy']; if (noProxy) { sandboxEnv['NO_PROXY'] = noProxy; sandboxEnv['no_proxy'] = noProxy; @@ -358,7 +358,7 @@ export async function start_sandbox( // if BUILD_SANDBOX is set, then call scripts/build_sandbox.js under gemini-cli repo // // note this can only be done with binary linked from gemini-cli repo - if (process.env.BUILD_SANDBOX) { + if (process.env['BUILD_SANDBOX']) { if (!gcPath.includes('gemini-cli/packages/')) { console.error( 'ERROR: cannot build sandbox using installed gemini binary; ' + @@ -408,8 +408,8 @@ export async function start_sandbox( const args = ['run', '-i', '--rm', '--init', '--workdir', containerWorkdir]; // add custom flags from SANDBOX_FLAGS - if (process.env.SANDBOX_FLAGS) { - const flags = parse(process.env.SANDBOX_FLAGS, process.env).filter( + if (process.env['SANDBOX_FLAGS']) { + const flags = parse(process.env['SANDBOX_FLAGS'], process.env).filter( (f): f is string => typeof f === 'string', ); args.push(...flags); @@ -456,8 +456,8 @@ export async function start_sandbox( } // mount ADC file if GOOGLE_APPLICATION_CREDENTIALS is set - if (process.env.GOOGLE_APPLICATION_CREDENTIALS) { - const adcFile = process.env.GOOGLE_APPLICATION_CREDENTIALS; + if (process.env['GOOGLE_APPLICATION_CREDENTIALS']) { + const adcFile = process.env['GOOGLE_APPLICATION_CREDENTIALS']; if (fs.existsSync(adcFile)) { args.push('--volume', `${adcFile}:${getContainerPath(adcFile)}:ro`); args.push( @@ -468,8 +468,8 @@ export async function start_sandbox( } // mount paths listed in SANDBOX_MOUNTS - if (process.env.SANDBOX_MOUNTS) { - for (let mount of process.env.SANDBOX_MOUNTS.split(',')) { + if (process.env['SANDBOX_MOUNTS']) { + for (let mount of process.env['SANDBOX_MOUNTS'].split(',')) { if (mount.trim()) { // parse mount as from:to:opts let [from, to, opts] = mount.trim().split(':'); @@ -500,22 +500,22 @@ export async function start_sandbox( ports().forEach((p) => args.push('--publish', `${p}:${p}`)); // if DEBUG is set, expose debugging port - if (process.env.DEBUG) { - const debugPort = process.env.DEBUG_PORT || '9229'; + if (process.env['DEBUG']) { + const debugPort = process.env['DEBUG_PORT'] || '9229'; args.push(`--publish`, `${debugPort}:${debugPort}`); } // copy proxy environment variables, replacing localhost with SANDBOX_PROXY_NAME // copy as both upper-case and lower-case as is required by some utilities // GEMINI_SANDBOX_PROXY_COMMAND implies HTTPS_PROXY unless HTTP_PROXY is set - const proxyCommand = process.env.GEMINI_SANDBOX_PROXY_COMMAND; + const proxyCommand = process.env['GEMINI_SANDBOX_PROXY_COMMAND']; if (proxyCommand) { let proxy = - process.env.HTTPS_PROXY || - process.env.https_proxy || - process.env.HTTP_PROXY || - process.env.http_proxy || + process.env['HTTPS_PROXY'] || + process.env['https_proxy'] || + process.env['HTTP_PROXY'] || + process.env['http_proxy'] || 'http://localhost:8877'; proxy = proxy.replace('localhost', SANDBOX_PROXY_NAME); if (proxy) { @@ -524,7 +524,7 @@ export async function start_sandbox( args.push('--env', `HTTP_PROXY=${proxy}`); args.push('--env', `http_proxy=${proxy}`); } - const noProxy = process.env.NO_PROXY || process.env.no_proxy; + const noProxy = process.env['NO_PROXY'] || process.env['no_proxy']; if (noProxy) { args.push('--env', `NO_PROXY=${noProxy}`); args.push('--env', `no_proxy=${noProxy}`); @@ -562,56 +562,56 @@ export async function start_sandbox( args.push('--name', containerName, '--hostname', containerName); // copy GEMINI_API_KEY(s) - if (process.env.GEMINI_API_KEY) { - args.push('--env', `GEMINI_API_KEY=${process.env.GEMINI_API_KEY}`); + if (process.env['GEMINI_API_KEY']) { + args.push('--env', `GEMINI_API_KEY=${process.env['GEMINI_API_KEY']}`); } - if (process.env.GOOGLE_API_KEY) { - args.push('--env', `GOOGLE_API_KEY=${process.env.GOOGLE_API_KEY}`); + if (process.env['GOOGLE_API_KEY']) { + args.push('--env', `GOOGLE_API_KEY=${process.env['GOOGLE_API_KEY']}`); } // copy GOOGLE_GENAI_USE_VERTEXAI - if (process.env.GOOGLE_GENAI_USE_VERTEXAI) { + if (process.env['GOOGLE_GENAI_USE_VERTEXAI']) { args.push( '--env', - `GOOGLE_GENAI_USE_VERTEXAI=${process.env.GOOGLE_GENAI_USE_VERTEXAI}`, + `GOOGLE_GENAI_USE_VERTEXAI=${process.env['GOOGLE_GENAI_USE_VERTEXAI']}`, ); } // copy GOOGLE_GENAI_USE_GCA - if (process.env.GOOGLE_GENAI_USE_GCA) { + if (process.env['GOOGLE_GENAI_USE_GCA']) { args.push( '--env', - `GOOGLE_GENAI_USE_GCA=${process.env.GOOGLE_GENAI_USE_GCA}`, + `GOOGLE_GENAI_USE_GCA=${process.env['GOOGLE_GENAI_USE_GCA']}`, ); } // copy GOOGLE_CLOUD_PROJECT - if (process.env.GOOGLE_CLOUD_PROJECT) { + if (process.env['GOOGLE_CLOUD_PROJECT']) { args.push( '--env', - `GOOGLE_CLOUD_PROJECT=${process.env.GOOGLE_CLOUD_PROJECT}`, + `GOOGLE_CLOUD_PROJECT=${process.env['GOOGLE_CLOUD_PROJECT']}`, ); } // copy GOOGLE_CLOUD_LOCATION - if (process.env.GOOGLE_CLOUD_LOCATION) { + if (process.env['GOOGLE_CLOUD_LOCATION']) { args.push( '--env', - `GOOGLE_CLOUD_LOCATION=${process.env.GOOGLE_CLOUD_LOCATION}`, + `GOOGLE_CLOUD_LOCATION=${process.env['GOOGLE_CLOUD_LOCATION']}`, ); } // copy GEMINI_MODEL - if (process.env.GEMINI_MODEL) { - args.push('--env', `GEMINI_MODEL=${process.env.GEMINI_MODEL}`); + if (process.env['GEMINI_MODEL']) { + args.push('--env', `GEMINI_MODEL=${process.env['GEMINI_MODEL']}`); } // copy TERM and COLORTERM to try to maintain terminal setup - if (process.env.TERM) { - args.push('--env', `TERM=${process.env.TERM}`); + if (process.env['TERM']) { + args.push('--env', `TERM=${process.env['TERM']}`); } - if (process.env.COLORTERM) { - args.push('--env', `COLORTERM=${process.env.COLORTERM}`); + if (process.env['COLORTERM']) { + args.push('--env', `COLORTERM=${process.env['COLORTERM']}`); } // Pass through IDE mode environment variables @@ -630,7 +630,9 @@ export async function start_sandbox( // sandbox can then set up this new VIRTUAL_ENV directory using sandbox.bashrc (see below) // directory will be empty if not set up, which is still preferable to having host binaries if ( - process.env.VIRTUAL_ENV?.toLowerCase().startsWith(workdir.toLowerCase()) + process.env['VIRTUAL_ENV'] + ?.toLowerCase() + .startsWith(workdir.toLowerCase()) ) { const sandboxVenvPath = path.resolve( SETTINGS_DIRECTORY_NAME, @@ -641,17 +643,17 @@ export async function start_sandbox( } args.push( '--volume', - `${sandboxVenvPath}:${getContainerPath(process.env.VIRTUAL_ENV)}`, + `${sandboxVenvPath}:${getContainerPath(process.env['VIRTUAL_ENV'])}`, ); args.push( '--env', - `VIRTUAL_ENV=${getContainerPath(process.env.VIRTUAL_ENV)}`, + `VIRTUAL_ENV=${getContainerPath(process.env['VIRTUAL_ENV'])}`, ); } // copy additional environment variables from SANDBOX_ENV - if (process.env.SANDBOX_ENV) { - for (let env of process.env.SANDBOX_ENV.split(',')) { + if (process.env['SANDBOX_ENV']) { + for (let env of process.env['SANDBOX_ENV'].split(',')) { if ((env = env.trim())) { if (env.includes('=')) { console.error(`SANDBOX_ENV: ${env}`); @@ -667,7 +669,7 @@ export async function start_sandbox( } // copy NODE_OPTIONS - const existingNodeOptions = process.env.NODE_OPTIONS || ''; + const existingNodeOptions = process.env['NODE_OPTIONS'] || ''; const allNodeOptions = [ ...(existingNodeOptions ? [existingNodeOptions] : []), ...nodeArgs, @@ -692,7 +694,7 @@ export async function start_sandbox( let userFlag = ''; const finalEntrypoint = entrypoint(workdir); - if (process.env.GEMINI_CLI_INTEGRATION_TEST === 'true') { + if (process.env['GEMINI_CLI_INTEGRATION_TEST'] === 'true') { args.push('--user', 'root'); userFlag = '--user root'; } else if (await shouldUseCurrentUserInSandbox()) { diff --git a/packages/cli/src/utils/version.ts b/packages/cli/src/utils/version.ts index 3b7bb0e2..0318f380 100644 --- a/packages/cli/src/utils/version.ts +++ b/packages/cli/src/utils/version.ts @@ -8,5 +8,5 @@ import { getPackageJson } from './package.js'; export async function getCliVersion(): Promise { const pkgJson = await getPackageJson(); - return process.env.CLI_VERSION || pkgJson?.version || 'unknown'; + return process.env['CLI_VERSION'] || pkgJson?.version || 'unknown'; } diff --git a/packages/cli/src/validateNonInterActiveAuth.test.ts b/packages/cli/src/validateNonInterActiveAuth.test.ts index 7c079e25..2cf4a260 100644 --- a/packages/cli/src/validateNonInterActiveAuth.test.ts +++ b/packages/cli/src/validateNonInterActiveAuth.test.ts @@ -23,12 +23,12 @@ describe('validateNonInterActiveAuth', () => { >; beforeEach(() => { - originalEnvGeminiApiKey = process.env.GEMINI_API_KEY; - originalEnvVertexAi = process.env.GOOGLE_GENAI_USE_VERTEXAI; - originalEnvGcp = process.env.GOOGLE_GENAI_USE_GCA; - delete process.env.GEMINI_API_KEY; - delete process.env.GOOGLE_GENAI_USE_VERTEXAI; - delete process.env.GOOGLE_GENAI_USE_GCA; + originalEnvGeminiApiKey = process.env['GEMINI_API_KEY']; + originalEnvVertexAi = process.env['GOOGLE_GENAI_USE_VERTEXAI']; + originalEnvGcp = process.env['GOOGLE_GENAI_USE_GCA']; + delete process.env['GEMINI_API_KEY']; + delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; + delete process.env['GOOGLE_GENAI_USE_GCA']; consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); processExitSpy = vi.spyOn(process, 'exit').mockImplementation((code) => { throw new Error(`process.exit(${code}) called`); @@ -38,19 +38,19 @@ describe('validateNonInterActiveAuth', () => { afterEach(() => { if (originalEnvGeminiApiKey !== undefined) { - process.env.GEMINI_API_KEY = originalEnvGeminiApiKey; + process.env['GEMINI_API_KEY'] = originalEnvGeminiApiKey; } else { - delete process.env.GEMINI_API_KEY; + delete process.env['GEMINI_API_KEY']; } if (originalEnvVertexAi !== undefined) { - process.env.GOOGLE_GENAI_USE_VERTEXAI = originalEnvVertexAi; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = originalEnvVertexAi; } else { - delete process.env.GOOGLE_GENAI_USE_VERTEXAI; + delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; } if (originalEnvGcp !== undefined) { - process.env.GOOGLE_GENAI_USE_GCA = originalEnvGcp; + process.env['GOOGLE_GENAI_USE_GCA'] = originalEnvGcp; } else { - delete process.env.GOOGLE_GENAI_USE_GCA; + delete process.env['GOOGLE_GENAI_USE_GCA']; } vi.restoreAllMocks(); }); @@ -76,7 +76,7 @@ describe('validateNonInterActiveAuth', () => { }); it('uses LOGIN_WITH_GOOGLE if GOOGLE_GENAI_USE_GCA is set', async () => { - process.env.GOOGLE_GENAI_USE_GCA = 'true'; + process.env['GOOGLE_GENAI_USE_GCA'] = 'true'; const nonInteractiveConfig: NonInteractiveConfig = { refreshAuth: refreshAuthMock, }; @@ -89,7 +89,7 @@ describe('validateNonInterActiveAuth', () => { }); it('uses USE_GEMINI if GEMINI_API_KEY is set', async () => { - process.env.GEMINI_API_KEY = 'fake-key'; + process.env['GEMINI_API_KEY'] = 'fake-key'; const nonInteractiveConfig: NonInteractiveConfig = { refreshAuth: refreshAuthMock, }; @@ -102,9 +102,9 @@ describe('validateNonInterActiveAuth', () => { }); it('uses USE_VERTEX_AI if GOOGLE_GENAI_USE_VERTEXAI is true (with GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION)', async () => { - process.env.GOOGLE_GENAI_USE_VERTEXAI = 'true'; - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; - process.env.GOOGLE_CLOUD_LOCATION = 'us-central1'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'test-project'; + process.env['GOOGLE_CLOUD_LOCATION'] = 'us-central1'; const nonInteractiveConfig: NonInteractiveConfig = { refreshAuth: refreshAuthMock, }; @@ -117,8 +117,8 @@ describe('validateNonInterActiveAuth', () => { }); it('uses USE_VERTEX_AI if GOOGLE_GENAI_USE_VERTEXAI is true and GOOGLE_API_KEY is set', async () => { - process.env.GOOGLE_GENAI_USE_VERTEXAI = 'true'; - process.env.GOOGLE_API_KEY = 'vertex-api-key'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + process.env['GOOGLE_API_KEY'] = 'vertex-api-key'; const nonInteractiveConfig: NonInteractiveConfig = { refreshAuth: refreshAuthMock, }; @@ -131,11 +131,11 @@ describe('validateNonInterActiveAuth', () => { }); it('uses LOGIN_WITH_GOOGLE if GOOGLE_GENAI_USE_GCA is set, even with other env vars', async () => { - process.env.GOOGLE_GENAI_USE_GCA = 'true'; - process.env.GEMINI_API_KEY = 'fake-key'; - process.env.GOOGLE_GENAI_USE_VERTEXAI = 'true'; - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; - process.env.GOOGLE_CLOUD_LOCATION = 'us-central1'; + process.env['GOOGLE_GENAI_USE_GCA'] = 'true'; + process.env['GEMINI_API_KEY'] = 'fake-key'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'test-project'; + process.env['GOOGLE_CLOUD_LOCATION'] = 'us-central1'; const nonInteractiveConfig: NonInteractiveConfig = { refreshAuth: refreshAuthMock, }; @@ -148,10 +148,10 @@ describe('validateNonInterActiveAuth', () => { }); it('uses USE_VERTEX_AI if both GEMINI_API_KEY and GOOGLE_GENAI_USE_VERTEXAI are set', async () => { - process.env.GEMINI_API_KEY = 'fake-key'; - process.env.GOOGLE_GENAI_USE_VERTEXAI = 'true'; - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; - process.env.GOOGLE_CLOUD_LOCATION = 'us-central1'; + process.env['GEMINI_API_KEY'] = 'fake-key'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'test-project'; + process.env['GOOGLE_CLOUD_LOCATION'] = 'us-central1'; const nonInteractiveConfig: NonInteractiveConfig = { refreshAuth: refreshAuthMock, }; @@ -164,10 +164,10 @@ describe('validateNonInterActiveAuth', () => { }); it('uses USE_GEMINI if GOOGLE_GENAI_USE_VERTEXAI is false, GEMINI_API_KEY is set, and project/location are available', async () => { - process.env.GOOGLE_GENAI_USE_VERTEXAI = 'false'; - process.env.GEMINI_API_KEY = 'fake-key'; - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; - process.env.GOOGLE_CLOUD_LOCATION = 'us-central1'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; + process.env['GEMINI_API_KEY'] = 'fake-key'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'test-project'; + process.env['GOOGLE_CLOUD_LOCATION'] = 'us-central1'; const nonInteractiveConfig: NonInteractiveConfig = { refreshAuth: refreshAuthMock, }; @@ -181,7 +181,7 @@ describe('validateNonInterActiveAuth', () => { it('uses configuredAuthType if provided', async () => { // Set required env var for USE_GEMINI - process.env.GEMINI_API_KEY = 'fake-key'; + process.env['GEMINI_API_KEY'] = 'fake-key'; const nonInteractiveConfig: NonInteractiveConfig = { refreshAuth: refreshAuthMock, }; diff --git a/packages/cli/src/validateNonInterActiveAuth.ts b/packages/cli/src/validateNonInterActiveAuth.ts index 7e80b30e..33b965b7 100644 --- a/packages/cli/src/validateNonInterActiveAuth.ts +++ b/packages/cli/src/validateNonInterActiveAuth.ts @@ -9,13 +9,13 @@ import { USER_SETTINGS_PATH } from './config/settings.js'; import { validateAuthMethod } from './config/auth.js'; function getAuthTypeFromEnv(): AuthType | undefined { - if (process.env.GOOGLE_GENAI_USE_GCA === 'true') { + if (process.env['GOOGLE_GENAI_USE_GCA'] === 'true') { return AuthType.LOGIN_WITH_GOOGLE; } - if (process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true') { + if (process.env['GOOGLE_GENAI_USE_VERTEXAI'] === 'true') { return AuthType.USE_VERTEX_AI; } - if (process.env.GEMINI_API_KEY) { + if (process.env['GEMINI_API_KEY']) { return AuthType.USE_GEMINI; } return undefined; diff --git a/packages/core/src/code_assist/oauth2.test.ts b/packages/core/src/code_assist/oauth2.test.ts index 77a8fb90..821788d5 100644 --- a/packages/core/src/code_assist/oauth2.test.ts +++ b/packages/core/src/code_assist/oauth2.test.ts @@ -57,9 +57,7 @@ describe('oauth2', () => { fs.rmSync(tempHomeDir, { recursive: true, force: true }); vi.clearAllMocks(); resetOauthClientForTesting(); - delete process.env.CLOUD_SHELL; - delete process.env.GOOGLE_GENAI_USE_GCA; - delete process.env.GOOGLE_CLOUD_ACCESS_TOKEN; + vi.unstubAllEnvs(); }); it('should perform a web login', async () => { @@ -328,8 +326,8 @@ describe('oauth2', () => { describe('with GCP environment variables', () => { it('should use GOOGLE_CLOUD_ACCESS_TOKEN when GOOGLE_GENAI_USE_GCA is true', async () => { - process.env.GOOGLE_GENAI_USE_GCA = 'true'; - process.env.GOOGLE_CLOUD_ACCESS_TOKEN = 'gcp-access-token'; + vi.stubEnv('GOOGLE_GENAI_USE_GCA', 'true'); + vi.stubEnv('GOOGLE_CLOUD_ACCESS_TOKEN', 'gcp-access-token'); const mockSetCredentials = vi.fn(); const mockGetAccessToken = vi @@ -387,7 +385,7 @@ describe('oauth2', () => { }); it('should not use GCP token if GOOGLE_CLOUD_ACCESS_TOKEN is not set', async () => { - process.env.GOOGLE_GENAI_USE_GCA = 'true'; + vi.stubEnv('GOOGLE_GENAI_USE_GCA', 'true'); const mockSetCredentials = vi.fn(); const mockGetAccessToken = vi @@ -418,7 +416,7 @@ describe('oauth2', () => { }); it('should not use GCP token if GOOGLE_GENAI_USE_GCA is not set', async () => { - process.env.GOOGLE_CLOUD_ACCESS_TOKEN = 'gcp-access-token'; + vi.stubEnv('GOOGLE_CLOUD_ACCESS_TOKEN', 'gcp-access-token'); const mockSetCredentials = vi.fn(); const mockGetAccessToken = vi diff --git a/packages/core/src/code_assist/oauth2.ts b/packages/core/src/code_assist/oauth2.ts index f9518cbe..a4dcf8a7 100644 --- a/packages/core/src/code_assist/oauth2.ts +++ b/packages/core/src/code_assist/oauth2.ts @@ -81,11 +81,11 @@ async function initOauthClient( }); if ( - process.env.GOOGLE_GENAI_USE_GCA && - process.env.GOOGLE_CLOUD_ACCESS_TOKEN + process.env['GOOGLE_GENAI_USE_GCA'] && + process.env['GOOGLE_CLOUD_ACCESS_TOKEN'] ) { client.setCredentials({ - access_token: process.env.GOOGLE_CLOUD_ACCESS_TOKEN, + access_token: process.env['GOOGLE_CLOUD_ACCESS_TOKEN'], }); await fetchAndCacheUserInfo(client); return client; @@ -248,7 +248,7 @@ async function authWithUserCode(client: OAuth2Client): Promise { async function authWithWeb(client: OAuth2Client): Promise { const port = await getAvailablePort(); // The hostname used for the HTTP server binding (e.g., '0.0.0.0' in Docker). - const host = process.env.OAUTH_CALLBACK_HOST || 'localhost'; + const host = process.env['OAUTH_CALLBACK_HOST'] || 'localhost'; // The `redirectUri` sent to Google's authorization server MUST use a loopback IP literal // (i.e., 'localhost' or '127.0.0.1'). This is a strict security policy for credentials of // type 'Desktop app' or 'Web application' (when using loopback flow) to mitigate @@ -323,7 +323,7 @@ export function getAvailablePort(): Promise { return new Promise((resolve, reject) => { let port = 0; try { - const portStr = process.env.OAUTH_CALLBACK_PORT; + const portStr = process.env['OAUTH_CALLBACK_PORT']; if (portStr) { port = parseInt(portStr, 10); if (isNaN(port) || port <= 0 || port > 65535) { @@ -353,7 +353,8 @@ export function getAvailablePort(): Promise { async function loadCachedCredentials(client: OAuth2Client): Promise { try { const keyFile = - process.env.GOOGLE_APPLICATION_CREDENTIALS || getCachedCredentialPath(); + process.env['GOOGLE_APPLICATION_CREDENTIALS'] || + getCachedCredentialPath(); const creds = await fs.readFile(keyFile, 'utf-8'); client.setCredentials(JSON.parse(creds)); diff --git a/packages/core/src/code_assist/server.ts b/packages/core/src/code_assist/server.ts index 08339bdc..470143a6 100644 --- a/packages/core/src/code_assist/server.ts +++ b/packages/core/src/code_assist/server.ts @@ -214,7 +214,8 @@ export class CodeAssistServer implements ContentGenerator { } getMethodUrl(method: string): string { - const endpoint = process.env.CODE_ASSIST_ENDPOINT ?? CODE_ASSIST_ENDPOINT; + const endpoint = + process.env['CODE_ASSIST_ENDPOINT'] ?? CODE_ASSIST_ENDPOINT; return `${endpoint}/${CODE_ASSIST_API_VERSION}:${method}`; } } diff --git a/packages/core/src/code_assist/setup.test.ts b/packages/core/src/code_assist/setup.test.ts index cba051dd..61a7431a 100644 --- a/packages/core/src/code_assist/setup.test.ts +++ b/packages/core/src/code_assist/setup.test.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { setupUser, ProjectIdRequiredError } from './setup.js'; import { CodeAssistServer } from '../code_assist/server.js'; import { OAuth2Client } from 'google-auth-library'; @@ -50,8 +50,12 @@ describe('setupUser for existing user', () => { ); }); + afterEach(() => { + vi.unstubAllEnvs(); + }); + it('should use GOOGLE_CLOUD_PROJECT when set and project from server is undefined', async () => { - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; + vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'test-project'); mockLoad.mockResolvedValue({ currentTier: mockPaidTier, }); @@ -66,7 +70,7 @@ describe('setupUser for existing user', () => { }); it('should ignore GOOGLE_CLOUD_PROJECT when project from server is set', async () => { - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; + vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'test-project'); mockLoad.mockResolvedValue({ cloudaicompanionProject: 'server-project', currentTier: mockPaidTier, @@ -86,7 +90,7 @@ describe('setupUser for existing user', () => { }); it('should throw ProjectIdRequiredError when no project ID is available', async () => { - delete process.env.GOOGLE_CLOUD_PROJECT; + vi.stubEnv('GOOGLE_CLOUD_PROJECT', ''); // And the server itself requires a project ID internally vi.mocked(CodeAssistServer).mockImplementation(() => { throw new ProjectIdRequiredError(); @@ -122,8 +126,12 @@ describe('setupUser for new user', () => { ); }); + afterEach(() => { + vi.unstubAllEnvs(); + }); + it('should use GOOGLE_CLOUD_PROJECT when set and onboard a new paid user', async () => { - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; + vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'test-project'); mockLoad.mockResolvedValue({ allowedTiers: [mockPaidTier], }); @@ -153,7 +161,7 @@ describe('setupUser for new user', () => { }); it('should onboard a new free user when GOOGLE_CLOUD_PROJECT is not set', async () => { - delete process.env.GOOGLE_CLOUD_PROJECT; + vi.stubEnv('GOOGLE_CLOUD_PROJECT', ''); mockLoad.mockResolvedValue({ allowedTiers: [mockFreeTier], }); @@ -182,7 +190,7 @@ describe('setupUser for new user', () => { }); it('should use GOOGLE_CLOUD_PROJECT when onboard response has no project ID', async () => { - process.env.GOOGLE_CLOUD_PROJECT = 'test-project'; + vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'test-project'); mockLoad.mockResolvedValue({ allowedTiers: [mockPaidTier], }); @@ -200,7 +208,7 @@ describe('setupUser for new user', () => { }); it('should throw ProjectIdRequiredError when no project ID is available', async () => { - delete process.env.GOOGLE_CLOUD_PROJECT; + vi.stubEnv('GOOGLE_CLOUD_PROJECT', ''); mockLoad.mockResolvedValue({ allowedTiers: [mockPaidTier], }); diff --git a/packages/core/src/code_assist/setup.ts b/packages/core/src/code_assist/setup.ts index 2e460c98..d563301d 100644 --- a/packages/core/src/code_assist/setup.ts +++ b/packages/core/src/code_assist/setup.ts @@ -33,7 +33,7 @@ export interface UserData { * @returns the user's actual project id */ export async function setupUser(client: OAuth2Client): Promise { - const projectId = process.env.GOOGLE_CLOUD_PROJECT || undefined; + const projectId = process.env['GOOGLE_CLOUD_PROJECT'] || undefined; const caServer = new CodeAssistServer(client, projectId, {}, '', undefined); const coreClientMetadata: ClientMetadata = { ideType: 'IDE_UNSPECIFIED', diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 49f9ab45..acaf5eb1 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -458,7 +458,7 @@ export class Config { isRestrictiveSandbox(): boolean { const sandboxConfig = this.getSandbox(); - const seatbeltProfile = process.env.SEATBELT_PROFILE; + const seatbeltProfile = process.env['SEATBELT_PROFILE']; return ( !!sandboxConfig && sandboxConfig.command === 'sandbox-exec' && diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index a2abd0d0..66f3e08f 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -286,7 +286,7 @@ export class GeminiClient { const contextData: Record = {}; if (activeFile) { - contextData.activeFile = { + contextData['activeFile'] = { path: activeFile.path, cursor: activeFile.cursor ? { @@ -299,7 +299,7 @@ export class GeminiClient { } if (otherOpenFiles.length > 0) { - contextData.otherOpenFiles = otherOpenFiles; + contextData['otherOpenFiles'] = otherOpenFiles; } if (Object.keys(contextData).length === 0) { @@ -345,7 +345,7 @@ export class GeminiClient { } } if (openedFiles.length > 0) { - changes.filesOpened = openedFiles; + changes['filesOpened'] = openedFiles; } const closedFiles: string[] = []; @@ -355,7 +355,7 @@ export class GeminiClient { } } if (closedFiles.length > 0) { - changes.filesClosed = closedFiles; + changes['filesClosed'] = closedFiles; } const lastActiveFile = ( @@ -367,7 +367,7 @@ export class GeminiClient { if (currentActiveFile) { if (!lastActiveFile || lastActiveFile.path !== currentActiveFile.path) { - changes.activeFileChanged = { + changes['activeFileChanged'] = { path: currentActiveFile.path, cursor: currentActiveFile.cursor ? { @@ -386,7 +386,7 @@ export class GeminiClient { lastCursor.line !== currentCursor.line || lastCursor.character !== currentCursor.character) ) { - changes.cursorMoved = { + changes['cursorMoved'] = { path: currentActiveFile.path, cursor: { line: currentCursor.line, @@ -398,14 +398,14 @@ export class GeminiClient { const lastSelectedText = lastActiveFile.selectedText || ''; const currentSelectedText = currentActiveFile.selectedText || ''; if (lastSelectedText !== currentSelectedText) { - changes.selectionChanged = { + changes['selectionChanged'] = { path: currentActiveFile.path, selectedText: currentSelectedText, }; } } } else if (lastActiveFile) { - changes.activeFileChanged = { + changes['activeFileChanged'] = { path: null, previousPath: lastActiveFile.path, }; @@ -415,7 +415,7 @@ export class GeminiClient { return { contextParts: [], newIdeContext: currentIdeContext }; } - delta.changes = changes; + delta['changes'] = changes; const jsonString = JSON.stringify(delta, null, 2); const contextParts = [ "Here is a summary of changes in the user's editor context, in JSON format. This is for your information only.", diff --git a/packages/core/src/core/contentGenerator.test.ts b/packages/core/src/core/contentGenerator.test.ts index e89b5bc7..b92e8046 100644 --- a/packages/core/src/core/contentGenerator.test.ts +++ b/packages/core/src/core/contentGenerator.test.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect, vi, beforeEach, afterAll } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { createContentGenerator, AuthType, @@ -72,7 +72,6 @@ describe('createContentGenerator', () => { }); describe('createContentGeneratorConfig', () => { - const originalEnv = process.env; const mockConfig = { getModel: vi.fn().mockReturnValue('gemini-pro'), setModel: vi.fn(), @@ -83,18 +82,15 @@ describe('createContentGeneratorConfig', () => { beforeEach(() => { // Reset modules to re-evaluate imports and environment variables vi.resetModules(); - // Restore process.env before each test - process.env = { ...originalEnv }; vi.clearAllMocks(); }); - afterAll(() => { - // Restore original process.env after all tests - process.env = originalEnv; + afterEach(() => { + vi.unstubAllEnvs(); }); it('should configure for Gemini using GEMINI_API_KEY when set', async () => { - process.env.GEMINI_API_KEY = 'env-gemini-key'; + vi.stubEnv('GEMINI_API_KEY', 'env-gemini-key'); const config = await createContentGeneratorConfig( mockConfig, AuthType.USE_GEMINI, @@ -104,7 +100,7 @@ describe('createContentGeneratorConfig', () => { }); it('should not configure for Gemini if GEMINI_API_KEY is empty', async () => { - process.env.GEMINI_API_KEY = ''; + vi.stubEnv('GEMINI_API_KEY', ''); const config = await createContentGeneratorConfig( mockConfig, AuthType.USE_GEMINI, @@ -114,7 +110,7 @@ describe('createContentGeneratorConfig', () => { }); it('should configure for Vertex AI using GOOGLE_API_KEY when set', async () => { - process.env.GOOGLE_API_KEY = 'env-google-key'; + vi.stubEnv('GOOGLE_API_KEY', 'env-google-key'); const config = await createContentGeneratorConfig( mockConfig, AuthType.USE_VERTEX_AI, @@ -124,8 +120,8 @@ describe('createContentGeneratorConfig', () => { }); it('should configure for Vertex AI using GCP project and location when set', async () => { - process.env.GOOGLE_CLOUD_PROJECT = 'env-gcp-project'; - process.env.GOOGLE_CLOUD_LOCATION = 'env-gcp-location'; + vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'env-gcp-project'); + vi.stubEnv('GOOGLE_CLOUD_LOCATION', 'env-gcp-location'); const config = await createContentGeneratorConfig( mockConfig, AuthType.USE_VERTEX_AI, @@ -135,9 +131,9 @@ describe('createContentGeneratorConfig', () => { }); it('should not configure for Vertex AI if required env vars are empty', async () => { - process.env.GOOGLE_API_KEY = ''; - process.env.GOOGLE_CLOUD_PROJECT = ''; - process.env.GOOGLE_CLOUD_LOCATION = ''; + vi.stubEnv('GOOGLE_API_KEY', ''); + vi.stubEnv('GOOGLE_CLOUD_PROJECT', ''); + vi.stubEnv('GOOGLE_CLOUD_LOCATION', ''); const config = await createContentGeneratorConfig( mockConfig, AuthType.USE_VERTEX_AI, diff --git a/packages/core/src/core/contentGenerator.ts b/packages/core/src/core/contentGenerator.ts index 599a569b..19b285c7 100644 --- a/packages/core/src/core/contentGenerator.ts +++ b/packages/core/src/core/contentGenerator.ts @@ -60,10 +60,10 @@ export function createContentGeneratorConfig( config: Config, authType: AuthType | undefined, ): ContentGeneratorConfig { - const geminiApiKey = process.env.GEMINI_API_KEY || undefined; - const googleApiKey = process.env.GOOGLE_API_KEY || undefined; - const googleCloudProject = process.env.GOOGLE_CLOUD_PROJECT || undefined; - const googleCloudLocation = process.env.GOOGLE_CLOUD_LOCATION || undefined; + const geminiApiKey = process.env['GEMINI_API_KEY'] || undefined; + const googleApiKey = process.env['GOOGLE_API_KEY'] || undefined; + const googleCloudProject = process.env['GOOGLE_CLOUD_PROJECT'] || undefined; + const googleCloudLocation = process.env['GOOGLE_CLOUD_LOCATION'] || undefined; // Use runtime model from config if available; otherwise, fall back to parameter or default const effectiveModel = config.getModel() || DEFAULT_GEMINI_MODEL; @@ -107,7 +107,7 @@ export async function createContentGenerator( gcConfig: Config, sessionId?: string, ): Promise { - const version = process.env.CLI_VERSION || process.version; + const version = process.env['CLI_VERSION'] || process.version; const httpOptions = { headers: { 'User-Agent': `GeminiCLI/${version} (${process.platform}; ${process.arch})`, diff --git a/packages/core/src/core/coreToolScheduler.ts b/packages/core/src/core/coreToolScheduler.ts index 0d442f9d..1a031de5 100644 --- a/packages/core/src/core/coreToolScheduler.ts +++ b/packages/core/src/core/coreToolScheduler.ts @@ -171,10 +171,10 @@ export function convertToFunctionResponse( // After this point, contentToProcess is a single Part object. if (contentToProcess.functionResponse) { - if (contentToProcess.functionResponse.response?.content) { + if (contentToProcess.functionResponse.response?.['content']) { const stringifiedOutput = getResponseTextFromParts( - contentToProcess.functionResponse.response.content as Part[], + contentToProcess.functionResponse.response['content'] as Part[], ) || ''; return createFunctionResponsePart(callId, toolName, stringifiedOutput); } diff --git a/packages/core/src/core/prompts.ts b/packages/core/src/core/prompts.ts index 95c55143..b4b9a979 100644 --- a/packages/core/src/core/prompts.ts +++ b/packages/core/src/core/prompts.ts @@ -24,7 +24,7 @@ export function getCoreSystemPrompt(userMemory?: string): string { // default path is .gemini/system.md but can be modified via custom path in GEMINI_SYSTEM_MD let systemMdEnabled = false; let systemMdPath = path.resolve(path.join(GEMINI_CONFIG_DIR, 'system.md')); - const systemMdVar = process.env.GEMINI_SYSTEM_MD; + const systemMdVar = process.env['GEMINI_SYSTEM_MD']; if (systemMdVar) { const systemMdVarLower = systemMdVar.toLowerCase(); if (!['0', 'false'].includes(systemMdVarLower)) { @@ -121,8 +121,8 @@ When requested to perform tasks like fixing bugs, adding features, refactoring, ${(function () { // Determine sandbox status based on environment variables - const isSandboxExec = process.env.SANDBOX === 'sandbox-exec'; - const isGenericSandbox = !!process.env.SANDBOX; // Check if SANDBOX is set to any non-empty value + const isSandboxExec = process.env['SANDBOX'] === 'sandbox-exec'; + const isGenericSandbox = !!process.env['SANDBOX']; // Check if SANDBOX is set to any non-empty value if (isSandboxExec) { return ` @@ -266,7 +266,7 @@ Your core function is efficient and safe assistance. Balance extreme conciseness `.trim(); // if GEMINI_WRITE_SYSTEM_MD is set (and not 0|false), write base system prompt to file - const writeSystemMdVar = process.env.GEMINI_WRITE_SYSTEM_MD; + const writeSystemMdVar = process.env['GEMINI_WRITE_SYSTEM_MD']; if (writeSystemMdVar) { const writeSystemMdVarLower = writeSystemMdVar.toLowerCase(); if (!['0', 'false'].includes(writeSystemMdVarLower)) { diff --git a/packages/core/src/ide/detect-ide.ts b/packages/core/src/ide/detect-ide.ts index 5cc3cb56..5eca5429 100644 --- a/packages/core/src/ide/detect-ide.ts +++ b/packages/core/src/ide/detect-ide.ts @@ -63,28 +63,28 @@ export function getIdeInfo(ide: DetectedIde): IdeInfo { export function detectIde(): DetectedIde | undefined { // Only VSCode-based integrations are currently supported. - if (process.env.TERM_PROGRAM !== 'vscode') { + if (process.env['TERM_PROGRAM'] !== 'vscode') { return undefined; } - if (process.env.__COG_BASHRC_SOURCED) { + if (process.env['__COG_BASHRC_SOURCED']) { return DetectedIde.Devin; } - if (process.env.REPLIT_USER) { + if (process.env['REPLIT_USER']) { return DetectedIde.Replit; } - if (process.env.CURSOR_TRACE_ID) { + if (process.env['CURSOR_TRACE_ID']) { return DetectedIde.Cursor; } - if (process.env.CODESPACES) { + if (process.env['CODESPACES']) { return DetectedIde.Codespaces; } - if (process.env.EDITOR_IN_CLOUD_SHELL || process.env.CLOUD_SHELL) { + if (process.env['EDITOR_IN_CLOUD_SHELL'] || process.env['CLOUD_SHELL']) { return DetectedIde.CloudShell; } - if (process.env.TERM_PRODUCT === 'Trae') { + if (process.env['TERM_PRODUCT'] === 'Trae') { return DetectedIde.Trae; } - if (process.env.FIREBASE_DEPLOY_AGENT || process.env.MONOSPACE_ENV) { + if (process.env['FIREBASE_DEPLOY_AGENT'] || process.env['MONOSPACE_ENV']) { return DetectedIde.FirebaseStudio; } return DetectedIde.VSCode; diff --git a/packages/core/src/ide/ide-installer.ts b/packages/core/src/ide/ide-installer.ts index dcfe9bee..4a771c67 100644 --- a/packages/core/src/ide/ide-installer.ts +++ b/packages/core/src/ide/ide-installer.ts @@ -59,7 +59,7 @@ async function findVsCodeCommand(): Promise { // Windows locations.push( path.join( - process.env.ProgramFiles || 'C:\\Program Files', + process.env['ProgramFiles'] || 'C:\\Program Files', 'Microsoft VS Code', 'bin', 'code.cmd', diff --git a/packages/core/src/services/loopDetectionService.ts b/packages/core/src/services/loopDetectionService.ts index f89e00da..8e94ac69 100644 --- a/packages/core/src/services/loopDetectionService.ts +++ b/packages/core/src/services/loopDetectionService.ts @@ -367,10 +367,10 @@ Please analyze the conversation history to determine the possibility that the co return false; } - if (typeof result.confidence === 'number') { - if (result.confidence > 0.9) { - if (typeof result.reasoning === 'string' && result.reasoning) { - console.warn(result.reasoning); + if (typeof result['confidence'] === 'number') { + if (result['confidence'] > 0.9) { + if (typeof result['reasoning'] === 'string' && result['reasoning']) { + console.warn(result['reasoning']); } logLoopDetected( this.config, @@ -381,7 +381,7 @@ Please analyze the conversation history to determine the possibility that the co this.llmCheckInterval = Math.round( MIN_LLM_CHECK_INTERVAL + (MAX_LLM_CHECK_INTERVAL - MIN_LLM_CHECK_INTERVAL) * - (1 - result.confidence), + (1 - result['confidence']), ); } } diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts index 60a31ae7..200a1a0d 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts @@ -87,11 +87,11 @@ export interface LogRequest { * methods might have in their runtimes. */ function determineSurface(): string { - if (process.env.SURFACE) { - return process.env.SURFACE; - } else if (process.env.GITHUB_SHA) { + if (process.env['SURFACE']) { + return process.env['SURFACE']; + } else if (process.env['GITHUB_SHA']) { return 'GitHub'; - } else if (process.env.TERM_PROGRAM === 'vscode') { + } else if (process.env['TERM_PROGRAM'] === 'vscode') { return detectIde() || DetectedIde.VSCode; } else { return 'SURFACE_NOT_SET'; diff --git a/packages/core/src/telemetry/loggers.ts b/packages/core/src/telemetry/loggers.ts index d7a81203..afc92807 100644 --- a/packages/core/src/telemetry/loggers.ts +++ b/packages/core/src/telemetry/loggers.ts @@ -98,7 +98,7 @@ export function logUserPrompt(config: Config, event: UserPromptEvent): void { }; if (shouldLogUserPrompts(config)) { - attributes.prompt = event.prompt; + attributes['prompt'] = event.prompt; } const logger = logs.getLogger(SERVICE_NAME); @@ -247,7 +247,7 @@ export function logApiResponse(config: Config, event: ApiResponseEvent): void { 'event.timestamp': new Date().toISOString(), }; if (event.response_text) { - attributes.response_text = event.response_text; + attributes['response_text'] = event.response_text; } if (event.error) { attributes['error.message'] = event.error; diff --git a/packages/core/src/telemetry/metrics.ts b/packages/core/src/telemetry/metrics.ts index 1e4509da..a0996410 100644 --- a/packages/core/src/telemetry/metrics.ts +++ b/packages/core/src/telemetry/metrics.ts @@ -197,14 +197,14 @@ export function recordFileOperationMetric( ...getCommonAttributes(config), operation, }; - if (lines !== undefined) attributes.lines = lines; - if (mimetype !== undefined) attributes.mimetype = mimetype; - if (extension !== undefined) attributes.extension = extension; + if (lines !== undefined) attributes['lines'] = lines; + if (mimetype !== undefined) attributes['mimetype'] = mimetype; + if (extension !== undefined) attributes['extension'] = extension; if (diffStat !== undefined) { - attributes.ai_added_lines = diffStat.ai_added_lines; - attributes.ai_removed_lines = diffStat.ai_removed_lines; - attributes.user_added_lines = diffStat.user_added_lines; - attributes.user_removed_lines = diffStat.user_removed_lines; + attributes['ai_added_lines'] = diffStat.ai_added_lines; + attributes['ai_removed_lines'] = diffStat.ai_removed_lines; + attributes['user_added_lines'] = diffStat.user_added_lines; + attributes['user_removed_lines'] = diffStat.user_removed_lines; } fileOperationCounter.add(1, attributes); } diff --git a/packages/core/src/tools/mcp-client.ts b/packages/core/src/tools/mcp-client.ts index 837b91fc..87d38815 100644 --- a/packages/core/src/tools/mcp-client.ts +++ b/packages/core/src/tools/mcp-client.ts @@ -448,7 +448,7 @@ export function hasValidTypes(schema: unknown): boolean { const s = schema as Record; - if (!s.type) { + if (!s['type']) { // These keywords contain an array of schemas that should be validated. // // If no top level type was given, then they must each have a type. @@ -470,9 +470,9 @@ export function hasValidTypes(schema: unknown): boolean { if (!hasSubSchema) return false; } - if (s.type === 'object' && s.properties) { - if (typeof s.properties === 'object' && s.properties !== null) { - for (const prop of Object.values(s.properties)) { + if (s['type'] === 'object' && s['properties']) { + if (typeof s['properties'] === 'object' && s['properties'] !== null) { + for (const prop of Object.values(s['properties'])) { if (!hasValidTypes(prop)) { return false; } @@ -480,8 +480,8 @@ export function hasValidTypes(schema: unknown): boolean { } } - if (s.type === 'array' && s.items) { - if (!hasValidTypes(s.items)) { + if (s['type'] === 'array' && s['items']) { + if (!hasValidTypes(s['items'])) { return false; } } @@ -1046,7 +1046,7 @@ export async function connectToMcpServer( conciseError = `Connection failed for '${mcpServerName}': ${errorMessage}`; } - if (process.env.SANDBOX) { + if (process.env['SANDBOX']) { conciseError += ` (check sandbox availability)`; } diff --git a/packages/core/src/tools/mcp-tool.ts b/packages/core/src/tools/mcp-tool.ts index 88e89c85..64952dd1 100644 --- a/packages/core/src/tools/mcp-tool.ts +++ b/packages/core/src/tools/mcp-tool.ts @@ -271,7 +271,7 @@ function transformResourceLinkBlock(block: McpResourceLinkBlock): Part { */ function transformMcpContentToParts(sdkResponse: Part[]): Part[] { const funcResponse = sdkResponse?.[0]?.functionResponse; - const mcpContent = funcResponse?.response?.content as McpContentBlock[]; + const mcpContent = funcResponse?.response?.['content'] as McpContentBlock[]; const toolName = funcResponse?.name || 'unknown tool'; if (!Array.isArray(mcpContent)) { @@ -308,8 +308,9 @@ function transformMcpContentToParts(sdkResponse: Part[]): Part[] { * @returns A formatted string representing the tool's output. */ function getStringifiedResultForDisplay(rawResponse: Part[]): string { - const mcpContent = rawResponse?.[0]?.functionResponse?.response - ?.content as McpContentBlock[]; + const mcpContent = rawResponse?.[0]?.functionResponse?.response?.[ + 'content' + ] as McpContentBlock[]; if (!Array.isArray(mcpContent)) { return '```json\n' + JSON.stringify(rawResponse, null, 2) + '\n```'; diff --git a/packages/core/src/utils/browser.ts b/packages/core/src/utils/browser.ts index a9b2b013..35ee7461 100644 --- a/packages/core/src/utils/browser.ts +++ b/packages/core/src/utils/browser.ts @@ -15,19 +15,22 @@ export function shouldAttemptBrowserLaunch(): boolean { // A list of browser names that indicate we should not attempt to open a // web browser for the user. const browserBlocklist = ['www-browser']; - const browserEnv = process.env.BROWSER; + const browserEnv = process.env['BROWSER']; if (browserEnv && browserBlocklist.includes(browserEnv)) { return false; } // Common environment variables used in CI/CD or other non-interactive shells. - if (process.env.CI || process.env.DEBIAN_FRONTEND === 'noninteractive') { + if ( + process.env['CI'] || + process.env['DEBIAN_FRONTEND'] === 'noninteractive' + ) { return false; } // The presence of SSH_CONNECTION indicates a remote session. // We should not attempt to launch a browser unless a display is explicitly available // (checked below for Linux). - const isSSH = !!process.env.SSH_CONNECTION; + const isSSH = !!process.env['SSH_CONNECTION']; // On Linux, the presence of a display server is a strong indicator of a GUI. if (process.platform === 'linux') { diff --git a/packages/core/src/utils/editCorrector.ts b/packages/core/src/utils/editCorrector.ts index faa52b51..0d857409 100644 --- a/packages/core/src/utils/editCorrector.ts +++ b/packages/core/src/utils/editCorrector.ts @@ -119,7 +119,7 @@ async function findLastEditTimestamp( const { response } = part.functionResponse; if (response && !('error' in response) && 'output' in response) { id = part.functionResponse.id; - content = response.output; + content = response['output']; } } @@ -411,10 +411,10 @@ Return ONLY the corrected target snippet in the specified JSON format with the k if ( result && - typeof result.corrected_target_snippet === 'string' && - result.corrected_target_snippet.length > 0 + typeof result['corrected_target_snippet'] === 'string' && + result['corrected_target_snippet'].length > 0 ) { - return result.corrected_target_snippet; + return result['corrected_target_snippet']; } else { return problematicSnippet; } @@ -499,10 +499,10 @@ Return ONLY the corrected string in the specified JSON format with the key 'corr if ( result && - typeof result.corrected_new_string === 'string' && - result.corrected_new_string.length > 0 + typeof result['corrected_new_string'] === 'string' && + result['corrected_new_string'].length > 0 ) { - return result.corrected_new_string; + return result['corrected_new_string']; } else { return originalNewString; } @@ -568,10 +568,10 @@ Return ONLY the corrected string in the specified JSON format with the key 'corr if ( result && - typeof result.corrected_new_string_escaping === 'string' && - result.corrected_new_string_escaping.length > 0 + typeof result['corrected_new_string_escaping'] === 'string' && + result['corrected_new_string_escaping'].length > 0 ) { - return result.corrected_new_string_escaping; + return result['corrected_new_string_escaping']; } else { return potentiallyProblematicNewString; } @@ -634,10 +634,10 @@ Return ONLY the corrected string in the specified JSON format with the key 'corr if ( result && - typeof result.corrected_string_escaping === 'string' && - result.corrected_string_escaping.length > 0 + typeof result['corrected_string_escaping'] === 'string' && + result['corrected_string_escaping'].length > 0 ) { - return result.corrected_string_escaping; + return result['corrected_string_escaping']; } else { return potentiallyProblematicString; } diff --git a/packages/core/src/utils/editor.test.ts b/packages/core/src/utils/editor.test.ts index afdc2b24..fcf50f9c 100644 --- a/packages/core/src/utils/editor.test.ts +++ b/packages/core/src/utils/editor.test.ts @@ -33,7 +33,7 @@ const originalPlatform = process.platform; describe('editor utils', () => { beforeEach(() => { vi.clearAllMocks(); - delete process.env.SANDBOX; + vi.unstubAllEnvs(); Object.defineProperty(process, 'platform', { value: originalPlatform, writable: true, @@ -42,7 +42,7 @@ describe('editor utils', () => { afterEach(() => { vi.restoreAllMocks(); - delete process.env.SANDBOX; + vi.unstubAllEnvs(); Object.defineProperty(process, 'platform', { value: originalPlatform, writable: true, @@ -461,7 +461,7 @@ describe('editor utils', () => { describe('allowEditorTypeInSandbox', () => { it('should allow vim in sandbox mode', () => { - process.env.SANDBOX = 'sandbox'; + vi.stubEnv('SANDBOX', 'sandbox'); expect(allowEditorTypeInSandbox('vim')).toBe(true); }); @@ -470,7 +470,7 @@ describe('editor utils', () => { }); it('should allow emacs in sandbox mode', () => { - process.env.SANDBOX = 'sandbox'; + vi.stubEnv('SANDBOX', 'sandbox'); expect(allowEditorTypeInSandbox('emacs')).toBe(true); }); @@ -479,7 +479,7 @@ describe('editor utils', () => { }); it('should allow neovim in sandbox mode', () => { - process.env.SANDBOX = 'sandbox'; + vi.stubEnv('SANDBOX', 'sandbox'); expect(allowEditorTypeInSandbox('neovim')).toBe(true); }); @@ -496,7 +496,7 @@ describe('editor utils', () => { ]; for (const editor of guiEditors) { it(`should not allow ${editor} in sandbox mode`, () => { - process.env.SANDBOX = 'sandbox'; + vi.stubEnv('SANDBOX', 'sandbox'); expect(allowEditorTypeInSandbox(editor)).toBe(false); }); @@ -533,25 +533,25 @@ describe('editor utils', () => { it('should return false for vscode when installed and in sandbox mode', () => { (execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/code')); - process.env.SANDBOX = 'sandbox'; + vi.stubEnv('SANDBOX', 'sandbox'); expect(isEditorAvailable('vscode')).toBe(false); }); it('should return true for vim when installed and in sandbox mode', () => { (execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/vim')); - process.env.SANDBOX = 'sandbox'; + vi.stubEnv('SANDBOX', 'sandbox'); expect(isEditorAvailable('vim')).toBe(true); }); it('should return true for emacs when installed and in sandbox mode', () => { (execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/emacs')); - process.env.SANDBOX = 'sandbox'; + vi.stubEnv('SANDBOX', 'sandbox'); expect(isEditorAvailable('emacs')).toBe(true); }); it('should return true for neovim when installed and in sandbox mode', () => { (execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/nvim')); - process.env.SANDBOX = 'sandbox'; + vi.stubEnv('SANDBOX', 'sandbox'); expect(isEditorAvailable('neovim')).toBe(true); }); }); diff --git a/packages/core/src/utils/editor.ts b/packages/core/src/utils/editor.ts index f22297df..efaef249 100644 --- a/packages/core/src/utils/editor.ts +++ b/packages/core/src/utils/editor.ts @@ -72,7 +72,7 @@ export function checkHasEditorType(editor: EditorType): boolean { } export function allowEditorTypeInSandbox(editor: EditorType): boolean { - const notUsingSandbox = !process.env.SANDBOX; + const notUsingSandbox = !process.env['SANDBOX']; if (['vscode', 'vscodium', 'windsurf', 'cursor', 'zed'].includes(editor)) { return notUsingSandbox; } diff --git a/packages/core/src/utils/memoryDiscovery.test.ts b/packages/core/src/utils/memoryDiscovery.test.ts index 6c229dbb..d8f3ccc5 100644 --- a/packages/core/src/utils/memoryDiscovery.test.ts +++ b/packages/core/src/utils/memoryDiscovery.test.ts @@ -48,8 +48,8 @@ describe('loadServerHierarchicalMemory', () => { vi.resetAllMocks(); // Set environment variables to indicate test environment - process.env.NODE_ENV = 'test'; - process.env.VITEST = 'true'; + vi.stubEnv('NODE_ENV', 'test'); + vi.stubEnv('VITEST', 'true'); projectRoot = await createEmptyDir(path.join(testRootDir, 'project')); cwd = await createEmptyDir(path.join(projectRoot, 'src')); @@ -58,6 +58,7 @@ describe('loadServerHierarchicalMemory', () => { }); afterEach(async () => { + vi.unstubAllEnvs(); // Some tests set this to a different value. setGeminiMdFilename(DEFAULT_CONTEXT_FILENAME); // Clean up the temporary directory to prevent resource leaks. diff --git a/packages/core/src/utils/memoryDiscovery.ts b/packages/core/src/utils/memoryDiscovery.ts index fcb1abdd..d3c24baf 100644 --- a/packages/core/src/utils/memoryDiscovery.ts +++ b/packages/core/src/utils/memoryDiscovery.ts @@ -57,8 +57,9 @@ async function findProjectRoot(startDir: string): Promise { (error as { code: string }).code === 'ENOENT'; // Only log unexpected errors in non-test environments - // process.env.NODE_ENV === 'test' or VITEST are common test indicators - const isTestEnv = process.env.NODE_ENV === 'test' || process.env.VITEST; + // process.env['NODE_ENV'] === 'test' or VITEST are common test indicators + const isTestEnv = + process.env['NODE_ENV'] === 'test' || process.env['VITEST']; if (!isENOENT && !isTestEnv) { if (typeof error === 'object' && error !== null && 'code' in error) { @@ -246,7 +247,8 @@ async function readGeminiMdFiles( `Successfully read and processed imports: ${filePath} (Length: ${processedResult.content.length})`, ); } catch (error: unknown) { - const isTestEnv = process.env.NODE_ENV === 'test' || process.env.VITEST; + const isTestEnv = + process.env['NODE_ENV'] === 'test' || process.env['VITEST']; if (!isTestEnv) { const message = error instanceof Error ? error.message : String(error); logger.warn( diff --git a/packages/core/src/utils/secure-browser-launcher.ts b/packages/core/src/utils/secure-browser-launcher.ts index ec8357be..c60a646d 100644 --- a/packages/core/src/utils/secure-browser-launcher.ts +++ b/packages/core/src/utils/secure-browser-launcher.ts @@ -151,20 +151,23 @@ export function shouldLaunchBrowser(): boolean { // A list of browser names that indicate we should not attempt to open a // web browser for the user. const browserBlocklist = ['www-browser']; - const browserEnv = process.env.BROWSER; + const browserEnv = process.env['BROWSER']; if (browserEnv && browserBlocklist.includes(browserEnv)) { return false; } // Common environment variables used in CI/CD or other non-interactive shells. - if (process.env.CI || process.env.DEBIAN_FRONTEND === 'noninteractive') { + if ( + process.env['CI'] || + process.env['DEBIAN_FRONTEND'] === 'noninteractive' + ) { return false; } // The presence of SSH_CONNECTION indicates a remote session. // We should not attempt to launch a browser unless a display is explicitly available // (checked below for Linux). - const isSSH = !!process.env.SSH_CONNECTION; + const isSSH = !!process.env['SSH_CONNECTION']; // On Linux, the presence of a display server is a strong indicator of a GUI. if (platform() === 'linux') { diff --git a/packages/core/src/utils/shell-utils.test.ts b/packages/core/src/utils/shell-utils.test.ts index 913ec897..18f88e11 100644 --- a/packages/core/src/utils/shell-utils.test.ts +++ b/packages/core/src/utils/shell-utils.test.ts @@ -388,7 +388,7 @@ describe('getShellConfiguration', () => { }); it('should return cmd.exe configuration by default', () => { - delete process.env.ComSpec; + delete process.env['ComSpec']; const config = getShellConfiguration(); expect(config.executable).toBe('cmd.exe'); expect(config.argsPrefix).toEqual(['/d', '/s', '/c']); @@ -397,7 +397,7 @@ describe('getShellConfiguration', () => { it('should respect ComSpec for cmd.exe', () => { const cmdPath = 'C:\\WINDOWS\\system32\\cmd.exe'; - process.env.ComSpec = cmdPath; + process.env['ComSpec'] = cmdPath; const config = getShellConfiguration(); expect(config.executable).toBe(cmdPath); expect(config.argsPrefix).toEqual(['/d', '/s', '/c']); @@ -407,7 +407,7 @@ describe('getShellConfiguration', () => { it('should return PowerShell configuration if ComSpec points to powershell.exe', () => { const psPath = 'C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'; - process.env.ComSpec = psPath; + process.env['ComSpec'] = psPath; const config = getShellConfiguration(); expect(config.executable).toBe(psPath); expect(config.argsPrefix).toEqual(['-NoProfile', '-Command']); @@ -416,7 +416,7 @@ describe('getShellConfiguration', () => { it('should return PowerShell configuration if ComSpec points to pwsh.exe', () => { const pwshPath = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe'; - process.env.ComSpec = pwshPath; + process.env['ComSpec'] = pwshPath; const config = getShellConfiguration(); expect(config.executable).toBe(pwshPath); expect(config.argsPrefix).toEqual(['-NoProfile', '-Command']); @@ -424,7 +424,7 @@ describe('getShellConfiguration', () => { }); it('should be case-insensitive when checking ComSpec', () => { - process.env.ComSpec = 'C:\\Path\\To\\POWERSHELL.EXE'; + process.env['ComSpec'] = 'C:\\Path\\To\\POWERSHELL.EXE'; const config = getShellConfiguration(); expect(config.executable).toBe('C:\\Path\\To\\POWERSHELL.EXE'); expect(config.argsPrefix).toEqual(['-NoProfile', '-Command']); diff --git a/packages/core/src/utils/shell-utils.ts b/packages/core/src/utils/shell-utils.ts index 2c818c8e..808cf893 100644 --- a/packages/core/src/utils/shell-utils.ts +++ b/packages/core/src/utils/shell-utils.ts @@ -37,7 +37,7 @@ export interface ShellConfiguration { */ export function getShellConfiguration(): ShellConfiguration { if (isWindows()) { - const comSpec = process.env.ComSpec || 'cmd.exe'; + const comSpec = process.env['ComSpec'] || 'cmd.exe'; const executable = comSpec.toLowerCase(); if ( diff --git a/packages/core/src/utils/systemEncoding.test.ts b/packages/core/src/utils/systemEncoding.test.ts index 9375db9e..afabd33f 100644 --- a/packages/core/src/utils/systemEncoding.test.ts +++ b/packages/core/src/utils/systemEncoding.test.ts @@ -39,9 +39,9 @@ describe('Shell Command Processor - Encoding Functions', () => { resetEncodingCache(); // Clear environment variables that might affect tests - delete process.env.LC_ALL; - delete process.env.LC_CTYPE; - delete process.env.LANG; + delete process.env['LC_ALL']; + delete process.env['LC_CTYPE']; + delete process.env['LANG']; }); afterEach(() => { @@ -218,21 +218,21 @@ describe('Shell Command Processor - Encoding Functions', () => { }); it('should parse locale from LC_ALL environment variable', () => { - process.env.LC_ALL = 'en_US.UTF-8'; + process.env['LC_ALL'] = 'en_US.UTF-8'; const result = getSystemEncoding(); expect(result).toBe('utf-8'); }); it('should parse locale from LC_CTYPE when LC_ALL is not set', () => { - process.env.LC_CTYPE = 'fr_FR.ISO-8859-1'; + process.env['LC_CTYPE'] = 'fr_FR.ISO-8859-1'; const result = getSystemEncoding(); expect(result).toBe('iso-8859-1'); }); it('should parse locale from LANG when LC_ALL and LC_CTYPE are not set', () => { - process.env.LANG = 'de_DE.UTF-8'; + process.env['LANG'] = 'de_DE.UTF-8'; const result = getSystemEncoding(); expect(result).toBe('utf-8'); @@ -268,16 +268,16 @@ describe('Shell Command Processor - Encoding Functions', () => { }); it('should handle locale without encoding (no dot)', () => { - process.env.LANG = 'C'; + process.env['LANG'] = 'C'; const result = getSystemEncoding(); expect(result).toBe('c'); }); it('should handle empty locale environment variables', () => { - process.env.LC_ALL = ''; - process.env.LC_CTYPE = ''; - process.env.LANG = ''; + process.env['LC_ALL'] = ''; + process.env['LC_CTYPE'] = ''; + process.env['LANG'] = ''; mockedExecSync.mockReturnValue('UTF-8'); const result = getSystemEncoding(); @@ -285,24 +285,24 @@ describe('Shell Command Processor - Encoding Functions', () => { }); it('should return locale as-is when locale format has no dot', () => { - process.env.LANG = 'invalid_format'; + process.env['LANG'] = 'invalid_format'; const result = getSystemEncoding(); expect(result).toBe('invalid_format'); }); it('should prioritize LC_ALL over other environment variables', () => { - process.env.LC_ALL = 'en_US.UTF-8'; - process.env.LC_CTYPE = 'fr_FR.ISO-8859-1'; - process.env.LANG = 'de_DE.CP1252'; + process.env['LC_ALL'] = 'en_US.UTF-8'; + process.env['LC_CTYPE'] = 'fr_FR.ISO-8859-1'; + process.env['LANG'] = 'de_DE.CP1252'; const result = getSystemEncoding(); expect(result).toBe('utf-8'); }); it('should prioritize LC_CTYPE over LANG', () => { - process.env.LC_CTYPE = 'fr_FR.ISO-8859-1'; - process.env.LANG = 'de_DE.CP1252'; + process.env['LC_CTYPE'] = 'fr_FR.ISO-8859-1'; + process.env['LANG'] = 'de_DE.CP1252'; const result = getSystemEncoding(); expect(result).toBe('iso-8859-1'); @@ -315,7 +315,7 @@ describe('Shell Command Processor - Encoding Functions', () => { }); it('should use cached system encoding on subsequent calls', () => { - process.env.LANG = 'en_US.UTF-8'; + process.env['LANG'] = 'en_US.UTF-8'; const buffer = Buffer.from('test'); // First call @@ -323,7 +323,7 @@ describe('Shell Command Processor - Encoding Functions', () => { expect(result1).toBe('utf-8'); // Change environment (should not affect cached result) - process.env.LANG = 'fr_FR.ISO-8859-1'; + process.env['LANG'] = 'fr_FR.ISO-8859-1'; // Second call should use cached value const result2 = getCachedEncodingForBuffer(buffer); @@ -435,7 +435,7 @@ describe('Shell Command Processor - Encoding Functions', () => { describe('Cross-platform behavior', () => { it('should work correctly on macOS', () => { mockedOsPlatform.mockReturnValue('darwin'); - process.env.LANG = 'en_US.UTF-8'; + process.env['LANG'] = 'en_US.UTF-8'; const result = getSystemEncoding(); expect(result).toBe('utf-8'); @@ -443,7 +443,7 @@ describe('Shell Command Processor - Encoding Functions', () => { it('should work correctly on other Unix-like systems', () => { mockedOsPlatform.mockReturnValue('freebsd'); - process.env.LANG = 'en_US.UTF-8'; + process.env['LANG'] = 'en_US.UTF-8'; const result = getSystemEncoding(); expect(result).toBe('utf-8'); @@ -451,7 +451,7 @@ describe('Shell Command Processor - Encoding Functions', () => { it('should handle unknown platforms as Unix-like', () => { mockedOsPlatform.mockReturnValue('unknown' as NodeJS.Platform); - process.env.LANG = 'en_US.UTF-8'; + process.env['LANG'] = 'en_US.UTF-8'; const result = getSystemEncoding(); expect(result).toBe('utf-8'); @@ -461,7 +461,7 @@ describe('Shell Command Processor - Encoding Functions', () => { describe('Edge cases and error handling', () => { it('should handle empty buffer gracefully', () => { mockedOsPlatform.mockReturnValue('linux'); - process.env.LANG = 'en_US.UTF-8'; + process.env['LANG'] = 'en_US.UTF-8'; const buffer = Buffer.alloc(0); const result = getCachedEncodingForBuffer(buffer); @@ -470,7 +470,7 @@ describe('Shell Command Processor - Encoding Functions', () => { it('should handle very large buffers', () => { mockedOsPlatform.mockReturnValue('linux'); - process.env.LANG = 'en_US.UTF-8'; + process.env['LANG'] = 'en_US.UTF-8'; const buffer = Buffer.alloc(1024 * 1024, 'a'); const result = getCachedEncodingForBuffer(buffer); diff --git a/packages/core/src/utils/systemEncoding.ts b/packages/core/src/utils/systemEncoding.ts index f162c223..ba632d41 100644 --- a/packages/core/src/utils/systemEncoding.ts +++ b/packages/core/src/utils/systemEncoding.ts @@ -79,7 +79,7 @@ export function getSystemEncoding(): string | null { // system encoding. However, these environment variables might not always // be set or accurate. Handle cases where none of these variables are set. const env = process.env; - let locale = env.LC_ALL || env.LC_CTYPE || env.LANG || ''; + let locale = env['LC_ALL'] || env['LC_CTYPE'] || env['LANG'] || ''; // Fallback to querying the system directly when environment variables are missing if (!locale) { diff --git a/tsconfig.json b/tsconfig.json index 5f303ddc..e46eff1d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "noImplicitReturns": true, "noImplicitThis": true, "forceConsistentCasingInFileNames": true, + "noPropertyAccessFromIndexSignature": true, "noUnusedLocals": true, "strictBindCallApply": true, "strictFunctionTypes": true,