mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-15 12:13:59 +00:00
feat: add first-class CCS WebSearch runtime
This commit is contained in:
1 parent
c45b2a1138
commit
e260df7178
26 files changed
+1241
-158
No files matched your search
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user