feat: add first-class CCS WebSearch runtime

This commit is contained in:
Tam Nhu Tran committed 2026-03-30 16:10:39 -04:00
1 parent c45b2a1138
commit e260df7178
26 files changed
+1241 -158

No files matched your search

+94 -61
View File
@@ -576,6 +576,90 @@ function outputAllFailedMessage(query, errors) {
emitHookOutput(buildFailureHookOutput(query, errors));
}
function getConfiguredProviders() {
return [
{
name: 'Exa',
id: 'exa',
available: () => isProviderEnabled('exa') && Boolean(getProviderApiKey('exa')),
fn: tryExaSearch,
},
{
name: 'Tavily',
id: 'tavily',
available: () => isProviderEnabled('tavily') && Boolean(getProviderApiKey('tavily')),
fn: tryTavilySearch,
},
{
name: 'Brave Search',
id: 'brave',
available: () => isProviderEnabled('brave') && Boolean(getProviderApiKey('brave')),
fn: tryBraveSearch,
},
{
name: 'DuckDuckGo',
id: 'duckduckgo',
available: () => isProviderEnabled('duckduckgo'),
fn: tryDuckDuckGoSearch,
},
{
name: 'Gemini CLI',
id: 'gemini',
available: () => isProviderEnabled('gemini') && isCliAvailable('gemini'),
fn: tryGeminiSearch,
},
{
name: 'OpenCode',
id: 'opencode',
available: () => isProviderEnabled('opencode') && isCliAvailable('opencode'),
fn: tryOpenCodeSearch,
},
{
name: 'Grok CLI',
id: 'grok',
available: () => isProviderEnabled('grok') && isCliAvailable('grok'),
fn: tryGrokSearch,
},
];
}
function getActiveProviders() {
return getConfiguredProviders().filter((provider) => provider.available());
}
function hasAnyActiveProviders() {
return getActiveProviders().length > 0;
}
async function runLocalWebSearch(query, timeoutSec = DEFAULT_TIMEOUT_SEC) {
const activeProviders = getActiveProviders();
debug(
`Enabled providers: ${activeProviders.map((provider) => provider.name).join(', ') || 'none'}`
);
if (activeProviders.length === 0) {
return { success: false, noActiveProviders: true, errors: [] };
}
const errors = [];
for (const provider of activeProviders) {
debug(`Trying ${provider.name}`);
const result = await provider.fn(query, timeoutSec);
if (result.success) {
return {
success: true,
providerId: provider.id,
providerName: provider.name,
content: result.content,
};
}
errors.push({ provider: provider.name, error: result.error });
}
return { success: false, noActiveProviders: false, errors };
}
async function processHook(input) {
try {
if (shouldSkipHook()) {
@@ -596,72 +680,17 @@ async function processHook(input) {
process.env.CCS_WEBSEARCH_TIMEOUT || `${DEFAULT_TIMEOUT_SEC}`,
10
);
const providers = [
{
name: 'Exa',
id: 'exa',
available: () => isProviderEnabled('exa') && Boolean(getProviderApiKey('exa')),
fn: tryExaSearch,
},
{
name: 'Tavily',
id: 'tavily',
available: () => isProviderEnabled('tavily') && Boolean(getProviderApiKey('tavily')),
fn: tryTavilySearch,
},
{
name: 'Brave Search',
id: 'brave',
available: () => isProviderEnabled('brave') && Boolean(getProviderApiKey('brave')),
fn: tryBraveSearch,
},
{
name: 'DuckDuckGo',
id: 'duckduckgo',
available: () => isProviderEnabled('duckduckgo'),
fn: tryDuckDuckGoSearch,
},
{
name: 'Gemini CLI',
id: 'gemini',
available: () => isProviderEnabled('gemini') && isCliAvailable('gemini'),
fn: tryGeminiSearch,
},
{
name: 'OpenCode',
id: 'opencode',
available: () => isProviderEnabled('opencode') && isCliAvailable('opencode'),
fn: tryOpenCodeSearch,
},
{
name: 'Grok CLI',
id: 'grok',
available: () => isProviderEnabled('grok') && isCliAvailable('grok'),
fn: tryGrokSearch,
},
];
const activeProviders = providers.filter((provider) => provider.available());
debug(
`Enabled providers: ${activeProviders.map((provider) => provider.name).join(', ') || 'none'}`
);
if (activeProviders.length === 0) {
const result = await runLocalWebSearch(query, timeout);
if (result.noActiveProviders) {
process.exit(0);
}
const errors = [];
for (const provider of activeProviders) {
debug(`Trying ${provider.name}`);
const result = await provider.fn(query, timeout);
if (result.success) {
outputSuccess(query, result.content, provider.name);
return;
}
errors.push({ provider: provider.name, error: result.error });
if (result.success) {
outputSuccess(query, result.content, result.providerName);
return;
}
outputAllFailedMessage(query, errors);
outputAllFailedMessage(query, result.errors);
} catch (error) {
debug(`Hook error: ${error.message}`);
process.exit(0);
@@ -691,6 +720,10 @@ module.exports = {
buildSuccessHookOutput,
extractDuckDuckGoResults,
formatStructuredSearchResults,
getActiveProviders,
hasAnyActiveProviders,
runLocalWebSearch,
shouldSkipHook,
tryExaSearch,
tryTavilySearch,
tryDuckDuckGoSearch,