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