mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
Add extension settings info to /extensions list (#14905)
This commit is contained in:
@@ -44,6 +44,7 @@ import {
|
||||
type GeminiCLIExtension,
|
||||
type HookDefinition,
|
||||
type HookEventName,
|
||||
type ResolvedExtensionSetting,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { maybeRequestConsentOrFail } from './extensions/consent.js';
|
||||
import { resolveEnvVarsInObject } from '../utils/envVarResolver.js';
|
||||
@@ -509,6 +510,24 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
);
|
||||
config = resolveEnvVarsInObject(config, customEnv);
|
||||
|
||||
const resolvedSettings: ResolvedExtensionSetting[] = [];
|
||||
if (config.settings) {
|
||||
for (const setting of config.settings) {
|
||||
const value = customEnv[setting.envVar];
|
||||
resolvedSettings.push({
|
||||
name: setting.name,
|
||||
envVar: setting.envVar,
|
||||
value:
|
||||
value === undefined
|
||||
? '[not set]'
|
||||
: setting.sensitive
|
||||
? '***'
|
||||
: value,
|
||||
sensitive: setting.sensitive ?? false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (config.mcpServers) {
|
||||
config.mcpServers = Object.fromEntries(
|
||||
Object.entries(config.mcpServers).map(([key, value]) => [
|
||||
@@ -532,7 +551,7 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
});
|
||||
}
|
||||
|
||||
const extension = {
|
||||
const extension: GeminiCLIExtension = {
|
||||
name: config.name,
|
||||
version: config.version,
|
||||
path: effectiveExtensionPath,
|
||||
@@ -546,6 +565,8 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
this.workspaceDir,
|
||||
),
|
||||
id: getExtensionId(config, installMetadata),
|
||||
settings: config.settings,
|
||||
resolvedSettings,
|
||||
};
|
||||
this.loadedExtensions = [...this.loadedExtensions, extension];
|
||||
|
||||
@@ -700,6 +721,13 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
output += `\n ${tool}`;
|
||||
});
|
||||
}
|
||||
const resolvedSettings = extension.resolvedSettings;
|
||||
if (resolvedSettings && resolvedSettings.length > 0) {
|
||||
output += `\n Settings:`;
|
||||
resolvedSettings.forEach((setting) => {
|
||||
output += `\n ${setting.name}: ${setting.value}`;
|
||||
});
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,4 +125,33 @@ describe('<ExtensionsList />', () => {
|
||||
unmount();
|
||||
});
|
||||
}
|
||||
|
||||
it('should render resolved settings for an extension', () => {
|
||||
mockUIState(new Map());
|
||||
const extensionWithSettings = {
|
||||
...mockExtensions[0],
|
||||
resolvedSettings: [
|
||||
{
|
||||
name: 'sensitiveApiKey',
|
||||
value: '***',
|
||||
envVar: 'API_KEY',
|
||||
sensitive: true,
|
||||
},
|
||||
{
|
||||
name: 'maxTokens',
|
||||
value: '1000',
|
||||
envVar: 'MAX_TOKENS',
|
||||
sensitive: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
const { lastFrame, unmount } = render(
|
||||
<ExtensionsList extensions={[extensionWithSettings]} />,
|
||||
);
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('settings:');
|
||||
expect(output).toContain('- sensitiveApiKey: ***');
|
||||
expect(output).toContain('- maxTokens: 1000');
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ export const ExtensionsList: React.FC<ExtensionsList> = ({ extensions }) => {
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginTop={1} marginBottom={1}>
|
||||
<Text>Installed extensions:</Text>
|
||||
<Text>Installed extensions: </Text>
|
||||
<Box flexDirection="column" paddingLeft={2}>
|
||||
{extensions.map((ext) => {
|
||||
const state = extensionsUpdateState.get(ext.name);
|
||||
@@ -59,12 +59,22 @@ export const ExtensionsList: React.FC<ExtensionsList> = ({ extensions }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Box key={ext.name}>
|
||||
<Box key={ext.name} flexDirection="column" marginBottom={1}>
|
||||
<Text>
|
||||
<Text color="cyan">{`${ext.name} (v${ext.version})`}</Text>
|
||||
<Text color={activeColor}>{` - ${activeString}`}</Text>
|
||||
{<Text color={stateColor}>{` (${stateText})`}</Text>}
|
||||
</Text>
|
||||
{ext.resolvedSettings && ext.resolvedSettings.length > 0 && (
|
||||
<Box flexDirection="column" paddingLeft={2}>
|
||||
<Text>settings:</Text>
|
||||
{ext.resolvedSettings.map((setting) => (
|
||||
<Text key={setting.name}>
|
||||
- {setting.name}: {setting.value}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -134,6 +134,20 @@ export interface CodebaseInvestigatorSettings {
|
||||
model?: string;
|
||||
}
|
||||
|
||||
export interface ExtensionSetting {
|
||||
name: string;
|
||||
description: string;
|
||||
envVar: string;
|
||||
sensitive?: boolean;
|
||||
}
|
||||
|
||||
export interface ResolvedExtensionSetting {
|
||||
name: string;
|
||||
envVar: string;
|
||||
value: string;
|
||||
sensitive: boolean;
|
||||
}
|
||||
|
||||
export interface IntrospectionAgentSettings {
|
||||
enabled?: boolean;
|
||||
}
|
||||
@@ -155,6 +169,8 @@ export interface GeminiCLIExtension {
|
||||
excludeTools?: string[];
|
||||
id: string;
|
||||
hooks?: { [K in HookEventName]?: HookDefinition[] };
|
||||
settings?: ExtensionSetting[];
|
||||
resolvedSettings?: ResolvedExtensionSetting[];
|
||||
}
|
||||
|
||||
export interface ExtensionInstallMetadata {
|
||||
|
||||
Reference in New Issue
Block a user