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 ansiEscapes from 'ansi-escapes';
|
||||||
import { OverflowProvider } from './contexts/OverflowContext.js';
|
import { OverflowProvider } from './contexts/OverflowContext.js';
|
||||||
import { ShowMoreLines } from './components/ShowMoreLines.js';
|
import { ShowMoreLines } from './components/ShowMoreLines.js';
|
||||||
|
import { PrivacyNotice } from './privacy/PrivacyNotice.js';
|
||||||
|
|
||||||
const CTRL_EXIT_PROMPT_DURATION_MS = 1000;
|
const CTRL_EXIT_PROMPT_DURATION_MS = 1000;
|
||||||
|
|
||||||
|
@ -130,6 +131,11 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
|
||||||
const [ctrlDPressedOnce, setCtrlDPressedOnce] = useState(false);
|
const [ctrlDPressedOnce, setCtrlDPressedOnce] = useState(false);
|
||||||
const ctrlDTimerRef = useRef<NodeJS.Timeout | null>(null);
|
const ctrlDTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const [constrainHeight, setConstrainHeight] = useState<boolean>(true);
|
const [constrainHeight, setConstrainHeight] = useState<boolean>(true);
|
||||||
|
const [showPrivacyNotice, setShowPrivacyNotice] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const openPrivacyNotice = useCallback(() => {
|
||||||
|
setShowPrivacyNotice(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const errorCount = useMemo(
|
const errorCount = useMemo(
|
||||||
() => consoleMessages.filter((msg) => msg.type === 'error').length,
|
() => consoleMessages.filter((msg) => msg.type === 'error').length,
|
||||||
|
@ -277,6 +283,7 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
|
||||||
toggleCorgiMode,
|
toggleCorgiMode,
|
||||||
showToolDescriptions,
|
showToolDescriptions,
|
||||||
setQuittingMessages,
|
setQuittingMessages,
|
||||||
|
openPrivacyNotice,
|
||||||
);
|
);
|
||||||
const pendingHistoryItems = [...pendingSlashCommandHistoryItems];
|
const pendingHistoryItems = [...pendingSlashCommandHistoryItems];
|
||||||
|
|
||||||
|
@ -712,6 +719,11 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
|
||||||
onExit={exitEditorDialog}
|
onExit={exitEditorDialog}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
) : showPrivacyNotice ? (
|
||||||
|
<PrivacyNotice
|
||||||
|
onExit={() => setShowPrivacyNotice(false)}
|
||||||
|
config={config}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<LoadingIndicator
|
<LoadingIndicator
|
||||||
|
|
|
@ -76,6 +76,7 @@ export const useSlashCommandProcessor = (
|
||||||
toggleCorgiMode: () => void,
|
toggleCorgiMode: () => void,
|
||||||
showToolDescriptions: boolean = false,
|
showToolDescriptions: boolean = false,
|
||||||
setQuittingMessages: (message: HistoryItem[]) => void,
|
setQuittingMessages: (message: HistoryItem[]) => void,
|
||||||
|
openPrivacyNotice: () => void,
|
||||||
) => {
|
) => {
|
||||||
const session = useSessionStats();
|
const session = useSessionStats();
|
||||||
const gitService = useMemo(() => {
|
const gitService = useMemo(() => {
|
||||||
|
@ -254,6 +255,13 @@ export const useSlashCommandProcessor = (
|
||||||
openEditorDialog();
|
openEditorDialog();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'privacy',
|
||||||
|
description: 'display the privacy notice',
|
||||||
|
action: (_mainCommand, _subCommand, _args) => {
|
||||||
|
openPrivacyNotice();
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'stats',
|
name: 'stats',
|
||||||
altName: 'usage',
|
altName: 'usage',
|
||||||
|
@ -1022,6 +1030,7 @@ export const useSlashCommandProcessor = (
|
||||||
setQuittingMessages,
|
setQuittingMessages,
|
||||||
pendingCompressionItemRef,
|
pendingCompressionItemRef,
|
||||||
setPendingCompressionItem,
|
setPendingCompressionItem,
|
||||||
|
openPrivacyNotice,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleSlashCommand = useCallback(
|
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 { AuthClient } from 'google-auth-library';
|
||||||
import {
|
import {
|
||||||
LoadCodeAssistResponse,
|
CodeAssistGlobalUserSettingResponse,
|
||||||
LoadCodeAssistRequest,
|
LoadCodeAssistRequest,
|
||||||
OnboardUserRequest,
|
LoadCodeAssistResponse,
|
||||||
LongrunningOperationResponse,
|
LongrunningOperationResponse,
|
||||||
|
OnboardUserRequest,
|
||||||
|
SetCodeAssistGlobalUserSettingRequest,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
import {
|
import {
|
||||||
GenerateContentResponse,
|
|
||||||
GenerateContentParameters,
|
|
||||||
CountTokensParameters,
|
CountTokensParameters,
|
||||||
EmbedContentResponse,
|
|
||||||
CountTokensResponse,
|
CountTokensResponse,
|
||||||
EmbedContentParameters,
|
EmbedContentParameters,
|
||||||
|
EmbedContentResponse,
|
||||||
|
GenerateContentParameters,
|
||||||
|
GenerateContentResponse,
|
||||||
} from '@google/genai';
|
} from '@google/genai';
|
||||||
import * as readline from 'readline';
|
import * as readline from 'readline';
|
||||||
import { ContentGenerator } from '../core/contentGenerator.js';
|
import { ContentGenerator } from '../core/contentGenerator.js';
|
||||||
import {
|
import {
|
||||||
|
CaCountTokenResponse,
|
||||||
CaGenerateContentResponse,
|
CaGenerateContentResponse,
|
||||||
toGenerateContentRequest,
|
fromCountTokenResponse,
|
||||||
fromGenerateContentResponse,
|
fromGenerateContentResponse,
|
||||||
toCountTokenRequest,
|
toCountTokenRequest,
|
||||||
fromCountTokenResponse,
|
toGenerateContentRequest,
|
||||||
CaCountTokenResponse,
|
|
||||||
} from './converter.js';
|
} from './converter.js';
|
||||||
import { PassThrough } from 'node:stream';
|
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> {
|
async countTokens(req: CountTokensParameters): Promise<CountTokensResponse> {
|
||||||
const resp = await this.callEndpoint<CaCountTokenResponse>(
|
const resp = await this.callEndpoint<CaCountTokenResponse>(
|
||||||
'countTokens',
|
'countTokens',
|
||||||
|
@ -126,6 +143,20 @@ export class CodeAssistServer implements ContentGenerator {
|
||||||
return res.data as T;
|
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>(
|
async streamEndpoint<T>(
|
||||||
method: string,
|
method: string,
|
||||||
req: object,
|
req: object,
|
||||||
|
|
|
@ -173,3 +173,13 @@ export interface HelpLinkUrl {
|
||||||
description: string;
|
description: string;
|
||||||
url: 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) {
|
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
|
// Always use the original default model when switching auth methods
|
||||||
// This ensures users don't stay on Flash after switching between auth types
|
// 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
|
// and allows API key users to get proper fallback behavior from getEffectiveModel
|
||||||
|
|
|
@ -71,7 +71,8 @@ export class GeminiClient {
|
||||||
);
|
);
|
||||||
this.chat = await this.startChat();
|
this.chat = await this.startChat();
|
||||||
}
|
}
|
||||||
private getContentGenerator(): ContentGenerator {
|
|
||||||
|
getContentGenerator(): ContentGenerator {
|
||||||
if (!this.contentGenerator) {
|
if (!this.contentGenerator) {
|
||||||
throw new Error('Content generator not initialized');
|
throw new Error('Content generator not initialized');
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,6 @@ export async function createContentGeneratorConfig(
|
||||||
return contentGeneratorConfig;
|
return contentGeneratorConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
if (authType === AuthType.USE_GEMINI && geminiApiKey) {
|
if (authType === AuthType.USE_GEMINI && geminiApiKey) {
|
||||||
contentGeneratorConfig.apiKey = geminiApiKey;
|
contentGeneratorConfig.apiKey = geminiApiKey;
|
||||||
contentGeneratorConfig.model = await getEffectiveModel(
|
contentGeneratorConfig.model = await getEffectiveModel(
|
||||||
|
|
|
@ -21,6 +21,8 @@ export * from './core/nonInteractiveToolExecutor.js';
|
||||||
|
|
||||||
export * from './code_assist/codeAssist.js';
|
export * from './code_assist/codeAssist.js';
|
||||||
export * from './code_assist/oauth2.js';
|
export * from './code_assist/oauth2.js';
|
||||||
|
export * from './code_assist/server.js';
|
||||||
|
export * from './code_assist/types.js';
|
||||||
|
|
||||||
// Export utilities
|
// Export utilities
|
||||||
export * from './utils/paths.js';
|
export * from './utils/paths.js';
|
||||||
|
|
Loading…
Reference in New Issue