@soulcraft/brainy 0.54.6 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Cortex Configuration Management
3
+ *
4
+ * Handles encrypted configuration storage, environment loading,
5
+ * and distributed coordination through Brainy.
6
+ */
7
+ import { BrainyData } from '../brainyData.js';
8
+ import { StorageConfig } from '../coreTypes.js';
9
+ export interface CortexConfigData {
10
+ version: number;
11
+ storage: StorageConfig;
12
+ encryption?: {
13
+ enabled: boolean;
14
+ keyDerivation: 'pbkdf2' | 'scrypt';
15
+ iterations?: number;
16
+ };
17
+ coordination?: {
18
+ enabled: boolean;
19
+ realtime: boolean;
20
+ pollInterval: number;
21
+ };
22
+ environments?: {
23
+ current: string;
24
+ available: string[];
25
+ };
26
+ }
27
+ export interface EncryptedValue {
28
+ encrypted: true;
29
+ algorithm: string;
30
+ iv: string;
31
+ authTag: string;
32
+ data: string;
33
+ }
34
+ export declare class CortexConfig {
35
+ private static instance;
36
+ private brainy?;
37
+ private config?;
38
+ private configPath;
39
+ private masterKey?;
40
+ private constructor();
41
+ static getInstance(): CortexConfig;
42
+ /**
43
+ * Initialize Cortex configuration
44
+ */
45
+ init(options?: Partial<CortexConfigData>): Promise<void>;
46
+ /**
47
+ * Load existing configuration
48
+ */
49
+ load(): Promise<void>;
50
+ /**
51
+ * Save configuration to disk
52
+ */
53
+ private saveConfig;
54
+ /**
55
+ * Initialize Brainy instance
56
+ */
57
+ private initBrainy;
58
+ /**
59
+ * Initialize or load master encryption key
60
+ */
61
+ private initMasterKey;
62
+ /**
63
+ * Get Brainy instance
64
+ */
65
+ getBrainy(): BrainyData;
66
+ /**
67
+ * Encrypt a value
68
+ */
69
+ encrypt(value: string): EncryptedValue;
70
+ /**
71
+ * Decrypt a value
72
+ */
73
+ decrypt(encryptedValue: EncryptedValue): string;
74
+ /**
75
+ * Set a configuration value in Brainy
76
+ */
77
+ set(key: string, value: any, options?: {
78
+ encrypt?: boolean;
79
+ }): Promise<void>;
80
+ /**
81
+ * Get a configuration value from Brainy
82
+ */
83
+ get(key: string): Promise<any>;
84
+ /**
85
+ * List all configuration keys
86
+ */
87
+ list(): Promise<Array<{
88
+ key: string;
89
+ encrypted: boolean;
90
+ environment: string;
91
+ }>>;
92
+ /**
93
+ * Load all configurations as environment variables
94
+ */
95
+ loadEnvironment(): Promise<Record<string, string>>;
96
+ /**
97
+ * Import configuration from .env file
98
+ */
99
+ importEnv(filePath: string): Promise<void>;
100
+ /**
101
+ * Get storage configuration
102
+ */
103
+ getStorageConfig(): StorageConfig | undefined;
104
+ /**
105
+ * Get current environment
106
+ */
107
+ getCurrentEnvironment(): string;
108
+ /**
109
+ * Switch environment
110
+ */
111
+ switchEnvironment(environment: string): Promise<void>;
112
+ }
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Cortex Configuration Management
3
+ *
4
+ * Handles encrypted configuration storage, environment loading,
5
+ * and distributed coordination through Brainy.
6
+ */
7
+ import { BrainyData } from '../brainyData.js';
8
+ import * as crypto from 'crypto';
9
+ import * as fs from 'fs/promises';
10
+ import * as path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ import { dirname } from 'path';
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+ export class CortexConfig {
16
+ constructor() {
17
+ this.configPath = path.join(process.cwd(), '.brainy', 'cortex.json');
18
+ }
19
+ static getInstance() {
20
+ if (!CortexConfig.instance) {
21
+ CortexConfig.instance = new CortexConfig();
22
+ }
23
+ return CortexConfig.instance;
24
+ }
25
+ /**
26
+ * Initialize Cortex configuration
27
+ */
28
+ async init(options = {}) {
29
+ // Create .brainy directory if it doesn't exist
30
+ const brainyDir = path.dirname(this.configPath);
31
+ await fs.mkdir(brainyDir, { recursive: true });
32
+ // Default configuration
33
+ const defaultConfig = {
34
+ version: 1,
35
+ storage: options.storage || { type: 'memory' },
36
+ encryption: {
37
+ enabled: true,
38
+ keyDerivation: 'pbkdf2',
39
+ iterations: 100000
40
+ },
41
+ coordination: {
42
+ enabled: true,
43
+ realtime: false,
44
+ pollInterval: 30000 // 30 seconds
45
+ },
46
+ environments: {
47
+ current: process.env.NODE_ENV || 'development',
48
+ available: ['development', 'staging', 'production']
49
+ }
50
+ };
51
+ this.config = { ...defaultConfig, ...options };
52
+ // Save configuration
53
+ await this.saveConfig();
54
+ // Initialize Brainy with the configuration
55
+ await this.initBrainy();
56
+ // Generate or load master key
57
+ await this.initMasterKey();
58
+ }
59
+ /**
60
+ * Load existing configuration
61
+ */
62
+ async load() {
63
+ try {
64
+ const configData = await fs.readFile(this.configPath, 'utf-8');
65
+ this.config = JSON.parse(configData);
66
+ await this.initBrainy();
67
+ await this.initMasterKey();
68
+ }
69
+ catch (error) {
70
+ throw new Error(`No Cortex configuration found. Run 'cortex init' first.`);
71
+ }
72
+ }
73
+ /**
74
+ * Save configuration to disk
75
+ */
76
+ async saveConfig() {
77
+ await fs.writeFile(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8');
78
+ }
79
+ /**
80
+ * Initialize Brainy instance
81
+ */
82
+ async initBrainy() {
83
+ if (!this.config) {
84
+ throw new Error('Configuration not loaded');
85
+ }
86
+ this.brainy = new BrainyData({
87
+ storage: this.config.storage,
88
+ writeOnlyMode: false,
89
+ enableMetadataIndexing: true
90
+ });
91
+ await this.brainy.init();
92
+ }
93
+ /**
94
+ * Initialize or load master encryption key
95
+ */
96
+ async initMasterKey() {
97
+ const keyPath = path.join(process.cwd(), '.brainy', 'cortex.key');
98
+ // Try to load from environment first
99
+ if (process.env.CORTEX_MASTER_KEY) {
100
+ this.masterKey = Buffer.from(process.env.CORTEX_MASTER_KEY, 'base64');
101
+ return;
102
+ }
103
+ // Try to load from file
104
+ try {
105
+ const keyData = await fs.readFile(keyPath, 'utf-8');
106
+ this.masterKey = Buffer.from(keyData, 'base64');
107
+ }
108
+ catch (error) {
109
+ // Generate new key
110
+ this.masterKey = crypto.randomBytes(32);
111
+ await fs.writeFile(keyPath, this.masterKey.toString('base64'), 'utf-8');
112
+ // Set restrictive permissions (Unix-like systems)
113
+ try {
114
+ await fs.chmod(keyPath, 0o600);
115
+ }
116
+ catch (e) {
117
+ // Windows doesn't support chmod, ignore
118
+ }
119
+ console.log('🔐 Generated new master key at .brainy/cortex.key');
120
+ console.log('⚠️ Keep this key safe! You\'ll need it to decrypt your configs.');
121
+ }
122
+ }
123
+ /**
124
+ * Get Brainy instance
125
+ */
126
+ getBrainy() {
127
+ if (!this.brainy) {
128
+ throw new Error('Brainy not initialized. Run load() first.');
129
+ }
130
+ return this.brainy;
131
+ }
132
+ /**
133
+ * Encrypt a value
134
+ */
135
+ encrypt(value) {
136
+ if (!this.masterKey) {
137
+ throw new Error('Master key not initialized');
138
+ }
139
+ const algorithm = 'aes-256-gcm';
140
+ const iv = crypto.randomBytes(16);
141
+ const cipher = crypto.createCipheriv(algorithm, this.masterKey, iv);
142
+ let encrypted = cipher.update(value, 'utf8', 'hex');
143
+ encrypted += cipher.final('hex');
144
+ const authTag = cipher.getAuthTag();
145
+ return {
146
+ encrypted: true,
147
+ algorithm,
148
+ iv: iv.toString('hex'),
149
+ authTag: authTag.toString('hex'),
150
+ data: encrypted
151
+ };
152
+ }
153
+ /**
154
+ * Decrypt a value
155
+ */
156
+ decrypt(encryptedValue) {
157
+ if (!this.masterKey) {
158
+ throw new Error('Master key not initialized');
159
+ }
160
+ const decipher = crypto.createDecipheriv(encryptedValue.algorithm, this.masterKey, Buffer.from(encryptedValue.iv, 'hex'));
161
+ decipher.setAuthTag(Buffer.from(encryptedValue.authTag, 'hex'));
162
+ let decrypted = decipher.update(encryptedValue.data, 'hex', 'utf8');
163
+ decrypted += decipher.final('utf8');
164
+ return decrypted;
165
+ }
166
+ /**
167
+ * Set a configuration value in Brainy
168
+ */
169
+ async set(key, value, options = {}) {
170
+ if (!this.brainy) {
171
+ await this.load();
172
+ }
173
+ const configKey = `_cortex/config/${key}`;
174
+ const configValue = options.encrypt && typeof value === 'string'
175
+ ? this.encrypt(value)
176
+ : value;
177
+ await this.brainy.addNoun({
178
+ id: configKey,
179
+ type: 'cortex_config',
180
+ metadata: {
181
+ key,
182
+ value: configValue,
183
+ encrypted: options.encrypt || false,
184
+ environment: this.config?.environments?.current,
185
+ updatedAt: new Date().toISOString()
186
+ }
187
+ });
188
+ }
189
+ /**
190
+ * Get a configuration value from Brainy
191
+ */
192
+ async get(key) {
193
+ if (!this.brainy) {
194
+ await this.load();
195
+ }
196
+ const configKey = `_cortex/config/${key}`;
197
+ try {
198
+ const noun = await this.brainy.getNoun(configKey);
199
+ if (!noun?.metadata?.value) {
200
+ return undefined;
201
+ }
202
+ const value = noun.metadata.value;
203
+ // Decrypt if needed
204
+ if (value.encrypted === true) {
205
+ return this.decrypt(value);
206
+ }
207
+ return value;
208
+ }
209
+ catch (error) {
210
+ return undefined;
211
+ }
212
+ }
213
+ /**
214
+ * List all configuration keys
215
+ */
216
+ async list() {
217
+ if (!this.brainy) {
218
+ await this.load();
219
+ }
220
+ const result = await this.brainy.getNouns({
221
+ filter: { type: 'cortex_config' },
222
+ pagination: { limit: 1000 }
223
+ });
224
+ return result.items.map(noun => ({
225
+ key: noun.metadata?.key || noun.id.replace('_cortex/config/', ''),
226
+ encrypted: noun.metadata?.encrypted || false,
227
+ environment: noun.metadata?.environment || 'default'
228
+ }));
229
+ }
230
+ /**
231
+ * Load all configurations as environment variables
232
+ */
233
+ async loadEnvironment() {
234
+ if (!this.brainy) {
235
+ await this.load();
236
+ }
237
+ const configs = await this.brainy.getNouns({
238
+ filter: {
239
+ type: 'cortex_config',
240
+ 'metadata.environment': this.config?.environments?.current
241
+ },
242
+ pagination: { limit: 1000 }
243
+ });
244
+ const env = {};
245
+ for (const config of configs.items) {
246
+ const key = config.metadata?.key || config.id.replace('_cortex/config/', '');
247
+ let value = config.metadata?.value;
248
+ // Decrypt if needed
249
+ if (value?.encrypted === true) {
250
+ value = this.decrypt(value);
251
+ }
252
+ // Convert to string if needed
253
+ if (typeof value !== 'string') {
254
+ value = JSON.stringify(value);
255
+ }
256
+ // Use the key as-is (could be nested like 'database.url')
257
+ // Or convert to UPPER_SNAKE_CASE
258
+ const envKey = key.toUpperCase().replace(/\./g, '_').replace(/-/g, '_');
259
+ env[envKey] = value;
260
+ }
261
+ return env;
262
+ }
263
+ /**
264
+ * Import configuration from .env file
265
+ */
266
+ async importEnv(filePath) {
267
+ const content = await fs.readFile(filePath, 'utf-8');
268
+ const lines = content.split('\n');
269
+ for (const line of lines) {
270
+ // Skip comments and empty lines
271
+ if (!line.trim() || line.startsWith('#')) {
272
+ continue;
273
+ }
274
+ const [key, ...valueParts] = line.split('=');
275
+ const value = valueParts.join('=').trim();
276
+ if (key && value) {
277
+ // Detect if it looks like a secret
278
+ const isSecret = key.includes('KEY') ||
279
+ key.includes('SECRET') ||
280
+ key.includes('PASSWORD') ||
281
+ key.includes('TOKEN');
282
+ await this.set(key.trim(), value, { encrypt: isSecret });
283
+ }
284
+ }
285
+ }
286
+ /**
287
+ * Get storage configuration
288
+ */
289
+ getStorageConfig() {
290
+ return this.config?.storage;
291
+ }
292
+ /**
293
+ * Get current environment
294
+ */
295
+ getCurrentEnvironment() {
296
+ return this.config?.environments?.current || 'development';
297
+ }
298
+ /**
299
+ * Switch environment
300
+ */
301
+ async switchEnvironment(environment) {
302
+ if (!this.config) {
303
+ await this.load();
304
+ }
305
+ if (!this.config.environments?.available.includes(environment)) {
306
+ throw new Error(`Unknown environment: ${environment}`);
307
+ }
308
+ this.config.environments.current = environment;
309
+ await this.saveConfig();
310
+ }
311
+ }
312
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cortex/config.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,11 @@ export declare class S3CompatibleStorage extends BaseStorage {
57
57
  private systemPrefix;
58
58
  private useDualWrite;
59
59
  protected statisticsCache: StatisticsData | null;
60
+ private throttlingDetected;
61
+ private throttlingBackoffMs;
62
+ private maxBackoffMs;
63
+ private consecutiveThrottleErrors;
64
+ private lastThrottleTime;
60
65
  private lockPrefix;
61
66
  private activeLocks;
62
67
  private changeLogPrefix;
@@ -106,6 +111,18 @@ export declare class S3CompatibleStorage extends BaseStorage {
106
111
  * Initialize the storage adapter
107
112
  */
108
113
  init(): Promise<void>;
114
+ /**
115
+ * Detect and handle storage throttling (429, 503, timeouts)
116
+ */
117
+ private isThrottlingError;
118
+ /**
119
+ * Handle throttling by implementing exponential backoff
120
+ */
121
+ private handleThrottling;
122
+ /**
123
+ * Smart delay based on current throttling status
124
+ */
125
+ private smartDelay;
109
126
  /**
110
127
  * Auto-cleanup legacy /index folder during initialization
111
128
  * This removes old index data that has been migrated to _system
@@ -48,6 +48,12 @@ export class S3CompatibleStorage extends BaseStorage {
48
48
  this.useDualWrite = true; // Write to both locations during migration
49
49
  // Statistics caching for better performance
50
50
  this.statisticsCache = null;
51
+ // Throttling detection and adaptation
52
+ this.throttlingDetected = false;
53
+ this.throttlingBackoffMs = 1000; // Start with 1 second
54
+ this.maxBackoffMs = 30000; // Max 30 seconds
55
+ this.consecutiveThrottleErrors = 0;
56
+ this.lastThrottleTime = 0;
51
57
  // Distributed locking for concurrent access control
52
58
  this.lockPrefix = 'locks/';
53
59
  this.activeLocks = new Set();
@@ -240,6 +246,60 @@ export class S3CompatibleStorage extends BaseStorage {
240
246
  throw new Error(`Failed to initialize ${this.serviceType} storage: ${error}`);
241
247
  }
242
248
  }
249
+ /**
250
+ * Detect and handle storage throttling (429, 503, timeouts)
251
+ */
252
+ isThrottlingError(error) {
253
+ const statusCode = error.$metadata?.httpStatusCode || error.statusCode;
254
+ const message = error.message?.toLowerCase() || '';
255
+ return (statusCode === 429 || // Too Many Requests
256
+ statusCode === 503 || // Service Unavailable / Slow Down
257
+ message.includes('throttl') ||
258
+ message.includes('slow down') ||
259
+ message.includes('rate limit') ||
260
+ message.includes('too many requests'));
261
+ }
262
+ /**
263
+ * Handle throttling by implementing exponential backoff
264
+ */
265
+ async handleThrottling(error) {
266
+ if (this.isThrottlingError(error)) {
267
+ this.throttlingDetected = true;
268
+ this.consecutiveThrottleErrors++;
269
+ this.lastThrottleTime = Date.now();
270
+ // Exponential backoff: 1s, 2s, 4s, 8s, etc. up to maxBackoffMs
271
+ this.throttlingBackoffMs = Math.min(this.throttlingBackoffMs * 2, this.maxBackoffMs);
272
+ prodLog.warn(`🐌 Storage throttling detected (${error.$metadata?.httpStatusCode || 'timeout'}). Backing off for ${this.throttlingBackoffMs}ms`);
273
+ await new Promise(resolve => setTimeout(resolve, this.throttlingBackoffMs));
274
+ }
275
+ else {
276
+ // Reset throttling detection on successful non-throttling operation
277
+ if (this.consecutiveThrottleErrors > 0) {
278
+ this.consecutiveThrottleErrors = 0;
279
+ this.throttlingBackoffMs = 1000; // Reset to initial backoff
280
+ if (this.throttlingDetected) {
281
+ prodLog.info('✅ Storage throttling cleared');
282
+ this.throttlingDetected = false;
283
+ }
284
+ }
285
+ }
286
+ }
287
+ /**
288
+ * Smart delay based on current throttling status
289
+ */
290
+ async smartDelay() {
291
+ if (this.throttlingDetected) {
292
+ // If currently throttled, add a preventive delay
293
+ const timeSinceThrottle = Date.now() - this.lastThrottleTime;
294
+ if (timeSinceThrottle < 60000) { // Within 1 minute of throttling
295
+ await new Promise(resolve => setTimeout(resolve, Math.min(this.throttlingBackoffMs / 2, 5000)));
296
+ }
297
+ }
298
+ else {
299
+ // Normal yield
300
+ await new Promise(resolve => setImmediate(resolve));
301
+ }
302
+ }
243
303
  /**
244
304
  * Auto-cleanup legacy /index folder during initialization
245
305
  * This removes old index data that has been migrated to _system
@@ -1493,9 +1553,13 @@ export class S3CompatibleStorage extends BaseStorage {
1493
1553
  return { id, metadata };
1494
1554
  }
1495
1555
  catch (error) {
1496
- // Enhanced error handling for minor socket issues
1556
+ // Handle throttling and enhanced error handling
1557
+ await this.handleThrottling(error);
1497
1558
  const errorMessage = error instanceof Error ? error.message : String(error);
1498
- if (errorMessage.includes('timeout') || errorMessage.includes('ECONNRESET')) {
1559
+ if (this.isThrottlingError(error)) {
1560
+ // Throttling errors are already logged in handleThrottling
1561
+ }
1562
+ else if (errorMessage.includes('timeout') || errorMessage.includes('ECONNRESET')) {
1499
1563
  this.logger.debug(`⏰ Metadata timeout for ${id} (normal during initial indexing):`, errorMessage);
1500
1564
  }
1501
1565
  else {
@@ -1515,21 +1579,23 @@ export class S3CompatibleStorage extends BaseStorage {
1515
1579
  errorCount++;
1516
1580
  }
1517
1581
  }
1518
- // Adaptive delay based on error rates (helps with S3 rate limiting)
1582
+ // Smart delay based on error rates and throttling status
1519
1583
  const errorRate = errorCount / batch.length;
1520
1584
  if (errorRate > 0.5) {
1521
- // High error rate - add extra delay
1522
- await new Promise(resolve => setTimeout(resolve, 2000)); // 2 second delay
1523
- prodLog.debug(`🐌 High error rate (${(errorRate * 100).toFixed(1)}%) - adding extra delay`);
1585
+ // High error rate - use smart delay with throttling awareness
1586
+ await this.smartDelay();
1587
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Extra delay for high error rates
1588
+ prodLog.debug(`🐌 High error rate (${(errorRate * 100).toFixed(1)}%) - adding smart delay`);
1524
1589
  }
1525
1590
  else if (errorRate > 0.2) {
1526
- // Moderate error rate - add modest delay
1527
- await new Promise(resolve => setTimeout(resolve, 500)); // 500ms delay
1528
- prodLog.debug(`⚡ Moderate error rate (${(errorRate * 100).toFixed(1)}%) - adding modest delay`);
1591
+ // Moderate error rate - smart delay
1592
+ await this.smartDelay();
1593
+ await new Promise(resolve => setTimeout(resolve, 500)); // Modest extra delay
1594
+ prodLog.debug(`⚡ Moderate error rate (${(errorRate * 100).toFixed(1)}%) - adding smart delay`);
1529
1595
  }
1530
1596
  else {
1531
- // Low error rate - normal yield
1532
- await new Promise(resolve => setImmediate(resolve));
1597
+ // Low error rate - just smart delay (respects throttling status)
1598
+ await this.smartDelay();
1533
1599
  }
1534
1600
  }
1535
1601
  return results;