chore: recovery checkpoint - v3.0 API successfully recovered
CRITICAL CHECKPOINT - DO NOT PUSH TO GITHUB Recovery Status: - Successfully recovered brainy.ts from compiled JavaScript - All core v3.0 API methods functional (add, get, update, delete, relate, find, etc.) - Neural subsystem intact (562KB embedded patterns, NLP working) - Augmentation pipeline operational (20+ augmentations) - HNSW clustering system complete - Triple Intelligence compiled (needs constructor fix) - Test suite validates functionality Changes preserved: - 898 files with changes from last 3 days - 144,475 insertions - All augmentation improvements - All test coverage enhancements - Complete v3.0 feature set This is a LOCAL checkpoint only - contains recovered work after corruption incident. Created backup in .backups/brainy-full-20250910-151314.tar.gz Branch: recovery-checkpoint-20250910-151433 Date: Wed Sep 10 03:18:04 PM PDT 2025
This commit is contained in:
parent
f65455fb22
commit
8ff382ca3b
895 changed files with 143654 additions and 28268 deletions
|
|
@ -0,0 +1,441 @@
|
|||
/**
|
||||
* Augmentation Discovery API
|
||||
*
|
||||
* Provides discovery and configuration capabilities for augmentations
|
||||
* Enables tools like brain-cloud to dynamically discover, configure, and manage augmentations
|
||||
*/
|
||||
/**
|
||||
* Augmentation Discovery API
|
||||
*
|
||||
* Provides a unified interface for discovering and managing augmentations
|
||||
*/
|
||||
export class AugmentationDiscovery {
|
||||
constructor(registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
/**
|
||||
* Discover all registered augmentations with manifests
|
||||
* @param options Discovery options
|
||||
* @returns List of augmentation listings
|
||||
*/
|
||||
async discover(options = {}) {
|
||||
const augmentations = this.registry.getAll();
|
||||
const listings = [];
|
||||
for (const aug of augmentations) {
|
||||
// Check if augmentation has manifest support
|
||||
const hasManifest = 'getManifest' in aug && typeof aug.getManifest === 'function';
|
||||
if (!hasManifest) {
|
||||
// Skip augmentations without manifest support (legacy)
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// Check if augmentation has manifest method
|
||||
if (!('getManifest' in aug) || typeof aug.getManifest !== 'function') {
|
||||
continue;
|
||||
}
|
||||
const getManifestFn = aug.getManifest;
|
||||
const manifest = getManifestFn();
|
||||
// Apply filters
|
||||
if (options.category && manifest.category !== options.category) {
|
||||
continue;
|
||||
}
|
||||
if (options.enabled !== undefined) {
|
||||
const isEnabled = aug.enabled !== false;
|
||||
if (isEnabled !== options.enabled) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Build listing
|
||||
const listing = {
|
||||
id: manifest.id,
|
||||
name: manifest.name,
|
||||
manifest,
|
||||
status: {
|
||||
enabled: aug.enabled !== false,
|
||||
initialized: aug.isInitialized || false,
|
||||
category: aug.category || manifest.category,
|
||||
priority: aug.priority
|
||||
}
|
||||
};
|
||||
// Include configuration if requested
|
||||
if (options.includeConfig && 'getConfig' in aug) {
|
||||
const getConfigFn = aug.getConfig;
|
||||
listing.config = {
|
||||
current: getConfigFn()
|
||||
};
|
||||
if (options.includeSchema) {
|
||||
listing.config.schema = manifest.configSchema;
|
||||
}
|
||||
}
|
||||
listings.push(listing);
|
||||
}
|
||||
catch (error) {
|
||||
console.warn(`Failed to get manifest for augmentation ${aug.name}:`, error);
|
||||
}
|
||||
}
|
||||
// Sort by priority (highest first) then by name
|
||||
listings.sort((a, b) => {
|
||||
const priorityDiff = b.status.priority - a.status.priority;
|
||||
if (priorityDiff !== 0)
|
||||
return priorityDiff;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
return listings;
|
||||
}
|
||||
/**
|
||||
* Get a specific augmentation's manifest
|
||||
* @param augId Augmentation ID
|
||||
* @returns Augmentation manifest or null if not found
|
||||
*/
|
||||
async getManifest(augId) {
|
||||
const aug = this.registry.get(augId);
|
||||
if (!aug || !('getManifest' in aug)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const getManifestFn = aug.getManifest;
|
||||
return getManifestFn();
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to get manifest for ${augId}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get configuration schema for an augmentation
|
||||
* @param augId Augmentation ID
|
||||
* @returns Configuration schema or null
|
||||
*/
|
||||
async getConfigSchema(augId) {
|
||||
const manifest = await this.getManifest(augId);
|
||||
return manifest?.configSchema || null;
|
||||
}
|
||||
/**
|
||||
* Get current configuration for an augmentation
|
||||
* @param augId Augmentation ID
|
||||
* @returns Current configuration or null
|
||||
*/
|
||||
async getConfig(augId) {
|
||||
const aug = this.registry.get(augId);
|
||||
if (!aug || !('getConfig' in aug)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const getConfigFn = aug.getConfig;
|
||||
return getConfigFn();
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to get config for ${augId}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Update configuration for an augmentation
|
||||
* @param augId Augmentation ID
|
||||
* @param config New configuration
|
||||
* @returns Updated configuration or null on failure
|
||||
*/
|
||||
async updateConfig(augId, config) {
|
||||
const aug = this.registry.get(augId);
|
||||
if (!aug || !('updateConfig' in aug) || !('getConfig' in aug)) {
|
||||
throw new Error(`Augmentation ${augId} does not support configuration updates`);
|
||||
}
|
||||
try {
|
||||
const updateConfigFn = aug.updateConfig;
|
||||
await updateConfigFn(config);
|
||||
const getConfigFn = aug.getConfig;
|
||||
return getConfigFn();
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Failed to update config for ${augId}: ${error}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validate configuration against schema
|
||||
* @param augId Augmentation ID
|
||||
* @param config Configuration to validate
|
||||
* @returns Validation result
|
||||
*/
|
||||
async validateConfig(augId, config) {
|
||||
const schema = await this.getConfigSchema(augId);
|
||||
if (!schema) {
|
||||
return {
|
||||
valid: true,
|
||||
warnings: ['No schema available for validation']
|
||||
};
|
||||
}
|
||||
const errors = [];
|
||||
const warnings = [];
|
||||
const suggestions = [];
|
||||
// Check required fields
|
||||
if (schema.required) {
|
||||
for (const field of schema.required) {
|
||||
if (config[field] === undefined) {
|
||||
errors.push(`Missing required field: ${field}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Validate properties
|
||||
if (schema.properties) {
|
||||
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
||||
const value = config[key];
|
||||
if (value === undefined) {
|
||||
// Check if there's a default
|
||||
if (propSchema.default !== undefined) {
|
||||
suggestions.push(`Field '${key}' not provided, will use default: ${JSON.stringify(propSchema.default)}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Type validation
|
||||
if (propSchema.type) {
|
||||
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
||||
if (actualType !== propSchema.type) {
|
||||
errors.push(`${key}: expected ${propSchema.type}, got ${actualType}`);
|
||||
}
|
||||
}
|
||||
// Additional validations for specific types
|
||||
this.validatePropertyValue(key, value, propSchema, errors, warnings);
|
||||
}
|
||||
}
|
||||
// Check for unknown properties
|
||||
if (schema.additionalProperties === false && schema.properties) {
|
||||
const allowedKeys = Object.keys(schema.properties);
|
||||
for (const key of Object.keys(config)) {
|
||||
if (!allowedKeys.includes(key)) {
|
||||
warnings.push(`Unknown property: ${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors: errors.length > 0 ? errors : undefined,
|
||||
warnings: warnings.length > 0 ? warnings : undefined,
|
||||
suggestions: suggestions.length > 0 ? suggestions : undefined
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Validate a property value against its schema
|
||||
*/
|
||||
validatePropertyValue(key, value, schema, errors, warnings) {
|
||||
// Number validations
|
||||
if (schema.type === 'number') {
|
||||
if (schema.minimum !== undefined && value < schema.minimum) {
|
||||
errors.push(`${key}: value ${value} is less than minimum ${schema.minimum}`);
|
||||
}
|
||||
if (schema.maximum !== undefined && value > schema.maximum) {
|
||||
errors.push(`${key}: value ${value} is greater than maximum ${schema.maximum}`);
|
||||
}
|
||||
}
|
||||
// String validations
|
||||
if (schema.type === 'string') {
|
||||
if (schema.minLength !== undefined && value.length < schema.minLength) {
|
||||
errors.push(`${key}: length ${value.length} is less than minimum ${schema.minLength}`);
|
||||
}
|
||||
if (schema.maxLength !== undefined && value.length > schema.maxLength) {
|
||||
errors.push(`${key}: length ${value.length} is greater than maximum ${schema.maxLength}`);
|
||||
}
|
||||
if (schema.pattern) {
|
||||
const regex = new RegExp(schema.pattern);
|
||||
if (!regex.test(value)) {
|
||||
errors.push(`${key}: value does not match pattern ${schema.pattern}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Enum validation
|
||||
if (schema.enum && !schema.enum.includes(value)) {
|
||||
errors.push(`${key}: value '${value}' is not one of allowed values: ${schema.enum.join(', ')}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get environment variables for an augmentation
|
||||
* @param augId Augmentation ID
|
||||
* @returns Map of environment variable names to descriptions
|
||||
*/
|
||||
async getEnvironmentVariables(augId) {
|
||||
const manifest = await this.getManifest(augId);
|
||||
if (!manifest?.configSchema?.properties) {
|
||||
return null;
|
||||
}
|
||||
const prefix = `BRAINY_AUG_${augId.toUpperCase()}_`;
|
||||
const vars = {};
|
||||
for (const [key, prop] of Object.entries(manifest.configSchema.properties)) {
|
||||
const envKey = prefix + key.replace(/([A-Z])/g, '_$1').toUpperCase();
|
||||
vars[envKey] = {
|
||||
configKey: key,
|
||||
description: prop.description,
|
||||
type: prop.type,
|
||||
default: prop.default,
|
||||
required: manifest.configSchema.required?.includes(key),
|
||||
currentValue: typeof process !== 'undefined' ? process.env?.[envKey] : undefined
|
||||
};
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
/**
|
||||
* Get configuration examples for an augmentation
|
||||
* @param augId Augmentation ID
|
||||
* @returns Configuration examples or empty array
|
||||
*/
|
||||
async getConfigExamples(augId) {
|
||||
const manifest = await this.getManifest(augId);
|
||||
return manifest?.configExamples || [];
|
||||
}
|
||||
/**
|
||||
* Check if an augmentation supports configuration
|
||||
* @param augId Augmentation ID
|
||||
* @returns True if augmentation supports configuration
|
||||
*/
|
||||
async supportsConfiguration(augId) {
|
||||
const aug = this.registry.get(augId);
|
||||
return !!(aug && 'getConfig' in aug && 'updateConfig' in aug);
|
||||
}
|
||||
/**
|
||||
* Get augmentations by category
|
||||
* @param category Category to filter by
|
||||
* @returns List of augmentations in the category
|
||||
*/
|
||||
async getByCategory(category) {
|
||||
return this.discover({ category });
|
||||
}
|
||||
/**
|
||||
* Get enabled augmentations
|
||||
* @returns List of enabled augmentations
|
||||
*/
|
||||
async getEnabled() {
|
||||
return this.discover({ enabled: true });
|
||||
}
|
||||
/**
|
||||
* Search augmentations by keyword
|
||||
* @param query Search query
|
||||
* @returns Matching augmentations
|
||||
*/
|
||||
async search(query) {
|
||||
const all = await this.discover();
|
||||
const queryLower = query.toLowerCase();
|
||||
return all.filter(listing => {
|
||||
const manifest = listing.manifest;
|
||||
// Search in various fields
|
||||
const searchFields = [
|
||||
manifest.name,
|
||||
manifest.description,
|
||||
manifest.longDescription,
|
||||
...(manifest.keywords || []),
|
||||
manifest.category
|
||||
].filter(Boolean).map(s => s.toLowerCase());
|
||||
return searchFields.some(field => field.includes(queryLower));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Export configuration for all augmentations
|
||||
* @returns Map of augmentation IDs to configurations
|
||||
*/
|
||||
async exportConfigurations() {
|
||||
const configs = {};
|
||||
const listings = await this.discover({ includeConfig: true });
|
||||
for (const listing of listings) {
|
||||
if (listing.config?.current) {
|
||||
configs[listing.id] = listing.config.current;
|
||||
}
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
/**
|
||||
* Import configurations for multiple augmentations
|
||||
* @param configs Map of augmentation IDs to configurations
|
||||
* @returns Results of import operation
|
||||
*/
|
||||
async importConfigurations(configs) {
|
||||
const results = {};
|
||||
for (const [augId, config] of Object.entries(configs)) {
|
||||
try {
|
||||
// Validate before applying
|
||||
const validation = await this.validateConfig(augId, config);
|
||||
if (!validation.valid) {
|
||||
results[augId] = {
|
||||
success: false,
|
||||
error: `Validation failed: ${validation.errors?.join(', ')}`
|
||||
};
|
||||
continue;
|
||||
}
|
||||
// Apply configuration
|
||||
await this.updateConfig(augId, config);
|
||||
results[augId] = { success: true };
|
||||
}
|
||||
catch (error) {
|
||||
results[augId] = {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
};
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
/**
|
||||
* Generate configuration documentation
|
||||
* @param augId Augmentation ID
|
||||
* @returns Markdown documentation
|
||||
*/
|
||||
async generateConfigDocs(augId) {
|
||||
const manifest = await this.getManifest(augId);
|
||||
if (!manifest)
|
||||
return null;
|
||||
const schema = manifest.configSchema;
|
||||
const examples = manifest.configExamples || [];
|
||||
const envVars = await this.getEnvironmentVariables(augId);
|
||||
let docs = `# ${manifest.name} Configuration\n\n`;
|
||||
docs += `${manifest.description}\n\n`;
|
||||
if (manifest.longDescription) {
|
||||
docs += `## Overview\n\n${manifest.longDescription}\n\n`;
|
||||
}
|
||||
// Configuration options
|
||||
if (schema?.properties) {
|
||||
docs += `## Configuration Options\n\n`;
|
||||
for (const [key, prop] of Object.entries(schema.properties)) {
|
||||
const required = schema.required?.includes(key) ? ' *(required)*' : '';
|
||||
docs += `### \`${key}\`${required}\n\n`;
|
||||
if (prop.description) {
|
||||
docs += `${prop.description}\n\n`;
|
||||
}
|
||||
docs += `- **Type**: ${prop.type}\n`;
|
||||
if (prop.default !== undefined) {
|
||||
docs += `- **Default**: \`${JSON.stringify(prop.default)}\`\n`;
|
||||
}
|
||||
if (prop.minimum !== undefined) {
|
||||
docs += `- **Minimum**: ${prop.minimum}\n`;
|
||||
}
|
||||
if (prop.maximum !== undefined) {
|
||||
docs += `- **Maximum**: ${prop.maximum}\n`;
|
||||
}
|
||||
if (prop.enum) {
|
||||
docs += `- **Allowed values**: ${prop.enum.map(v => `\`${v}\``).join(', ')}\n`;
|
||||
}
|
||||
docs += '\n';
|
||||
}
|
||||
}
|
||||
// Environment variables
|
||||
if (envVars && Object.keys(envVars).length > 0) {
|
||||
docs += `## Environment Variables\n\n`;
|
||||
docs += `| Variable | Config Key | Type | Required | Default |\n`;
|
||||
docs += `|----------|------------|------|----------|----------|\n`;
|
||||
for (const [envKey, info] of Object.entries(envVars)) {
|
||||
docs += `| \`${envKey}\` | ${info.configKey} | ${info.type} | ${info.required ? 'Yes' : 'No'} | ${info.default !== undefined ? `\`${info.default}\`` : '-'} |\n`;
|
||||
}
|
||||
docs += '\n';
|
||||
}
|
||||
// Examples
|
||||
if (examples.length > 0) {
|
||||
docs += `## Examples\n\n`;
|
||||
for (const example of examples) {
|
||||
docs += `### ${example.name}\n\n`;
|
||||
if (example.description) {
|
||||
docs += `${example.description}\n\n`;
|
||||
}
|
||||
docs += '```json\n';
|
||||
docs += JSON.stringify(example.config, null, 2);
|
||||
docs += '\n```\n\n';
|
||||
}
|
||||
}
|
||||
return docs;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=discovery.js.map
|
||||
Loading…
Add table
Add a link
Reference in a new issue