@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.
- package/README.md +63 -0
- package/dist/brainyData.d.ts +40 -0
- package/dist/brainyData.js +76 -0
- package/dist/brainyData.js.map +1 -1
- package/dist/cortex/cli.d.ts +8 -0
- package/dist/cortex/cli.js +207 -0
- package/dist/cortex/cli.js.map +1 -0
- package/dist/cortex/commands/index.d.ts +79 -0
- package/dist/cortex/commands/index.js +614 -0
- package/dist/cortex/commands/index.js.map +1 -0
- package/dist/cortex/config.d.ts +112 -0
- package/dist/cortex/config.js +312 -0
- package/dist/cortex/config.js.map +1 -0
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +17 -0
- package/dist/storage/adapters/s3CompatibleStorage.js +77 -11
- package/dist/storage/adapters/s3CompatibleStorage.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
//
|
|
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 (
|
|
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
|
-
//
|
|
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 -
|
|
1522
|
-
await
|
|
1523
|
-
|
|
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 -
|
|
1527
|
-
await
|
|
1528
|
-
|
|
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 -
|
|
1532
|
-
await
|
|
1597
|
+
// Low error rate - just smart delay (respects throttling status)
|
|
1598
|
+
await this.smartDelay();
|
|
1533
1599
|
}
|
|
1534
1600
|
}
|
|
1535
1601
|
return results;
|