mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-04 22:13:23 +00:00
Google retired the gemini CLI on 2026-06-18, so the gemini websearch fallback no longer works. Add agy (Antigravity) as the primary CLI websearch provider end to end: runtime spawn in websearch-transformer.cjs (agy --dangerously-skip-permissions --print-timeout Ns -p), detection (agy.ts), config schema and defaults, hook env, status, dashboard persistence, and docs. The legacy gemini provider stays present but is marked deprecated for enterprise users who retain access. Image analysis already supported the agy provider (no change). The CLIProxy gemini quota fetcher (Gemini API OAuth, not the cli binary) is untouched. Validation: typecheck, lint, and the websearch + config + web-server unit suites pass.
130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
/**
|
|
* WebSearch Hook Environment Variables
|
|
*
|
|
* Provides environment variables for WebSearch hook configuration.
|
|
*
|
|
* @module utils/websearch/hook-env
|
|
*/
|
|
|
|
import { normalizeSearxngBaseUrl } from './types';
|
|
import { resolveAllowedWebSearchTraceFile } from './trace';
|
|
import { getWebSearchConfig } from '../../config/config-loader-facade';
|
|
|
|
/**
|
|
* Get environment variables for WebSearch hook configuration.
|
|
*
|
|
* Simple env vars - hook reads these to control behavior.
|
|
*
|
|
* @returns Record of environment variables to set before spawning Claude
|
|
*/
|
|
export function getWebSearchHookEnv(): Record<string, string> {
|
|
const wsConfig = getWebSearchConfig();
|
|
const env: Record<string, string> = {
|
|
CCS_WEBSEARCH_ENABLED: '0',
|
|
CCS_WEBSEARCH_SKIP: '0',
|
|
CCS_WEBSEARCH_EXA: '0',
|
|
CCS_WEBSEARCH_TAVILY: '0',
|
|
CCS_WEBSEARCH_BRAVE: '0',
|
|
CCS_WEBSEARCH_SEARXNG: '0',
|
|
CCS_WEBSEARCH_DUCKDUCKGO: '0',
|
|
CCS_WEBSEARCH_AGY: '0',
|
|
CCS_WEBSEARCH_GEMINI: '0',
|
|
CCS_WEBSEARCH_OPENCODE: '0',
|
|
CCS_WEBSEARCH_GROK: '0',
|
|
};
|
|
|
|
if (process.env.CCS_WEBSEARCH_TRACE === '1' || process.env.CCS_DEBUG === '1') {
|
|
env.CCS_WEBSEARCH_TRACE = '1';
|
|
}
|
|
const traceFileOverride = resolveAllowedWebSearchTraceFile(process.env);
|
|
if (traceFileOverride) {
|
|
env.CCS_WEBSEARCH_TRACE_FILE = traceFileOverride;
|
|
}
|
|
|
|
// Skip hook entirely if disabled
|
|
if (!wsConfig.enabled) {
|
|
env.CCS_WEBSEARCH_SKIP = '1';
|
|
return env;
|
|
}
|
|
|
|
// Pass master switch
|
|
env.CCS_WEBSEARCH_ENABLED = '1';
|
|
|
|
// Pass individual provider enabled states
|
|
// Hook will only use providers that are BOTH enabled AND ready.
|
|
if (wsConfig.providers?.exa?.enabled) {
|
|
env.CCS_WEBSEARCH_EXA = '1';
|
|
env.CCS_WEBSEARCH_EXA_MAX_RESULTS = String(wsConfig.providers.exa.max_results || 5);
|
|
}
|
|
|
|
if (wsConfig.providers?.tavily?.enabled) {
|
|
env.CCS_WEBSEARCH_TAVILY = '1';
|
|
env.CCS_WEBSEARCH_TAVILY_MAX_RESULTS = String(wsConfig.providers.tavily.max_results || 5);
|
|
}
|
|
|
|
if (wsConfig.providers?.duckduckgo?.enabled) {
|
|
env.CCS_WEBSEARCH_DUCKDUCKGO = '1';
|
|
env.CCS_WEBSEARCH_DUCKDUCKGO_MAX_RESULTS = String(
|
|
wsConfig.providers.duckduckgo.max_results || 5
|
|
);
|
|
}
|
|
|
|
if (wsConfig.providers?.brave?.enabled) {
|
|
env.CCS_WEBSEARCH_BRAVE = '1';
|
|
env.CCS_WEBSEARCH_BRAVE_MAX_RESULTS = String(wsConfig.providers.brave.max_results || 5);
|
|
}
|
|
|
|
const searxngBaseUrl = normalizeSearxngBaseUrl(wsConfig.providers?.searxng?.url);
|
|
if (wsConfig.providers?.searxng?.enabled && searxngBaseUrl) {
|
|
env.CCS_WEBSEARCH_SEARXNG = '1';
|
|
env.CCS_WEBSEARCH_SEARXNG_URL = searxngBaseUrl;
|
|
env.CCS_WEBSEARCH_SEARXNG_MAX_RESULTS = String(wsConfig.providers.searxng.max_results || 5);
|
|
}
|
|
|
|
if (wsConfig.providers?.agy?.enabled) {
|
|
env.CCS_WEBSEARCH_AGY = '1';
|
|
if (wsConfig.providers.agy.model) {
|
|
env.CCS_WEBSEARCH_AGY_MODEL = wsConfig.providers.agy.model;
|
|
}
|
|
// Antigravity is the primary CLI fallback, so its timeout wins.
|
|
env.CCS_WEBSEARCH_TIMEOUT = String(wsConfig.providers.agy.timeout || 90);
|
|
}
|
|
|
|
if (wsConfig.providers?.gemini?.enabled) {
|
|
env.CCS_WEBSEARCH_GEMINI = '1';
|
|
if (wsConfig.providers.gemini.model) {
|
|
env.CCS_WEBSEARCH_GEMINI_MODEL = wsConfig.providers.gemini.model;
|
|
}
|
|
// Only set if Antigravity (primary) has not already chosen the timeout.
|
|
if (!env.CCS_WEBSEARCH_TIMEOUT) {
|
|
env.CCS_WEBSEARCH_TIMEOUT = String(wsConfig.providers.gemini.timeout || 55);
|
|
}
|
|
}
|
|
|
|
if (wsConfig.providers?.opencode?.enabled) {
|
|
env.CCS_WEBSEARCH_OPENCODE = '1';
|
|
if (wsConfig.providers.opencode.model) {
|
|
env.CCS_WEBSEARCH_OPENCODE_MODEL = wsConfig.providers.opencode.model;
|
|
}
|
|
// Use opencode timeout if no gemini timeout set
|
|
if (!env.CCS_WEBSEARCH_TIMEOUT) {
|
|
env.CCS_WEBSEARCH_TIMEOUT = String(wsConfig.providers.opencode.timeout || 90);
|
|
}
|
|
}
|
|
|
|
if (wsConfig.providers?.grok?.enabled) {
|
|
env.CCS_WEBSEARCH_GROK = '1';
|
|
// Use grok timeout if no other timeout set
|
|
if (!env.CCS_WEBSEARCH_TIMEOUT) {
|
|
env.CCS_WEBSEARCH_TIMEOUT = String(wsConfig.providers.grok.timeout || 55);
|
|
}
|
|
}
|
|
|
|
// Default timeout if none set
|
|
if (!env.CCS_WEBSEARCH_TIMEOUT) {
|
|
env.CCS_WEBSEARCH_TIMEOUT = '55';
|
|
}
|
|
|
|
return env;
|
|
}
|