mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-23 03:24:42 -07:00
Add extensions logging (#11261)
This commit is contained in:
@@ -199,28 +199,6 @@ export function loadExtension(
|
||||
)
|
||||
.filter((contextFilePath) => fs.existsSync(contextFilePath));
|
||||
|
||||
// IDs are created by hashing details of the installation source in order to
|
||||
// deduplicate extensions with conflicting names and also obfuscate any
|
||||
// potentially sensitive information such as private git urls, system paths,
|
||||
// or project names.
|
||||
const hash = createHash('sha256');
|
||||
const githubUrlParts =
|
||||
installMetadata &&
|
||||
(installMetadata.type === 'git' ||
|
||||
installMetadata.type === 'github-release')
|
||||
? tryParseGithubUrl(installMetadata.source)
|
||||
: null;
|
||||
if (githubUrlParts) {
|
||||
// For github repos, we use the https URI to the repo as the ID.
|
||||
hash.update(
|
||||
`https://github.com/${githubUrlParts.owner}/${githubUrlParts.repo}`,
|
||||
);
|
||||
} else {
|
||||
hash.update(installMetadata?.source ?? config.name);
|
||||
}
|
||||
|
||||
const id = hash.digest('hex');
|
||||
|
||||
return {
|
||||
name: config.name,
|
||||
version: config.version,
|
||||
@@ -230,7 +208,7 @@ export function loadExtension(
|
||||
mcpServers: config.mcpServers,
|
||||
excludeTools: config.excludeTools,
|
||||
isActive: extensionEnablementManager.isEnabled(config.name, workspaceDir),
|
||||
id,
|
||||
id: getExtensionId(config, installMetadata),
|
||||
};
|
||||
} catch (e) {
|
||||
debugLogger.error(
|
||||
@@ -384,6 +362,10 @@ async function promptForConsentInteractive(
|
||||
});
|
||||
}
|
||||
|
||||
export function hashValue(value: string): string {
|
||||
return createHash('sha256').update(value).digest('hex');
|
||||
}
|
||||
|
||||
export async function installOrUpdateExtension(
|
||||
installMetadata: ExtensionInstallMetadata,
|
||||
requestConsent: (consent: string) => Promise<boolean>,
|
||||
@@ -520,15 +502,15 @@ export async function installOrUpdateExtension(
|
||||
await fs.promises.rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (isUpdate) {
|
||||
logExtensionUpdateEvent(
|
||||
telemetryConfig,
|
||||
new ExtensionUpdateEvent(
|
||||
newExtensionConfig.name,
|
||||
hashValue(newExtensionConfig.name),
|
||||
getExtensionId(newExtensionConfig, installMetadata),
|
||||
newExtensionConfig.version,
|
||||
previousExtensionConfig.version,
|
||||
installMetadata.source,
|
||||
installMetadata.type,
|
||||
'success',
|
||||
),
|
||||
);
|
||||
@@ -536,9 +518,10 @@ export async function installOrUpdateExtension(
|
||||
logExtensionInstallEvent(
|
||||
telemetryConfig,
|
||||
new ExtensionInstallEvent(
|
||||
newExtensionConfig.name,
|
||||
hashValue(newExtensionConfig.name),
|
||||
getExtensionId(newExtensionConfig, installMetadata),
|
||||
newExtensionConfig.version,
|
||||
installMetadata.source,
|
||||
installMetadata.type,
|
||||
'success',
|
||||
),
|
||||
);
|
||||
@@ -564,14 +547,19 @@ export async function installOrUpdateExtension(
|
||||
// Ignore error, this is just for logging.
|
||||
}
|
||||
}
|
||||
const config = newExtensionConfig ?? previousExtensionConfig;
|
||||
const extensionId = config
|
||||
? getExtensionId(config, installMetadata)
|
||||
: undefined;
|
||||
if (isUpdate) {
|
||||
logExtensionUpdateEvent(
|
||||
telemetryConfig,
|
||||
new ExtensionUpdateEvent(
|
||||
newExtensionConfig?.name ?? previousExtensionConfig.name,
|
||||
hashValue(config?.name ?? ''),
|
||||
extensionId ?? '',
|
||||
newExtensionConfig?.version ?? '',
|
||||
previousExtensionConfig.version,
|
||||
installMetadata.source,
|
||||
installMetadata.type,
|
||||
'error',
|
||||
),
|
||||
);
|
||||
@@ -579,9 +567,10 @@ export async function installOrUpdateExtension(
|
||||
logExtensionInstallEvent(
|
||||
telemetryConfig,
|
||||
new ExtensionInstallEvent(
|
||||
newExtensionConfig?.name ?? '',
|
||||
hashValue(newExtensionConfig?.name ?? ''),
|
||||
extensionId ?? '',
|
||||
newExtensionConfig?.version ?? '',
|
||||
installMetadata.source,
|
||||
installMetadata.type,
|
||||
'error',
|
||||
),
|
||||
);
|
||||
@@ -707,16 +696,16 @@ export async function uninstallExtension(
|
||||
new ExtensionEnablementManager(),
|
||||
cwd,
|
||||
);
|
||||
const extensionName = installedExtensions.find(
|
||||
const extension = installedExtensions.find(
|
||||
(installed) =>
|
||||
installed.name.toLowerCase() === extensionIdentifier.toLowerCase() ||
|
||||
installed.installMetadata?.source.toLowerCase() ===
|
||||
extensionIdentifier.toLowerCase(),
|
||||
)?.name;
|
||||
if (!extensionName) {
|
||||
);
|
||||
if (!extension) {
|
||||
throw new Error(`Extension not found.`);
|
||||
}
|
||||
const storage = new ExtensionStorage(extensionName);
|
||||
const storage = new ExtensionStorage(extension.name);
|
||||
|
||||
await fs.promises.rm(storage.getExtensionDir(), {
|
||||
recursive: true,
|
||||
@@ -727,13 +716,17 @@ export async function uninstallExtension(
|
||||
// uninstalls related to updates.
|
||||
if (isUpdate) return;
|
||||
|
||||
const manager = new ExtensionEnablementManager([extensionName]);
|
||||
manager.remove(extensionName);
|
||||
const manager = new ExtensionEnablementManager([extension.name]);
|
||||
manager.remove(extension.name);
|
||||
|
||||
const telemetryConfig = getTelemetryConfig(cwd);
|
||||
logExtensionUninstall(
|
||||
telemetryConfig,
|
||||
new ExtensionUninstallEvent(extensionName, 'success'),
|
||||
new ExtensionUninstallEvent(
|
||||
hashValue(extension.name),
|
||||
extension.id,
|
||||
'success',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -747,6 +740,7 @@ export function toOutputString(
|
||||
|
||||
const status = workspaceEnabled ? chalk.green('✓') : chalk.red('✗');
|
||||
let output = `${status} ${extension.name} (${extension.version})`;
|
||||
output += `\n ID: ${extension.id}`;
|
||||
output += `\n Path: ${extension.path}`;
|
||||
if (extension.installMetadata) {
|
||||
output += `\n Source: ${extension.installMetadata.source} (Type: ${extension.installMetadata.type})`;
|
||||
@@ -797,7 +791,10 @@ export function disableExtension(
|
||||
|
||||
const scopePath = scope === SettingScope.Workspace ? cwd : os.homedir();
|
||||
extensionEnablementManager.disable(name, true, scopePath);
|
||||
logExtensionDisable(config, new ExtensionDisableEvent(name, scope));
|
||||
logExtensionDisable(
|
||||
config,
|
||||
new ExtensionDisableEvent(hashValue(name), extension.id, scope),
|
||||
);
|
||||
}
|
||||
|
||||
export function enableExtension(
|
||||
@@ -816,5 +813,32 @@ export function enableExtension(
|
||||
const scopePath = scope === SettingScope.Workspace ? cwd : os.homedir();
|
||||
extensionEnablementManager.enable(name, true, scopePath);
|
||||
const config = getTelemetryConfig(cwd);
|
||||
logExtensionEnable(config, new ExtensionEnableEvent(name, scope));
|
||||
logExtensionEnable(
|
||||
config,
|
||||
new ExtensionEnableEvent(hashValue(name), extension.id, scope),
|
||||
);
|
||||
}
|
||||
|
||||
function getExtensionId(
|
||||
config: ExtensionConfig,
|
||||
installMetadata?: ExtensionInstallMetadata,
|
||||
): string {
|
||||
// IDs are created by hashing details of the installation source in order to
|
||||
// deduplicate extensions with conflicting names and also obfuscate any
|
||||
// potentially sensitive information such as private git urls, system paths,
|
||||
// or project names.
|
||||
let idValue = config.name;
|
||||
const githubUrlParts =
|
||||
installMetadata &&
|
||||
(installMetadata.type === 'git' ||
|
||||
installMetadata.type === 'github-release')
|
||||
? tryParseGithubUrl(installMetadata.source)
|
||||
: null;
|
||||
if (githubUrlParts) {
|
||||
// For github repos, we use the https URI to the repo as the ID.
|
||||
idValue = `https://github.com/${githubUrlParts.owner}/${githubUrlParts.repo}`;
|
||||
} else {
|
||||
idValue = installMetadata?.source ?? config.name;
|
||||
}
|
||||
return hashValue(idValue);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user