@wildcard-ai/deepcontext
Version:
Advanced codebase indexing and semantic search MCP server
282 lines ⢠11.1 kB
JavaScript
/**
* Configuration Service
* Handles application configuration, environment variables, and service validation
*/
import { Logger } from '../utils/Logger.js';
export class ConfigurationService {
logger;
config;
validationResult = null;
constructor(configOverride, options = {}, loggerName = 'ConfigurationService') {
this.logger = new Logger(loggerName);
this.config = this.loadConfig(configOverride);
if (options.validateOnLoad !== false) {
this.validationResult = this.validateConfiguration(options.allowTestKeys !== false);
}
if (options.logConfigurationStatus !== false) {
this.logConfigurationStatus();
}
}
/**
* Load configuration from environment variables and overrides
*/
loadConfig(override) {
const envLogLevel = process.env.LOG_LEVEL;
const validLogLevel = envLogLevel === 'debug' || envLogLevel === 'info' ||
envLogLevel === 'warn' || envLogLevel === 'error'
? envLogLevel
: 'info';
const baseConfig = {
wildcardApiKey: process.env.WILDCARD_API_KEY || '',
jinaApiKey: process.env.JINA_API_KEY || (process.env.WILDCARD_API_KEY ? '' : 'test'),
turbopufferApiKey: process.env.TURBOPUFFER_API_KEY || (process.env.WILDCARD_API_KEY ? '' : 'test'),
logLevel: validLogLevel,
search: {
defaultVectorWeight: 0.6, // Primary weight for vector similarity
defaultBm25Weight: 0.4, // Secondary weight for keyword matching
defaultResultLimit: 8
},
chunking: {
maxChunkSize: 1500, // Standardized chunk size limit
treeSitterLimit: 32768, // 32KB TreeSitter reliable limit
jinaMaxChars: 20000, // Jina API character limit per chunk
semanticContextMargin: 100 // Margin for context size calculations
},
processing: {
maxAgeHours: 24, // Default incremental update window
batchSize: 20, // Conservative batch size to prevent API payload limits
batchDelayMs: 500 // Delay between batches in milliseconds
}
};
const finalConfig = { ...baseConfig, ...override };
this.logger.debug('Configuration loaded', {
hasJinaKey: !!finalConfig.jinaApiKey,
hasTurbopufferKey: !!finalConfig.turbopufferApiKey,
logLevel: finalConfig.logLevel
});
return finalConfig;
}
/**
* Get the current configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Update configuration
*/
updateConfig(updates, revalidate = true) {
this.config = { ...this.config, ...updates };
if (revalidate) {
this.validationResult = this.validateConfiguration();
}
this.logger.info('Configuration updated', {
updatedFields: Object.keys(updates),
isValid: this.validationResult?.isValid
});
}
/**
* Validate the current configuration
*/
validateConfiguration(allowTestKeys = true) {
const errors = [];
const warnings = [];
const hasWildcardKey = !!(this.config.wildcardApiKey && this.config.wildcardApiKey !== 'test');
// Check required keys - either Wildcard OR individual APIs
if (!hasWildcardKey) {
if (!this.config.jinaApiKey || this.config.jinaApiKey === 'test') {
if (!allowTestKeys) {
errors.push('Jina API key is required');
}
}
if (!this.config.turbopufferApiKey || this.config.turbopufferApiKey === 'test') {
if (!allowTestKeys) {
errors.push('Turbopuffer API key is required');
}
}
if (!allowTestKeys && errors.length > 0) {
errors.push('Or use Wildcard API key for all-in-one solution. Get it from https://wild-card.ai/deepcontext');
}
}
// Validate log level
const validLogLevels = ['debug', 'info', 'warn', 'error'];
if (!validLogLevels.includes(this.config.logLevel)) {
warnings.push(`Invalid log level: ${this.config.logLevel}. Using 'info' instead.`);
this.config.logLevel = 'info';
}
// Determine capabilities - Wildcard provides all, or check individual APIs
const capabilities = {
reranking: hasWildcardKey || !!(this.config.jinaApiKey && this.config.jinaApiKey !== 'test'),
vectorSearch: hasWildcardKey || !!(this.config.turbopufferApiKey && this.config.turbopufferApiKey !== 'test'),
embedding: hasWildcardKey || !!(this.config.jinaApiKey && this.config.jinaApiKey !== 'test')
};
const result = {
isValid: errors.length === 0,
errors,
warnings,
capabilities
};
this.validationResult = result;
return result;
}
/**
* Get the last validation result
*/
getValidationResult() {
return this.validationResult;
}
/**
* Check if configuration is valid
*/
isValid() {
return this.validationResult?.isValid ?? false;
}
/**
* Get service capabilities
*/
getCapabilities() {
if (!this.validationResult) {
this.validationResult = this.validateConfiguration();
}
return { ...this.validationResult.capabilities };
}
/**
* Check if a specific capability is available
*/
hasCapability(capability) {
const capabilities = this.getCapabilities();
return capabilities[capability];
}
/**
* Get configuration for specific services
*/
getJinaConfig() {
return {
apiKey: this.config.jinaApiKey,
isAvailable: !!(this.config.jinaApiKey && this.config.jinaApiKey !== 'test')
};
}
getTurbopufferConfig() {
return {
apiKey: this.config.turbopufferApiKey,
isAvailable: !!(this.config.turbopufferApiKey && this.config.turbopufferApiKey !== 'test')
};
}
getSearchConfig() {
return { ...this.config.search };
}
getChunkingConfig() {
return { ...this.config.chunking };
}
getProcessingConfig() {
return { ...this.config.processing };
}
/**
* Log configuration status to console
*/
logConfigurationStatus() {
const capabilities = this.getCapabilities();
const hasWildcardKey = !!(this.config.wildcardApiKey && this.config.wildcardApiKey !== 'test');
console.error('\nš§ Intelligent Context MCP Configuration:');
console.error('='.repeat(50));
console.error(`š Log Level: ${this.config.logLevel.toUpperCase()}`);
if (hasWildcardKey) {
console.error(`š Wildcard Backend: ā
Enabled (all-in-one solution)`);
}
else {
console.error(`š Jina API: ${this.config.jinaApiKey !== 'test' && this.config.jinaApiKey ? 'ā
Configured' : 'ā ļø Test Key'}`);
console.error(`šļø Turbopuffer: ${this.config.turbopufferApiKey !== 'test' && this.config.turbopufferApiKey ? 'ā
Configured' : 'ā ļø Test Key'}`);
}
console.error('\nš Available Capabilities:');
if (hasWildcardKey) {
console.error(`š Result Reranking: ā
Enabled (via Wildcard)`);
console.error(`š Vector Search: ā
Enabled (via Wildcard)`);
console.error(`š Embeddings: ā
Enabled (via Wildcard)`);
}
else {
console.error(`š Result Reranking: ${capabilities.reranking ? 'ā
Enabled' : 'ā Disabled'}`);
console.error(`š Vector Search: ${capabilities.vectorSearch ? 'ā
Enabled' : 'ā Disabled'}`);
console.error(`š Embeddings: ${capabilities.embedding ? 'ā
Enabled' : 'ā Disabled'}`);
}
// Show warnings if any
if (this.validationResult?.warnings.length) {
console.error('\nā ļø Configuration Warnings:');
this.validationResult.warnings.forEach(warning => {
console.error(` ⢠${warning}`);
});
}
// Show limitations only if not using Wildcard
if (!hasWildcardKey && (!capabilities.reranking || !capabilities.vectorSearch)) {
console.error('\nš” To enable full functionality:');
if (!capabilities.reranking) {
console.error(' ⢠Set JINA_API_KEY environment variable');
}
if (!capabilities.vectorSearch) {
console.error(' ⢠Set TURBOPUFFER_API_KEY environment variable');
}
console.error('\nš Or use the Wildcard all-in-one solution:');
console.error(' ⢠Set WILDCARD_API_KEY environment variable');
}
console.error('='.repeat(50));
}
/**
* Get configuration summary for status reporting
*/
getConfigurationSummary() {
const validation = this.validationResult || this.validateConfiguration();
return {
isValid: validation.isValid,
capabilities: validation.capabilities,
keyStatus: {
jina: !this.config.jinaApiKey ? 'missing' :
this.config.jinaApiKey === 'test' ? 'test' : 'configured',
turbopuffer: !this.config.turbopufferApiKey ? 'missing' :
this.config.turbopufferApiKey === 'test' ? 'test' : 'configured'
},
errors: validation.errors,
warnings: validation.warnings
};
}
/**
* Create a masked version of the config for logging (hides API keys)
*/
getMaskedConfig() {
return {
jinaApiKey: this.maskApiKey(this.config.jinaApiKey),
turbopufferApiKey: this.maskApiKey(this.config.turbopufferApiKey),
logLevel: this.config.logLevel
};
}
/**
* Mask API key for safe logging
*/
maskApiKey(key) {
if (!key || key === 'test')
return key;
if (key.length <= 8)
return '*'.repeat(key.length);
return key.substring(0, 4) + '*'.repeat(key.length - 8) + key.substring(key.length - 4);
}
/**
* Reset configuration to defaults
*/
resetToDefaults() {
this.config = this.loadConfig();
this.validationResult = this.validateConfiguration();
this.logger.info('Configuration reset to defaults');
}
/**
* Check if configuration has changed since last validation
*/
needsRevalidation() {
return this.validationResult === null;
}
/**
* Force revalidation of configuration
*/
revalidate(allowTestKeys = true) {
this.validationResult = this.validateConfiguration(allowTestKeys);
return this.validationResult;
}
}
//# sourceMappingURL=ConfigurationService.js.map