Add privacy notice slash command (#2059)
This commit is contained in:
parent
4fbffdf617
commit
a2a46c7c67
|
@ -71,6 +71,7 @@ import { checkForUpdates } from './utils/updateCheck.js';
|
|||
import ansiEscapes from 'ansi-escapes';
|
||||
import { OverflowProvider } from './contexts/OverflowContext.js';
|
||||
import { ShowMoreLines } from './components/ShowMoreLines.js';
|
||||
import { PrivacyNotice } from './privacy/PrivacyNotice.js';
|
||||
|
||||
const CTRL_EXIT_PROMPT_DURATION_MS = 1000;
|
||||
|
||||
|
@ -130,6 +131,11 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
|
|||
const [ctrlDPressedOnce, setCtrlDPressedOnce] = useState(false);
|
||||
const ctrlDTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const [constrainHeight, setConstrainHeight] = useState<boolean>(true);
|
||||
const [showPrivacyNotice, setShowPrivacyNotice] = useState<boolean>(false);
|
||||
|
||||
const openPrivacyNotice = useCallback(() => {
|
||||
setShowPrivacyNotice(true);
|
||||
}, []);
|
||||
|
||||
const errorCount = useMemo(
|
||||
() => consoleMessages.filter((msg) => msg.type === 'error').length,
|
||||
|
@ -277,6 +283,7 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
|
|||
toggleCorgiMode,
|
||||
showToolDescriptions,
|
||||
setQuittingMessages,
|
||||
openPrivacyNotice,
|
||||
);
|
||||
const pendingHistoryItems = [...pendingSlashCommandHistoryItems];
|
||||
|
||||
|
@ -712,6 +719,11 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
|
|||
onExit={exitEditorDialog}
|
||||
/>
|
||||
</Box>
|
||||
) : showPrivacyNotice ? (
|
||||
<PrivacyNotice
|
||||
onExit={() => setShowPrivacyNotice(false)}
|
||||
config={config}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<LoadingIndicator
|
||||
|
|
|
@ -76,6 +76,7 @@ export const useSlashCommandProcessor = (
|
|||
toggleCorgiMode: () => void,
|
||||
showToolDescriptions: boolean = false,
|
||||
setQuittingMessages: (message: HistoryItem[]) => void,
|
||||
openPrivacyNotice: () => void,
|
||||
) => {
|
||||
const session = useSessionStats();
|
||||
const gitService = useMemo(() => {
|
||||
|
@ -254,6 +255,13 @@ export const useSlashCommandProcessor = (
|
|||
openEditorDialog();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'privacy',
|
||||
description: 'display the privacy notice',
|
||||
action: (_mainCommand, _subCommand, _args) => {
|
||||
openPrivacyNotice();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'stats',
|
||||
altName: 'usage',
|
||||
|
@ -1022,6 +1030,7 @@ export const useSlashCommandProcessor = (
|
|||
setQuittingMessages,
|
||||
pendingCompressionItemRef,
|
||||
setPendingCompressionItem,
|
||||
openPrivacyNotice,
|
||||
]);
|
||||
|
||||
const handleSlashCommand = useCallback(
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { GaxiosError } from 'gaxios';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Config, CodeAssistServer, UserTierId } from '@google/gemini-cli-core';
|
||||
|
||||
export interface PrivacyState {
|
||||
isLoading: boolean;
|
||||
error?: string;
|
||||
isFreeTier?: boolean;
|
||||
dataCollectionOptIn?: boolean;
|
||||
}
|
||||
|
||||
export const usePrivacySettings = (config: Config) => {
|
||||
const [privacyState, setPrivacyState] = useState<PrivacyState>({
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchInitialState = async () => {
|
||||
setPrivacyState({
|
||||
isLoading: true,
|
||||
});
|
||||
try {
|
||||
const server = getCodeAssistServer(config);
|
||||
const tier = await getTier(server);
|
||||
if (tier !== UserTierId.FREE) {
|
||||
// We don't need to fetch opt-out info since non-free tier
|
||||
// data gathering is already worked out some other way.
|
||||
setPrivacyState({
|
||||
isLoading: false,
|
||||
isFreeTier: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const optIn = await getRemoteDataCollectionOptIn(server);
|
||||
setPrivacyState({
|
||||
isLoading: false,
|
||||
isFreeTier: true,
|
||||
dataCollectionOptIn: optIn,
|
||||
});
|
||||
} catch (e) {
|
||||
setPrivacyState({
|
||||
isLoading: false,
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
});
|
||||
}
|
||||
};
|
||||
fetchInitialState();
|
||||
}, [config]);
|
||||
|
||||
const updateDataCollectionOptIn = useCallback(
|
||||
async (optIn: boolean) => {
|
||||
try {
|
||||
const server = getCodeAssistServer(config);
|
||||
const updatedOptIn = await setRemoteDataCollectionOptIn(server, optIn);
|
||||
setPrivacyState({
|
||||
isLoading: false,
|
||||
isFreeTier: true,
|
||||
dataCollectionOptIn: updatedOptIn,
|
||||
});
|
||||
} catch (e) {
|
||||
setPrivacyState({
|
||||
isLoading: false,
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
});
|
||||
}
|
||||
},
|
||||
[config],
|
||||
);
|
||||
|
||||
return {
|
||||
privacyState,
|
||||
updateDataCollectionOptIn,
|
||||
};
|
||||
};
|
||||
|
||||
function getCodeAssistServer(config: Config): CodeAssistServer {
|
||||
const server = config.getGeminiClient().getContentGenerator();
|
||||
// Neither of these cases should ever happen.
|
||||
if (!(server instanceof CodeAssistServer)) {
|
||||
throw new Error('Oauth not being used');
|
||||
} else if (!server.projectId) {
|
||||
throw new Error('Oauth not being used');
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
async function getTier(server: CodeAssistServer): Promise<UserTierId> {
|
||||
const loadRes = await server.loadCodeAssist({
|
||||
cloudaicompanionProject: server.projectId,
|
||||
metadata: {
|
||||
ideType: 'IDE_UNSPECIFIED',
|
||||
platform: 'PLATFORM_UNSPECIFIED',
|
||||
pluginType: 'GEMINI',
|
||||
duetProject: server.projectId,
|
||||
},
|
||||
});
|
||||
if (!loadRes.currentTier) {
|
||||
throw new Error('User does not have a current tier');
|
||||
}
|
||||
return loadRes.currentTier.id;
|
||||
}
|
||||
|
||||
async function getRemoteDataCollectionOptIn(
|
||||
server: CodeAssistServer,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const resp = await server.getCodeAssistGlobalUserSetting();
|
||||
return resp.freeTierDataCollectionOptin;
|
||||
} catch (e) {
|
||||
if (e instanceof GaxiosError) {
|
||||
if (e.response?.status === 404) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function setRemoteDataCollectionOptIn(
|
||||
server: CodeAssistServer,
|
||||
optIn: boolean,
|
||||
): Promise<boolean> {
|
||||
const resp = await server.setCodeAssistGlobalUserSetting({
|
||||
cloudaicompanionProject: server.projectId,
|
||||
freeTierDataCollectionOptin: optIn,
|
||||
});
|
||||
return resp.freeTierDataCollectionOptin;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Box, Newline, Text } from 'ink';
|
||||
import { RadioButtonSelect } from '../components/shared/RadioButtonSelect.js';
|
||||
import { usePrivacySettings } from '../hooks/usePrivacySettings.js';
|
||||
import { CloudPaidPrivacyNotice } from './CloudPaidPrivacyNotice.js';
|
||||
import { Config } from '@google/gemini-cli-core';
|
||||
import { Colors } from '../colors.js';
|
||||
|
||||
interface CloudFreePrivacyNoticeProps {
|
||||
config: Config;
|
||||
onExit: () => void;
|
||||
}
|
||||
|
||||
export const CloudFreePrivacyNotice = ({
|
||||
config,
|
||||
onExit,
|
||||
}: CloudFreePrivacyNoticeProps) => {
|
||||
const { privacyState, updateDataCollectionOptIn } =
|
||||
usePrivacySettings(config);
|
||||
|
||||
if (privacyState.isLoading) {
|
||||
return <Text color={Colors.Gray}>Loading...</Text>;
|
||||
}
|
||||
|
||||
if (privacyState.error) {
|
||||
return (
|
||||
<Text color={Colors.AccentRed}>
|
||||
Error loading Opt-in settings: {privacyState.error}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (privacyState.isFreeTier === false) {
|
||||
return <CloudPaidPrivacyNotice onExit={onExit} />;
|
||||
}
|
||||
|
||||
const items = [
|
||||
{ label: 'Yes', value: true },
|
||||
{ label: 'No', value: false },
|
||||
];
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginY={1}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
Gemini Code Assist for Individuals Privacy Notice
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
This notice and our Privacy Policy
|
||||
<Text color={Colors.AccentBlue}>[1]</Text> describe how Gemini Code
|
||||
Assist handles your data. Please read them carefully.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
When you use Gemini Code Assist for individuals with Gemini CLI, Google
|
||||
collects your prompts, related code, generated output, code edits,
|
||||
related feature usage information, and your feedback to provide,
|
||||
improve, and develop Google products and services and machine learning
|
||||
technologies.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
To help with quality and improve our products (such as generative
|
||||
machine-learning models), human reviewers may read, annotate, and
|
||||
process the data collected above. We take steps to protect your privacy
|
||||
as part of this process. This includes disconnecting the data from your
|
||||
Google Account before reviewers see or annotate it, and storing those
|
||||
disconnected copies for up to 18 months. Please don't submit
|
||||
confidential information or any data you wouldn't want a reviewer
|
||||
to see or Google to use to improve our products, services and
|
||||
machine-learning technologies.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Box flexDirection="column">
|
||||
<Text>
|
||||
Allow Google to use this data to develop and improve our products?
|
||||
</Text>
|
||||
<RadioButtonSelect
|
||||
items={items}
|
||||
initialIndex={privacyState.dataCollectionOptIn ? 0 : 1}
|
||||
onSelect={(value) => {
|
||||
updateDataCollectionOptIn(value);
|
||||
// Only exit if there was no error.
|
||||
if (!privacyState.error) {
|
||||
onExit();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||
https://policies.google.com/privacy
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color={Colors.Gray}>Press Enter to choose an option and exit.</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Box, Newline, Text, useInput } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
|
||||
interface CloudPaidPrivacyNoticeProps {
|
||||
onExit: () => void;
|
||||
}
|
||||
|
||||
export const CloudPaidPrivacyNotice = ({
|
||||
onExit,
|
||||
}: CloudPaidPrivacyNoticeProps) => {
|
||||
useInput((input, key) => {
|
||||
if (key.escape) {
|
||||
onExit();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
Vertex AI Notice
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
Service Specific Terms<Text color={Colors.AccentBlue}>[1]</Text> are
|
||||
incorporated into the agreement under which Google has agreed to provide
|
||||
Google Cloud Platform<Text color={Colors.AccentGreen}>[2]</Text> to
|
||||
Customer (the “Agreement”). If the Agreement authorizes the resale or
|
||||
supply of Google Cloud Platform under a Google Cloud partner or reseller
|
||||
program, then except for in the section entitled “Partner-Specific
|
||||
Terms”, all references to Customer in the Service Specific Terms mean
|
||||
Partner or Reseller (as applicable), and all references to Customer Data
|
||||
in the Service Specific Terms mean Partner Data. Capitalized terms used
|
||||
but not defined in the Service Specific Terms have the meaning given to
|
||||
them in the Agreement.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||
https://cloud.google.com/terms/service-terms
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={Colors.AccentGreen}>[2]</Text>{' '}
|
||||
https://cloud.google.com/terms/services
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color={Colors.Gray}>Press Esc to exit.</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Box, Newline, Text, useInput } from 'ink';
|
||||
import { Colors } from '../colors.js';
|
||||
|
||||
interface GeminiPrivacyNoticeProps {
|
||||
onExit: () => void;
|
||||
}
|
||||
|
||||
export const GeminiPrivacyNotice = ({ onExit }: GeminiPrivacyNoticeProps) => {
|
||||
useInput((input, key) => {
|
||||
if (key.escape) {
|
||||
onExit();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
Gemini API Key Notice
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
By using the Gemini API<Text color={Colors.AccentBlue}>[1]</Text>,
|
||||
Google AI Studio
|
||||
<Text color={Colors.AccentRed}>[2]</Text>, and the other Google
|
||||
developer services that reference these terms (collectively, the
|
||||
"APIs" or "Services"), you are agreeing to Google
|
||||
APIs Terms of Service (the "API Terms")
|
||||
<Text color={Colors.AccentGreen}>[3]</Text>, and the Gemini API
|
||||
Additional Terms of Service (the "Additional Terms")
|
||||
<Text color={Colors.AccentPurple}>[4]</Text>.
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text>
|
||||
<Text color={Colors.AccentBlue}>[1]</Text>{' '}
|
||||
https://ai.google.dev/docs/gemini_api_overview
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={Colors.AccentRed}>[2]</Text> https://aistudio.google.com/
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={Colors.AccentGreen}>[3]</Text>{' '}
|
||||
https://developers.google.com/terms
|
||||
</Text>
|
||||
<Text>
|
||||
<Text color={Colors.AccentPurple}>[4]</Text>{' '}
|
||||
https://ai.google.dev/gemini-api/terms
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color={Colors.Gray}>Press Esc to exit.</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Box } from 'ink';
|
||||
import { type Config, AuthType } from '@google/gemini-cli-core';
|
||||
import { GeminiPrivacyNotice } from './GeminiPrivacyNotice.js';
|
||||
import { CloudPaidPrivacyNotice } from './CloudPaidPrivacyNotice.js';
|
||||
import { CloudFreePrivacyNotice } from './CloudFreePrivacyNotice.js';
|
||||
|
||||
interface PrivacyNoticeProps {
|
||||
onExit: () => void;
|
||||
config: Config;
|
||||
}
|
||||
|
||||
const PrivacyNoticeText = ({
|
||||
config,
|
||||
onExit,
|
||||
}: {
|
||||
config: Config;
|
||||
onExit: () => void;
|
||||
}) => {
|
||||
const authType = config.getContentGeneratorConfig()?.authType;
|
||||
|
||||
switch (authType) {
|
||||
case AuthType.USE_GEMINI:
|
||||
return <GeminiPrivacyNotice onExit={onExit} />;
|
||||
case AuthType.USE_VERTEX_AI:
|
||||
return <CloudPaidPrivacyNotice onExit={onExit} />;
|
||||
case AuthType.LOGIN_WITH_GOOGLE_PERSONAL:
|
||||
default:
|
||||
return <CloudFreePrivacyNotice config={config} onExit={onExit} />;
|
||||
}
|
||||
};
|
||||
|
||||
export const PrivacyNotice = ({ onExit, config }: PrivacyNoticeProps) => (
|
||||
<Box borderStyle="round" padding={1} flexDirection="column">
|
||||
<PrivacyNoticeText config={config} onExit={onExit} />
|
||||
</Box>
|
||||
);
|
|
@ -6,28 +6,30 @@
|
|||
|
||||
import { AuthClient } from 'google-auth-library';
|
||||
import {
|
||||
LoadCodeAssistResponse,
|
||||
CodeAssistGlobalUserSettingResponse,
|
||||
LoadCodeAssistRequest,
|
||||
OnboardUserRequest,
|
||||
LoadCodeAssistResponse,
|
||||
LongrunningOperationResponse,
|
||||
OnboardUserRequest,
|
||||
SetCodeAssistGlobalUserSettingRequest,
|
||||
} from './types.js';
|
||||
import {
|
||||
GenerateContentResponse,
|
||||
GenerateContentParameters,
|
||||
CountTokensParameters,
|
||||
EmbedContentResponse,
|
||||
CountTokensResponse,
|
||||
EmbedContentParameters,
|
||||
EmbedContentResponse,
|
||||
GenerateContentParameters,
|
||||
GenerateContentResponse,
|
||||
} from '@google/genai';
|
||||
import * as readline from 'readline';
|
||||
import { ContentGenerator } from '../core/contentGenerator.js';
|
||||
import {
|
||||
CaCountTokenResponse,
|
||||
CaGenerateContentResponse,
|
||||
toGenerateContentRequest,
|
||||
fromCountTokenResponse,
|
||||
fromGenerateContentResponse,
|
||||
toCountTokenRequest,
|
||||
fromCountTokenResponse,
|
||||
CaCountTokenResponse,
|
||||
toGenerateContentRequest,
|
||||
} from './converter.js';
|
||||
import { PassThrough } from 'node:stream';
|
||||
|
||||
|
@ -93,6 +95,21 @@ export class CodeAssistServer implements ContentGenerator {
|
|||
);
|
||||
}
|
||||
|
||||
async getCodeAssistGlobalUserSetting(): Promise<CodeAssistGlobalUserSettingResponse> {
|
||||
return await this.getEndpoint<CodeAssistGlobalUserSettingResponse>(
|
||||
'getCodeAssistGlobalUserSetting',
|
||||
);
|
||||
}
|
||||
|
||||
async setCodeAssistGlobalUserSetting(
|
||||
req: SetCodeAssistGlobalUserSettingRequest,
|
||||
): Promise<CodeAssistGlobalUserSettingResponse> {
|
||||
return await this.callEndpoint<CodeAssistGlobalUserSettingResponse>(
|
||||
'setCodeAssistGlobalUserSetting',
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
async countTokens(req: CountTokensParameters): Promise<CountTokensResponse> {
|
||||
const resp = await this.callEndpoint<CaCountTokenResponse>(
|
||||
'countTokens',
|
||||
|
@ -126,6 +143,20 @@ export class CodeAssistServer implements ContentGenerator {
|
|||
return res.data as T;
|
||||
}
|
||||
|
||||
async getEndpoint<T>(method: string, signal?: AbortSignal): Promise<T> {
|
||||
const res = await this.auth.request({
|
||||
url: `${CODE_ASSIST_ENDPOINT}/${CODE_ASSIST_API_VERSION}:${method}`,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...this.httpOptions.headers,
|
||||
},
|
||||
responseType: 'json',
|
||||
signal,
|
||||
});
|
||||
return res.data as T;
|
||||
}
|
||||
|
||||
async streamEndpoint<T>(
|
||||
method: string,
|
||||
req: object,
|
||||
|
|
|
@ -173,3 +173,13 @@ export interface HelpLinkUrl {
|
|||
description: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface SetCodeAssistGlobalUserSettingRequest {
|
||||
cloudaicompanionProject?: string;
|
||||
freeTierDataCollectionOptin: boolean;
|
||||
}
|
||||
|
||||
export interface CodeAssistGlobalUserSettingResponse {
|
||||
cloudaicompanionProject?: string;
|
||||
freeTierDataCollectionOptin: boolean;
|
||||
}
|
||||
|
|
|
@ -226,11 +226,6 @@ export class Config {
|
|||
}
|
||||
|
||||
async refreshAuth(authMethod: AuthType) {
|
||||
// Check if this is actually a switch to a different auth method
|
||||
const previousAuthType = this.contentGeneratorConfig?.authType;
|
||||
const _isAuthMethodSwitch =
|
||||
previousAuthType && previousAuthType !== authMethod;
|
||||
|
||||
// Always use the original default model when switching auth methods
|
||||
// This ensures users don't stay on Flash after switching between auth types
|
||||
// and allows API key users to get proper fallback behavior from getEffectiveModel
|
||||
|
|
|
@ -71,7 +71,8 @@ export class GeminiClient {
|
|||
);
|
||||
this.chat = await this.startChat();
|
||||
}
|
||||
private getContentGenerator(): ContentGenerator {
|
||||
|
||||
getContentGenerator(): ContentGenerator {
|
||||
if (!this.contentGenerator) {
|
||||
throw new Error('Content generator not initialized');
|
||||
}
|
||||
|
|
|
@ -70,7 +70,6 @@ export async function createContentGeneratorConfig(
|
|||
return contentGeneratorConfig;
|
||||
}
|
||||
|
||||
//
|
||||
if (authType === AuthType.USE_GEMINI && geminiApiKey) {
|
||||
contentGeneratorConfig.apiKey = geminiApiKey;
|
||||
contentGeneratorConfig.model = await getEffectiveModel(
|
||||
|
|
|
@ -21,6 +21,8 @@ export * from './core/nonInteractiveToolExecutor.js';
|
|||
|
||||
export * from './code_assist/codeAssist.js';
|
||||
export * from './code_assist/oauth2.js';
|
||||
export * from './code_assist/server.js';
|
||||
export * from './code_assist/types.js';
|
||||
|
||||
// Export utilities
|
||||
export * from './utils/paths.js';
|
||||
|
|
Loading…
Reference in New Issue