mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 18:11:02 -07:00
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
144 lines
4.3 KiB
TypeScript
144 lines
4.3 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
import type { LoadedSettings } from '../../config/settings.js';
|
|
import { FolderTrustChoice } from '../components/FolderTrustDialog.js';
|
|
import {
|
|
loadTrustedFolders,
|
|
TrustLevel,
|
|
isWorkspaceTrusted,
|
|
} from '../../config/trustedFolders.js';
|
|
import * as process from 'node:process';
|
|
import { type HistoryItemWithoutId, MessageType } from '../types.js';
|
|
import {
|
|
coreEvents,
|
|
ExitCodes,
|
|
isHeadlessMode,
|
|
FolderTrustDiscoveryService,
|
|
type FolderDiscoveryResults,
|
|
} from '@google/gemini-cli-core';
|
|
import { runExitCleanup } from '../../utils/cleanup.js';
|
|
|
|
export const useFolderTrust = (
|
|
settings: LoadedSettings,
|
|
onTrustChange: (isTrusted: boolean | undefined) => void,
|
|
addItem: (item: HistoryItemWithoutId, timestamp: number) => number,
|
|
) => {
|
|
const [isTrusted, setIsTrusted] = useState<boolean | undefined>(undefined);
|
|
const [isFolderTrustDialogOpen, setIsFolderTrustDialogOpen] = useState(false);
|
|
const [discoveryResults, setDiscoveryResults] =
|
|
useState<FolderDiscoveryResults | null>(null);
|
|
const [isRestarting, setIsRestarting] = useState(false);
|
|
const startupMessageSent = useRef(false);
|
|
|
|
const folderTrust = settings.merged.security.folderTrust.enabled ?? true;
|
|
|
|
useEffect(() => {
|
|
let isMounted = true;
|
|
const { isTrusted: trusted } = isWorkspaceTrusted(settings.merged);
|
|
|
|
if (trusted === undefined || trusted === false) {
|
|
void FolderTrustDiscoveryService.discover(process.cwd())
|
|
.then((results) => {
|
|
if (isMounted) {
|
|
setDiscoveryResults(results);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// Silently ignore discovery errors as they are handled within the service
|
|
// and reported via results.discoveryErrors if successful.
|
|
});
|
|
}
|
|
|
|
const showUntrustedMessage = () => {
|
|
if (trusted === false && !startupMessageSent.current) {
|
|
addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: 'This folder is untrusted, project settings, hooks, MCPs, and GEMINI.md files will not be applied for this folder.\nUse the `/permissions` command to change the trust level.',
|
|
},
|
|
Date.now(),
|
|
);
|
|
startupMessageSent.current = true;
|
|
}
|
|
};
|
|
|
|
if (isHeadlessMode()) {
|
|
if (isMounted) {
|
|
setIsTrusted(trusted);
|
|
setIsFolderTrustDialogOpen(false);
|
|
onTrustChange(true);
|
|
showUntrustedMessage();
|
|
}
|
|
} else if (isMounted) {
|
|
setIsTrusted(trusted);
|
|
setIsFolderTrustDialogOpen(trusted === undefined);
|
|
onTrustChange(trusted);
|
|
showUntrustedMessage();
|
|
}
|
|
|
|
return () => {
|
|
isMounted = false;
|
|
};
|
|
}, [folderTrust, onTrustChange, settings.merged, addItem]);
|
|
|
|
const handleFolderTrustSelect = useCallback(
|
|
async (choice: FolderTrustChoice) => {
|
|
const trustLevelMap: Record<FolderTrustChoice, TrustLevel> = {
|
|
[FolderTrustChoice.TRUST_FOLDER]: TrustLevel.TRUST_FOLDER,
|
|
[FolderTrustChoice.TRUST_PARENT]: TrustLevel.TRUST_PARENT,
|
|
[FolderTrustChoice.DO_NOT_TRUST]: TrustLevel.DO_NOT_TRUST,
|
|
};
|
|
|
|
const trustLevel = trustLevelMap[choice];
|
|
if (!trustLevel) return;
|
|
|
|
const cwd = process.cwd();
|
|
const trustedFolders = loadTrustedFolders();
|
|
|
|
try {
|
|
await trustedFolders.setValue(cwd, trustLevel);
|
|
} catch (_e) {
|
|
coreEvents.emitFeedback(
|
|
'error',
|
|
'Failed to save trust settings. Exiting Gemini CLI.',
|
|
);
|
|
setTimeout(async () => {
|
|
await runExitCleanup();
|
|
process.exit(ExitCodes.FATAL_CONFIG_ERROR);
|
|
}, 100);
|
|
return;
|
|
}
|
|
|
|
const currentIsTrusted =
|
|
trustLevel === TrustLevel.TRUST_FOLDER ||
|
|
trustLevel === TrustLevel.TRUST_PARENT;
|
|
|
|
onTrustChange(currentIsTrusted);
|
|
setIsTrusted(currentIsTrusted);
|
|
|
|
const wasTrusted = isTrusted ?? false;
|
|
|
|
if (wasTrusted !== currentIsTrusted) {
|
|
setIsRestarting(true);
|
|
setIsFolderTrustDialogOpen(true);
|
|
} else {
|
|
setIsFolderTrustDialogOpen(false);
|
|
}
|
|
},
|
|
[onTrustChange, isTrusted],
|
|
);
|
|
|
|
return {
|
|
isTrusted,
|
|
isFolderTrustDialogOpen,
|
|
discoveryResults,
|
|
handleFolderTrustSelect,
|
|
isRestarting,
|
|
};
|
|
};
|