mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-11 06:31:01 -07:00
Add an experimental setting for extension config (#16506)
This commit is contained in:
@@ -830,6 +830,11 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.extensionConfig`** (boolean):
|
||||
- **Description:** Enable requesting and fetching of extension settings.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.extensionReloading`** (boolean):
|
||||
- **Description:** Enables extension loading/unloading within the CLI session.
|
||||
- **Default:** `false`
|
||||
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
getScopedEnvContents,
|
||||
} from '../../config/extensions/extensionSettings.js';
|
||||
import { getExtensionAndManager, getExtensionManager } from './utils.js';
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
import { loadSettings } from '../../config/settings.js';
|
||||
import { debugLogger, coreEvents } from '@google/gemini-cli-core';
|
||||
import { exitCli } from '../utils.js';
|
||||
import prompts from 'prompts';
|
||||
import type { ExtensionConfig } from '../../config/extension.js';
|
||||
@@ -43,6 +44,16 @@ export const configureCommand: CommandModule<object, ConfigureArgs> = {
|
||||
}),
|
||||
handler: async (args) => {
|
||||
const { name, setting, scope } = args;
|
||||
const settings = loadSettings(process.cwd()).merged;
|
||||
|
||||
if (!(settings.experimental?.extensionConfig ?? true)) {
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
'Extension configuration is currently disabled. Enable it by setting "experimental.extensionConfig" to true.',
|
||||
);
|
||||
await exitCli();
|
||||
return;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
if (name.includes('/') || name.includes('\\') || name.includes('..')) {
|
||||
|
||||
@@ -109,6 +109,9 @@ describe('ExtensionManager Settings Scope', () => {
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
experimental: {
|
||||
extensionConfig: true,
|
||||
},
|
||||
} as Settings,
|
||||
});
|
||||
|
||||
@@ -148,6 +151,9 @@ describe('ExtensionManager Settings Scope', () => {
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
experimental: {
|
||||
extensionConfig: true,
|
||||
},
|
||||
} as Settings,
|
||||
});
|
||||
|
||||
@@ -185,6 +191,9 @@ describe('ExtensionManager Settings Scope', () => {
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
experimental: {
|
||||
extensionConfig: true,
|
||||
},
|
||||
} as Settings,
|
||||
});
|
||||
|
||||
|
||||
@@ -287,7 +287,10 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
}
|
||||
|
||||
await fs.promises.mkdir(destinationPath, { recursive: true });
|
||||
if (this.requestSetting) {
|
||||
if (
|
||||
this.requestSetting &&
|
||||
(this.settings.experimental?.extensionConfig ?? false)
|
||||
) {
|
||||
if (isUpdate) {
|
||||
await maybePromptForSettings(
|
||||
newExtensionConfig,
|
||||
@@ -305,11 +308,14 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
}
|
||||
}
|
||||
|
||||
const missingSettings = await getMissingSettings(
|
||||
newExtensionConfig,
|
||||
extensionId,
|
||||
this.workspaceDir,
|
||||
);
|
||||
const missingSettings =
|
||||
(this.settings.experimental?.extensionConfig ?? false)
|
||||
? await getMissingSettings(
|
||||
newExtensionConfig,
|
||||
extensionId,
|
||||
this.workspaceDir,
|
||||
)
|
||||
: [];
|
||||
if (missingSettings.length > 0) {
|
||||
const message = `Extension "${newExtensionConfig.name}" has missing settings: ${missingSettings
|
||||
.map((s) => s.name)
|
||||
@@ -526,23 +532,31 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
|
||||
const extensionId = getExtensionId(config, installMetadata);
|
||||
|
||||
const userSettings = await getScopedEnvContents(
|
||||
config,
|
||||
extensionId,
|
||||
ExtensionSettingScope.USER,
|
||||
);
|
||||
const workspaceSettings = await getScopedEnvContents(
|
||||
config,
|
||||
extensionId,
|
||||
ExtensionSettingScope.WORKSPACE,
|
||||
this.workspaceDir,
|
||||
);
|
||||
let userSettings: Record<string, string> = {};
|
||||
let workspaceSettings: Record<string, string> = {};
|
||||
|
||||
if (this.settings.experimental?.extensionConfig ?? false) {
|
||||
userSettings = await getScopedEnvContents(
|
||||
config,
|
||||
extensionId,
|
||||
ExtensionSettingScope.USER,
|
||||
);
|
||||
workspaceSettings = await getScopedEnvContents(
|
||||
config,
|
||||
extensionId,
|
||||
ExtensionSettingScope.WORKSPACE,
|
||||
this.workspaceDir,
|
||||
);
|
||||
}
|
||||
|
||||
const customEnv = { ...userSettings, ...workspaceSettings };
|
||||
config = resolveEnvVarsInObject(config, customEnv);
|
||||
|
||||
const resolvedSettings: ResolvedExtensionSetting[] = [];
|
||||
if (config.settings) {
|
||||
if (
|
||||
config.settings &&
|
||||
(this.settings.experimental?.extensionConfig ?? false)
|
||||
) {
|
||||
for (const setting of config.settings) {
|
||||
const value = customEnv[setting.envVar];
|
||||
let scope: 'user' | 'workspace' | undefined;
|
||||
|
||||
@@ -200,11 +200,13 @@ describe('extension tests', () => {
|
||||
source: undefined,
|
||||
});
|
||||
vi.spyOn(process, 'cwd').mockReturnValue(tempWorkspaceDir);
|
||||
const settings = loadSettings(tempWorkspaceDir).merged;
|
||||
(settings.experimental ??= {}).extensionConfig = true;
|
||||
extensionManager = new ExtensionManager({
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
requestConsent: mockRequestConsent,
|
||||
requestSetting: mockPromptForSettings,
|
||||
settings: loadSettings(tempWorkspaceDir).merged,
|
||||
settings,
|
||||
});
|
||||
resetTrustedFoldersForTesting();
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ import * as fs from 'node:fs';
|
||||
import { getMissingSettings } from './extensionSettings.js';
|
||||
import type { ExtensionConfig } from '../extension.js';
|
||||
import { ExtensionStorage } from './storage.js';
|
||||
import type { Settings } from '../settings.js';
|
||||
import {
|
||||
KeychainTokenStorage,
|
||||
debugLogger,
|
||||
@@ -245,8 +246,13 @@ describe('extensionUpdates', () => {
|
||||
|
||||
const manager = new ExtensionManager({
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
settings: { telemetry: {} } as any,
|
||||
|
||||
settings: {
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
experimental: { extensionConfig: true },
|
||||
} as unknown as Settings,
|
||||
requestConsent: vi.fn().mockResolvedValue(true),
|
||||
requestSetting: null, // Simulate non-interactive
|
||||
});
|
||||
|
||||
@@ -1413,6 +1413,15 @@ const SETTINGS_SCHEMA = {
|
||||
description: 'Enable extension management features.',
|
||||
showInDialog: false,
|
||||
},
|
||||
extensionConfig: {
|
||||
type: 'boolean',
|
||||
label: 'Extension Configuration',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description: 'Enable requesting and fetching of extension settings.',
|
||||
showInDialog: false,
|
||||
},
|
||||
extensionReloading: {
|
||||
type: 'boolean',
|
||||
label: 'Extension Reloading',
|
||||
|
||||
@@ -1393,6 +1393,13 @@
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"extensionConfig": {
|
||||
"title": "Extension Configuration",
|
||||
"description": "Enable requesting and fetching of extension settings.",
|
||||
"markdownDescription": "Enable requesting and fetching of extension settings.\n\n- Category: `Experimental`\n- Requires restart: `yes`\n- Default: `false`",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"extensionReloading": {
|
||||
"title": "Extension Reloading",
|
||||
"description": "Enables extension loading/unloading within the CLI session.",
|
||||
|
||||
Reference in New Issue
Block a user