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 ### 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) - OAuth authentication (no GEMINI_API_KEY needed)
### Installation ### Installation
The Gemini CLI is typically installed via: The Gemini CLI is installed via npm:
```bash ```bash
pip install google-generativeai npm install -g @google/gemini-cli
# or
pipx install google-generativeai
``` ```
Then authenticate: Then authenticate by running gemini once (opens browser):
```bash ```bash
gemini auth login gemini
``` ```
## MCP Providers ## MCP Providers
@@ -176,8 +174,8 @@ CCS writes MCP configuration to `~/.claude/.mcp.json`. Example:
### Gemini CLI Issues ### Gemini CLI Issues
1. **Not installed**: Install with `pip install google-generativeai` 1. **Not installed**: Install with `npm install -g @google/gemini-cli`
2. **Not authenticated**: Run `gemini auth login` 2. **Not authenticated**: Run `gemini` to open browser for OAuth login
3. **Timeout**: Increase timeout in config or via `CCS_GEMINI_TIMEOUT=90` 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. **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}"`, `Query: "${query}"`,
'', '',
'Troubleshooting:', 'Troubleshooting:',
' - Check if Gemini CLI is authenticated: gemini auth status', ' - Check ~/.gemini/oauth_creds.json exists',
' - Re-authenticate if needed: gemini auth login', ' - Re-authenticate: run `gemini` (opens browser)',
].join('\n'); ].join('\n');
const output = { const output = {
@@ -546,7 +546,7 @@ function outputNoProvidersEnabled(query) {
'', '',
'1. Gemini CLI (FREE, 1000 req/day):', '1. Gemini CLI (FREE, 1000 req/day):',
' npm install -g @google/gemini-cli', ' npm install -g @google/gemini-cli',
' gemini auth login', ' gemini # opens browser to authenticate',
'', '',
'2. OpenCode (FREE via Zen):', '2. OpenCode (FREE via Zen):',
' curl -fsSL https://opencode.ai/install | bash', ' curl -fsSL https://opencode.ai/install | bash',
@@ -595,7 +595,7 @@ function outputAllFailedMessage(query, errors) {
`Query: "${query}"`, `Query: "${query}"`,
'', '',
'Troubleshooting:', 'Troubleshooting:',
' - Gemini: gemini auth status / gemini auth login', ' - Gemini: run `gemini` to authenticate (opens browser)',
' - OpenCode: opencode --version', ' - OpenCode: opencode --version',
' - Grok: Check XAI_API_KEY environment variable', ' - Grok: Check XAI_API_KEY environment variable',
].join('\n'); ].join('\n');
+69 -8
View File
@@ -114,6 +114,32 @@ export function hasGeminiCli(): boolean {
return getGeminiCliStatus().installed; 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) * 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 * WebSearch availability status for third-party profiles
*/ */
export type WebSearchReadiness = 'ready' | 'unavailable'; export type WebSearchReadiness = 'ready' | 'needs_auth' | 'unavailable';
/** /**
* WebSearch status for display * WebSearch status for display
@@ -700,6 +726,7 @@ export type WebSearchReadiness = 'ready' | 'unavailable';
export interface WebSearchStatus { export interface WebSearchStatus {
readiness: WebSearchReadiness; readiness: WebSearchReadiness;
geminiCli: boolean; geminiCli: boolean;
geminiAuthenticated: boolean;
grokCli: boolean; grokCli: boolean;
opencodeCli: boolean; opencodeCli: boolean;
message: string; message: string;
@@ -709,6 +736,7 @@ export interface WebSearchStatus {
* Get WebSearch readiness status for display * Get WebSearch readiness status for display
* *
* Called on third-party profile startup to inform user. * Called on third-party profile startup to inform user.
* Checks both installation AND authentication status for Gemini CLI.
*/ */
export function getWebSearchReadiness(): WebSearchStatus { export function getWebSearchReadiness(): WebSearchStatus {
const wsConfig = getWebSearchConfig(); const wsConfig = getWebSearchConfig();
@@ -718,6 +746,7 @@ export function getWebSearchReadiness(): WebSearchStatus {
return { return {
readiness: 'unavailable', readiness: 'unavailable',
geminiCli: false, geminiCli: false,
geminiAuthenticated: false,
grokCli: false, grokCli: false,
opencodeCli: false, opencodeCli: false,
message: 'Disabled in config', message: 'Disabled in config',
@@ -726,28 +755,56 @@ export function getWebSearchReadiness(): WebSearchStatus {
// Check all CLIs // Check all CLIs
const geminiInstalled = hasGeminiCli(); const geminiInstalled = hasGeminiCli();
const geminiAuthed = geminiInstalled && isGeminiAuthenticated();
const grokInstalled = hasGrokCli(); const grokInstalled = hasGrokCli();
const opencodeInstalled = hasOpenCodeCli(); const opencodeInstalled = hasOpenCodeCli();
// Build message based on installed CLIs // Build message based on installed + authenticated CLIs
const installedClis: string[] = []; const readyClis: string[] = [];
if (geminiInstalled) installedClis.push('Gemini'); const needsAuthClis: string[] = [];
if (grokInstalled) installedClis.push('Grok');
if (opencodeInstalled) installedClis.push('OpenCode');
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 { return {
readiness: 'ready', readiness: 'ready',
geminiCli: geminiInstalled, geminiCli: geminiInstalled,
geminiAuthenticated: geminiAuthed,
grokCli: grokInstalled, grokCli: grokInstalled,
opencodeCli: opencodeInstalled, 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 { return {
readiness: 'unavailable', readiness: 'unavailable',
geminiCli: false, geminiCli: false,
geminiAuthenticated: false,
grokCli: false, grokCli: false,
opencodeCli: false, opencodeCli: false,
message: 'Install: npm i -g @google/gemini-cli', message: 'Install: npm i -g @google/gemini-cli',
@@ -767,6 +824,10 @@ export function displayWebSearchStatus(): void {
case 'ready': case 'ready':
console.error(ok(`WebSearch: ${status.message}`)); console.error(ok(`WebSearch: ${status.message}`));
break; break;
case 'needs_auth':
// CLI installed but not authenticated - show warning
console.error(warn(`WebSearch: ${status.message}`));
break;
case 'unavailable': case 'unavailable':
console.error(fail(`WebSearch: ${status.message}`)); console.error(fail(`WebSearch: ${status.message}`));
// Show install hints for CLI-only users // Show install hints for CLI-only users