fix(websearch): detect Gemini CLI auth status before showing Ready

Previously, WebSearch status showed "Ready (Gemini)" when the CLI binary
was installed, misleading users who hadn't authenticated yet.

Changes:
- Add isGeminiAuthenticated() to check ~/.gemini/oauth_creds.json
- Add 'needs_auth' state to WebSearchReadiness type
- Show warning "[!] Gemini: run 'gemini' to login" when not authenticated
- Fix incorrect 'gemini auth login' references (no such command exists)
- Update docs with correct npm install and authentication instructions
This commit is contained in:
kaitranntt
2025-12-18 03:24:59 -05:00
parent f3a6f4ef4e
commit 98c21efb5a
3 changed files with 80 additions and 21 deletions
+7 -9
View File
@@ -57,21 +57,19 @@ The **ultimate solution** for third-party WebSearch. Uses `gemini` CLI with OAut
### Requirements
- `gemini` CLI installed and authenticated (`gemini auth login`)
- `gemini` CLI installed and authenticated (run `gemini` to authenticate via browser)
- OAuth authentication (no GEMINI_API_KEY needed)
### Installation
The Gemini CLI is typically installed via:
The Gemini CLI is installed via npm:
```bash
pip install google-generativeai
# or
pipx install google-generativeai
npm install -g @google/gemini-cli
```
Then authenticate:
Then authenticate by running gemini once (opens browser):
```bash
gemini auth login
gemini
```
## MCP Providers
@@ -176,8 +174,8 @@ CCS writes MCP configuration to `~/.claude/.mcp.json`. Example:
### Gemini CLI Issues
1. **Not installed**: Install with `pip install google-generativeai`
2. **Not authenticated**: Run `gemini auth login`
1. **Not installed**: Install with `npm install -g @google/gemini-cli`
2. **Not authenticated**: Run `gemini` to open browser for OAuth login
3. **Timeout**: Increase timeout in config or via `CCS_GEMINI_TIMEOUT=90`
4. **Skip Gemini**: Set `CCS_GEMINI_SKIP=1` to use MCP fallback only
+4 -4
View File
@@ -513,8 +513,8 @@ function outputError(query, error, providerName) {
`Query: "${query}"`,
'',
'Troubleshooting:',
' - Check if Gemini CLI is authenticated: gemini auth status',
' - Re-authenticate if needed: gemini auth login',
' - Check ~/.gemini/oauth_creds.json exists',
' - Re-authenticate: run `gemini` (opens browser)',
].join('\n');
const output = {
@@ -546,7 +546,7 @@ function outputNoProvidersEnabled(query) {
'',
'1. Gemini CLI (FREE, 1000 req/day):',
' npm install -g @google/gemini-cli',
' gemini auth login',
' gemini # opens browser to authenticate',
'',
'2. OpenCode (FREE via Zen):',
' curl -fsSL https://opencode.ai/install | bash',
@@ -595,7 +595,7 @@ function outputAllFailedMessage(query, errors) {
`Query: "${query}"`,
'',
'Troubleshooting:',
' - Gemini: gemini auth status / gemini auth login',
' - Gemini: run `gemini` to authenticate (opens browser)',
' - OpenCode: opencode --version',
' - Grok: Check XAI_API_KEY environment variable',
].join('\n');
+69 -8
View File
@@ -114,6 +114,32 @@ export function hasGeminiCli(): boolean {
return getGeminiCliStatus().installed;
}
/**
* Check if Gemini CLI is authenticated
*
* Gemini CLI stores OAuth credentials in ~/.gemini/oauth_creds.json
* Authentication is done by running `gemini` which opens browser for OAuth.
* Note: There is NO `gemini auth login` command - just run `gemini` to authenticate.
*
* @returns true if oauth_creds.json exists with access_token
*/
export function isGeminiAuthenticated(): boolean {
const oauthPath = path.join(os.homedir(), '.gemini', 'oauth_creds.json');
if (!fs.existsSync(oauthPath)) {
return false;
}
try {
const content = fs.readFileSync(oauthPath, 'utf8');
const creds = JSON.parse(content);
// Check if access_token exists (doesn't validate expiry - Gemini CLI handles refresh)
return !!creds.access_token;
} catch {
return false;
}
}
/**
* Clear Gemini CLI cache (for testing or after installation)
*/
@@ -692,7 +718,7 @@ export function getWebSearchHookEnv(): Record<string, string> {
/**
* WebSearch availability status for third-party profiles
*/
export type WebSearchReadiness = 'ready' | 'unavailable';
export type WebSearchReadiness = 'ready' | 'needs_auth' | 'unavailable';
/**
* WebSearch status for display
@@ -700,6 +726,7 @@ export type WebSearchReadiness = 'ready' | 'unavailable';
export interface WebSearchStatus {
readiness: WebSearchReadiness;
geminiCli: boolean;
geminiAuthenticated: boolean;
grokCli: boolean;
opencodeCli: boolean;
message: string;
@@ -709,6 +736,7 @@ export interface WebSearchStatus {
* Get WebSearch readiness status for display
*
* Called on third-party profile startup to inform user.
* Checks both installation AND authentication status for Gemini CLI.
*/
export function getWebSearchReadiness(): WebSearchStatus {
const wsConfig = getWebSearchConfig();
@@ -718,6 +746,7 @@ export function getWebSearchReadiness(): WebSearchStatus {
return {
readiness: 'unavailable',
geminiCli: false,
geminiAuthenticated: false,
grokCli: false,
opencodeCli: false,
message: 'Disabled in config',
@@ -726,28 +755,56 @@ export function getWebSearchReadiness(): WebSearchStatus {
// Check all CLIs
const geminiInstalled = hasGeminiCli();
const geminiAuthed = geminiInstalled && isGeminiAuthenticated();
const grokInstalled = hasGrokCli();
const opencodeInstalled = hasOpenCodeCli();
// Build message based on installed CLIs
const installedClis: string[] = [];
if (geminiInstalled) installedClis.push('Gemini');
if (grokInstalled) installedClis.push('Grok');
if (opencodeInstalled) installedClis.push('OpenCode');
// Build message based on installed + authenticated CLIs
const readyClis: string[] = [];
const needsAuthClis: string[] = [];
if (installedClis.length > 0) {
// Gemini requires auth check
if (geminiInstalled) {
if (geminiAuthed) {
readyClis.push('Gemini');
} else {
needsAuthClis.push('Gemini');
}
}
// Other CLIs don't require auth check (for now)
if (grokInstalled) readyClis.push('Grok');
if (opencodeInstalled) readyClis.push('OpenCode');
// Determine overall status
if (readyClis.length > 0) {
// At least one CLI is ready
return {
readiness: 'ready',
geminiCli: geminiInstalled,
geminiAuthenticated: geminiAuthed,
grokCli: grokInstalled,
opencodeCli: opencodeInstalled,
message: `Ready (${installedClis.join(' + ')})`,
message: `Ready (${readyClis.join(' + ')})`,
};
}
if (needsAuthClis.length > 0) {
// CLIs installed but need auth
return {
readiness: 'needs_auth',
geminiCli: geminiInstalled,
geminiAuthenticated: false,
grokCli: grokInstalled,
opencodeCli: opencodeInstalled,
message: `Gemini: run 'gemini' to login`,
};
}
return {
readiness: 'unavailable',
geminiCli: false,
geminiAuthenticated: false,
grokCli: false,
opencodeCli: false,
message: 'Install: npm i -g @google/gemini-cli',
@@ -767,6 +824,10 @@ export function displayWebSearchStatus(): void {
case 'ready':
console.error(ok(`WebSearch: ${status.message}`));
break;
case 'needs_auth':
// CLI installed but not authenticated - show warning
console.error(warn(`WebSearch: ${status.message}`));
break;
case 'unavailable':
console.error(fail(`WebSearch: ${status.message}`));
// Show install hints for CLI-only users