tracelattice 1.2.5
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/LICENSE +24 -0
- package/README.md +112 -0
- package/dist/ServerConfig.d.ts +229 -0
- package/dist/ServerConfig.d.ts.map +1 -0
- package/dist/ServerConfig.js +121 -0
- package/dist/ServerConfig.js.map +1 -0
- package/dist/__tests__/base-registry.test.d.ts +2 -0
- package/dist/__tests__/base-registry.test.d.ts.map +1 -0
- package/dist/__tests__/base-transport-cov.test.d.ts +2 -0
- package/dist/__tests__/base-transport-cov.test.d.ts.map +1 -0
- package/dist/__tests__/base-transport.test.d.ts +2 -0
- package/dist/__tests__/base-transport.test.d.ts.map +1 -0
- package/dist/__tests__/config-loader.test.d.ts +2 -0
- package/dist/__tests__/config-loader.test.d.ts.map +1 -0
- package/dist/__tests__/connection-pool-cov.test.d.ts +2 -0
- package/dist/__tests__/connection-pool-cov.test.d.ts.map +1 -0
- package/dist/__tests__/connection-pool.test.d.ts +2 -0
- package/dist/__tests__/connection-pool.test.d.ts.map +1 -0
- package/dist/__tests__/container.test.d.ts +2 -0
- package/dist/__tests__/container.test.d.ts.map +1 -0
- package/dist/__tests__/crud.test.d.ts +2 -0
- package/dist/__tests__/crud.test.d.ts.map +1 -0
- package/dist/__tests__/discovery-cache.test.d.ts +2 -0
- package/dist/__tests__/discovery-cache.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.d.ts +2 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/factories.test.d.ts +2 -0
- package/dist/__tests__/factories.test.d.ts.map +1 -0
- package/dist/__tests__/health-checker-cov.test.d.ts +2 -0
- package/dist/__tests__/health-checker-cov.test.d.ts.map +1 -0
- package/dist/__tests__/health-checker.test.d.ts +2 -0
- package/dist/__tests__/health-checker.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/factories.d.ts +36 -0
- package/dist/__tests__/helpers/factories.d.ts.map +1 -0
- package/dist/__tests__/helpers/index.d.ts +3 -0
- package/dist/__tests__/helpers/index.d.ts.map +1 -0
- package/dist/__tests__/helpers/timers.d.ts +4 -0
- package/dist/__tests__/helpers/timers.d.ts.map +1 -0
- package/dist/__tests__/history-manager.test.d.ts +2 -0
- package/dist/__tests__/history-manager.test.d.ts.map +1 -0
- package/dist/__tests__/http-helpers-cov.test.d.ts +2 -0
- package/dist/__tests__/http-helpers-cov.test.d.ts.map +1 -0
- package/dist/__tests__/http-transport-cov.test.d.ts +2 -0
- package/dist/__tests__/http-transport-cov.test.d.ts.map +1 -0
- package/dist/__tests__/http-transport.test.d.ts +2 -0
- package/dist/__tests__/http-transport.test.d.ts.map +1 -0
- package/dist/__tests__/input-normalizer.test.d.ts +8 -0
- package/dist/__tests__/input-normalizer.test.d.ts.map +1 -0
- package/dist/__tests__/integration.test.d.ts +2 -0
- package/dist/__tests__/integration.test.d.ts.map +1 -0
- package/dist/__tests__/lib-server.test.d.ts +2 -0
- package/dist/__tests__/lib-server.test.d.ts.map +1 -0
- package/dist/__tests__/memory-persistence.test.d.ts +2 -0
- package/dist/__tests__/memory-persistence.test.d.ts.map +1 -0
- package/dist/__tests__/metrics-integration.test.d.ts +2 -0
- package/dist/__tests__/metrics-integration.test.d.ts.map +1 -0
- package/dist/__tests__/persistence.test.d.ts +2 -0
- package/dist/__tests__/persistence.test.d.ts.map +1 -0
- package/dist/__tests__/reasoning-integration.test.d.ts +11 -0
- package/dist/__tests__/reasoning-integration.test.d.ts.map +1 -0
- package/dist/__tests__/reasoning-types.test.d.ts +2 -0
- package/dist/__tests__/reasoning-types.test.d.ts.map +1 -0
- package/dist/__tests__/request-context.test.d.ts +2 -0
- package/dist/__tests__/request-context.test.d.ts.map +1 -0
- package/dist/__tests__/sanitize.test.d.ts +2 -0
- package/dist/__tests__/sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/schema.test.d.ts +2 -0
- package/dist/__tests__/schema.test.d.ts.map +1 -0
- package/dist/__tests__/sequentialthinking-tools.test.d.ts +2 -0
- package/dist/__tests__/sequentialthinking-tools.test.d.ts.map +1 -0
- package/dist/__tests__/server-config.test.d.ts +2 -0
- package/dist/__tests__/server-config.test.d.ts.map +1 -0
- package/dist/__tests__/skill-discovery.test.d.ts +2 -0
- package/dist/__tests__/skill-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/skill-registry.test.d.ts +2 -0
- package/dist/__tests__/skill-registry.test.d.ts.map +1 -0
- package/dist/__tests__/skill-watcher.test.d.ts +2 -0
- package/dist/__tests__/skill-watcher.test.d.ts.map +1 -0
- package/dist/__tests__/sqlite-persistence.test.d.ts +2 -0
- package/dist/__tests__/sqlite-persistence.test.d.ts.map +1 -0
- package/dist/__tests__/sse-transport-cov.test.d.ts +2 -0
- package/dist/__tests__/sse-transport-cov.test.d.ts.map +1 -0
- package/dist/__tests__/sse-transport.test.d.ts +2 -0
- package/dist/__tests__/sse-transport.test.d.ts.map +1 -0
- package/dist/__tests__/streamable-http-cov.test.d.ts +2 -0
- package/dist/__tests__/streamable-http-cov.test.d.ts.map +1 -0
- package/dist/__tests__/streamable-http-transport.test.d.ts +2 -0
- package/dist/__tests__/streamable-http-transport.test.d.ts.map +1 -0
- package/dist/__tests__/structured-logger.test.d.ts +2 -0
- package/dist/__tests__/structured-logger.test.d.ts.map +1 -0
- package/dist/__tests__/thought-evaluator.test.d.ts +2 -0
- package/dist/__tests__/thought-evaluator.test.d.ts.map +1 -0
- package/dist/__tests__/thought-formatter.test.d.ts +2 -0
- package/dist/__tests__/thought-formatter.test.d.ts.map +1 -0
- package/dist/__tests__/thought-processor.test.d.ts +8 -0
- package/dist/__tests__/thought-processor.test.d.ts.map +1 -0
- package/dist/__tests__/tool-registry-cov.test.d.ts +2 -0
- package/dist/__tests__/tool-registry-cov.test.d.ts.map +1 -0
- package/dist/__tests__/tool-registry.test.d.ts +2 -0
- package/dist/__tests__/tool-registry.test.d.ts.map +1 -0
- package/dist/__tests__/tool-watcher.test.d.ts +2 -0
- package/dist/__tests__/tool-watcher.test.d.ts.map +1 -0
- package/dist/__tests__/worker-manager-cov.test.d.ts +2 -0
- package/dist/__tests__/worker-manager-cov.test.d.ts.map +1 -0
- package/dist/__tests__/worker-manager.test.d.ts +2 -0
- package/dist/__tests__/worker-manager.test.d.ts.map +1 -0
- package/dist/cache/DiscoveryCache.d.ts +269 -0
- package/dist/cache/DiscoveryCache.d.ts.map +1 -0
- package/dist/cache/DiscoveryCache.js +100 -0
- package/dist/cache/DiscoveryCache.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +114 -0
- package/dist/cli.js.map +1 -0
- package/dist/cluster/WorkerManager.d.ts +166 -0
- package/dist/cluster/WorkerManager.d.ts.map +1 -0
- package/dist/cluster/WorkerManager.js +202 -0
- package/dist/cluster/WorkerManager.js.map +1 -0
- package/dist/cluster/worker.d.ts +11 -0
- package/dist/cluster/worker.d.ts.map +1 -0
- package/dist/cluster/worker.js +36 -0
- package/dist/cluster/worker.js.map +1 -0
- package/dist/config/ConfigLoader.d.ts +224 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +85 -0
- package/dist/config/ConfigLoader.js.map +1 -0
- package/dist/context/RequestContext.d.ts +61 -0
- package/dist/context/RequestContext.d.ts.map +1 -0
- package/dist/context/RequestContext.js +17 -0
- package/dist/context/RequestContext.js.map +1 -0
- package/dist/contracts/index.d.ts +10 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/interfaces.d.ts +107 -0
- package/dist/contracts/interfaces.d.ts.map +1 -0
- package/dist/contracts/interfaces.js +1 -0
- package/dist/core/HistoryManager.d.ts +514 -0
- package/dist/core/HistoryManager.d.ts.map +1 -0
- package/dist/core/HistoryManager.js +331 -0
- package/dist/core/HistoryManager.js.map +1 -0
- package/dist/core/IHistoryManager.d.ts +100 -0
- package/dist/core/IHistoryManager.d.ts.map +1 -0
- package/dist/core/IHistoryManager.js +1 -0
- package/dist/core/InputNormalizer.d.ts +139 -0
- package/dist/core/InputNormalizer.d.ts.map +1 -0
- package/dist/core/InputNormalizer.js +101 -0
- package/dist/core/InputNormalizer.js.map +1 -0
- package/dist/core/ThoughtEvaluator.d.ts +127 -0
- package/dist/core/ThoughtEvaluator.d.ts.map +1 -0
- package/dist/core/ThoughtEvaluator.js +346 -0
- package/dist/core/ThoughtEvaluator.js.map +1 -0
- package/dist/core/ThoughtFormatter.d.ts +133 -0
- package/dist/core/ThoughtFormatter.d.ts.map +1 -0
- package/dist/core/ThoughtFormatter.js +70 -0
- package/dist/core/ThoughtFormatter.js.map +1 -0
- package/dist/core/ThoughtProcessor.d.ts +218 -0
- package/dist/core/ThoughtProcessor.d.ts.map +1 -0
- package/dist/core/ThoughtProcessor.js +205 -0
- package/dist/core/ThoughtProcessor.js.map +1 -0
- package/dist/core/reasoning.d.ts +169 -0
- package/dist/core/reasoning.d.ts.map +1 -0
- package/dist/core/reasoning.js +1 -0
- package/dist/core/step.d.ts +45 -0
- package/dist/core/step.d.ts.map +1 -0
- package/dist/core/step.js +1 -0
- package/dist/core/thought.d.ts +190 -0
- package/dist/core/thought.d.ts.map +1 -0
- package/dist/core/thought.js +1 -0
- package/dist/di/Container.d.ts +226 -0
- package/dist/di/Container.d.ts.map +1 -0
- package/dist/di/Container.js +96 -0
- package/dist/di/Container.js.map +1 -0
- package/dist/di/ServiceRegistry.d.ts +32 -0
- package/dist/di/ServiceRegistry.d.ts.map +1 -0
- package/dist/di/ServiceRegistry.js +1 -0
- package/dist/errors.d.ts +482 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +108 -0
- package/dist/errors.js.map +1 -0
- package/dist/health/HealthChecker.d.ts +73 -0
- package/dist/health/HealthChecker.d.ts.map +1 -0
- package/dist/health/HealthChecker.js +69 -0
- package/dist/health/HealthChecker.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/lib.d.ts +205 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +219 -0
- package/dist/lib.js.map +1 -0
- package/dist/logger/NullLogger.d.ts +154 -0
- package/dist/logger/NullLogger.d.ts.map +1 -0
- package/dist/logger/NullLogger.js +24 -0
- package/dist/logger/NullLogger.js.map +1 -0
- package/dist/logger/StructuredLogger.d.ts +327 -0
- package/dist/logger/StructuredLogger.d.ts.map +1 -0
- package/dist/logger/StructuredLogger.js +72 -0
- package/dist/logger/StructuredLogger.js.map +1 -0
- package/dist/metrics/__tests__/metrics.test.d.ts +2 -0
- package/dist/metrics/__tests__/metrics.test.d.ts.map +1 -0
- package/dist/metrics/metrics.impl.d.ts +252 -0
- package/dist/metrics/metrics.impl.d.ts.map +1 -0
- package/dist/metrics/metrics.impl.js +197 -0
- package/dist/metrics/metrics.impl.js.map +1 -0
- package/dist/persistence/FilePersistence.d.ts +66 -0
- package/dist/persistence/FilePersistence.d.ts.map +1 -0
- package/dist/persistence/FilePersistence.js +132 -0
- package/dist/persistence/FilePersistence.js.map +1 -0
- package/dist/persistence/MemoryPersistence.d.ts +68 -0
- package/dist/persistence/MemoryPersistence.d.ts.map +1 -0
- package/dist/persistence/MemoryPersistence.js +51 -0
- package/dist/persistence/MemoryPersistence.js.map +1 -0
- package/dist/persistence/PersistenceBackend.d.ts +69 -0
- package/dist/persistence/PersistenceBackend.d.ts.map +1 -0
- package/dist/persistence/PersistenceBackend.js +1 -0
- package/dist/persistence/PersistenceFactory.d.ts +21 -0
- package/dist/persistence/PersistenceFactory.d.ts.map +1 -0
- package/dist/persistence/PersistenceFactory.js +25 -0
- package/dist/persistence/PersistenceFactory.js.map +1 -0
- package/dist/persistence/SqlitePersistence.d.ts +60 -0
- package/dist/persistence/SqlitePersistence.d.ts.map +1 -0
- package/dist/persistence/SqlitePersistence.js +136 -0
- package/dist/persistence/SqlitePersistence.js.map +1 -0
- package/dist/pool/ConnectionPool.d.ts +215 -0
- package/dist/pool/ConnectionPool.d.ts.map +1 -0
- package/dist/pool/ConnectionPool.js +187 -0
- package/dist/pool/ConnectionPool.js.map +1 -0
- package/dist/registry/BaseRegistry.d.ts +203 -0
- package/dist/registry/BaseRegistry.d.ts.map +1 -0
- package/dist/registry/BaseRegistry.js +165 -0
- package/dist/registry/BaseRegistry.js.map +1 -0
- package/dist/registry/SkillRegistry.d.ts +69 -0
- package/dist/registry/SkillRegistry.d.ts.map +1 -0
- package/dist/registry/SkillRegistry.js +88 -0
- package/dist/registry/SkillRegistry.js.map +1 -0
- package/dist/registry/ToolRegistry.d.ts +69 -0
- package/dist/registry/ToolRegistry.d.ts.map +1 -0
- package/dist/registry/ToolRegistry.js +93 -0
- package/dist/registry/ToolRegistry.js.map +1 -0
- package/dist/sanitize.d.ts +63 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +14 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/schema.d.ts +531 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +204 -0
- package/dist/schema.js.map +1 -0
- package/dist/telemetry/Telemetry.d.ts +36 -0
- package/dist/telemetry/Telemetry.d.ts.map +1 -0
- package/dist/telemetry/Telemetry.js +68 -0
- package/dist/telemetry/Telemetry.js.map +1 -0
- package/dist/telemetry/__tests__/Telemetry.test.d.ts +2 -0
- package/dist/telemetry/__tests__/Telemetry.test.d.ts.map +1 -0
- package/dist/transport/BaseTransport.d.ts +184 -0
- package/dist/transport/BaseTransport.d.ts.map +1 -0
- package/dist/transport/BaseTransport.js +200 -0
- package/dist/transport/BaseTransport.js.map +1 -0
- package/dist/transport/HttpHelpers.d.ts +60 -0
- package/dist/transport/HttpHelpers.d.ts.map +1 -0
- package/dist/transport/HttpHelpers.js +50 -0
- package/dist/transport/HttpHelpers.js.map +1 -0
- package/dist/transport/HttpTransport.d.ts +134 -0
- package/dist/transport/HttpTransport.d.ts.map +1 -0
- package/dist/transport/HttpTransport.js +175 -0
- package/dist/transport/HttpTransport.js.map +1 -0
- package/dist/transport/SseTransport.d.ts +133 -0
- package/dist/transport/SseTransport.d.ts.map +1 -0
- package/dist/transport/SseTransport.js +318 -0
- package/dist/transport/SseTransport.js.map +1 -0
- package/dist/transport/StreamableHttpTransport.d.ts +224 -0
- package/dist/transport/StreamableHttpTransport.d.ts.map +1 -0
- package/dist/transport/StreamableHttpTransport.js +407 -0
- package/dist/transport/StreamableHttpTransport.js.map +1 -0
- package/dist/types/disposable.d.ts +22 -0
- package/dist/types/disposable.d.ts.map +1 -0
- package/dist/types/disposable.js +1 -0
- package/dist/types/server-config.d.ts +32 -0
- package/dist/types/server-config.d.ts.map +1 -0
- package/dist/types/server-config.js +1 -0
- package/dist/types/skill.d.ts +69 -0
- package/dist/types/skill.d.ts.map +1 -0
- package/dist/types/skill.js +1 -0
- package/dist/types/tool.d.ts +68 -0
- package/dist/types/tool.d.ts.map +1 -0
- package/dist/types/tool.js +1 -0
- package/dist/watchers/SkillWatcher.d.ts +132 -0
- package/dist/watchers/SkillWatcher.d.ts.map +1 -0
- package/dist/watchers/SkillWatcher.js +73 -0
- package/dist/watchers/SkillWatcher.js.map +1 -0
- package/dist/watchers/ToolWatcher.d.ts +109 -0
- package/dist/watchers/ToolWatcher.d.ts.map +1 -0
- package/dist/watchers/ToolWatcher.js +71 -0
- package/dist/watchers/ToolWatcher.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { cpus } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { Worker } from "node:worker_threads";
|
|
6
|
+
class WorkerManager {
|
|
7
|
+
_workers = new Map();
|
|
8
|
+
_nextWorkerId = 0;
|
|
9
|
+
_maxWorkers;
|
|
10
|
+
_workerScript;
|
|
11
|
+
_workerTimeout;
|
|
12
|
+
_enableHealthCheck;
|
|
13
|
+
_healthCheckInterval;
|
|
14
|
+
_maxRetries;
|
|
15
|
+
_activeRequests = new Map();
|
|
16
|
+
_workerRetryCount = new Map();
|
|
17
|
+
_healthCheckTimer = null;
|
|
18
|
+
_nextWorkerIndex = 0;
|
|
19
|
+
_terminated = false;
|
|
20
|
+
_logger;
|
|
21
|
+
constructor(options = {}){
|
|
22
|
+
this._maxWorkers = options.maxWorkers ?? cpus().length;
|
|
23
|
+
this._workerScript = options.workerScript ?? join(dirname(fileURLToPath(import.meta.url)), 'worker.js');
|
|
24
|
+
this._workerTimeout = options.workerTimeout ?? 30000;
|
|
25
|
+
this._enableHealthCheck = options.enableHealthCheck ?? true;
|
|
26
|
+
this._healthCheckInterval = options.healthCheckInterval ?? 60000;
|
|
27
|
+
this._maxRetries = options.maxRetries ?? 3;
|
|
28
|
+
this._logger = options.logger ?? this._createNoopLogger();
|
|
29
|
+
}
|
|
30
|
+
_createNoopLogger() {
|
|
31
|
+
return {
|
|
32
|
+
info: ()=>{},
|
|
33
|
+
warn: ()=>{},
|
|
34
|
+
error: ()=>{},
|
|
35
|
+
debug: ()=>{},
|
|
36
|
+
setLevel: ()=>{},
|
|
37
|
+
getLevel: ()=>'info'
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async start() {
|
|
41
|
+
if (this._terminated) throw new Error('WorkerManager has been terminated');
|
|
42
|
+
if (!existsSync(this._workerScript)) throw new Error(`Worker script not found: ${this._workerScript}`);
|
|
43
|
+
for(let i = 0; i < this._maxWorkers; i++)await this._spawnWorker();
|
|
44
|
+
if (this._enableHealthCheck) this._startHealthCheck();
|
|
45
|
+
this._logger.info(`WorkerManager started with ${this._workers.size} workers`);
|
|
46
|
+
}
|
|
47
|
+
async _spawnWorker(reuseId) {
|
|
48
|
+
const workerId = void 0 !== reuseId ? reuseId : this._nextWorkerId++;
|
|
49
|
+
const worker = new Worker(this._workerScript, {
|
|
50
|
+
resourceLimits: {
|
|
51
|
+
maxOldGenerationSizeMb: 100
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
worker.on('online', ()=>{
|
|
55
|
+
this._logger.info(`Worker ${workerId} is online`);
|
|
56
|
+
this._workerRetryCount.delete(workerId);
|
|
57
|
+
});
|
|
58
|
+
worker.on('message', (message)=>{
|
|
59
|
+
this._handleWorkerMessage(workerId, message);
|
|
60
|
+
});
|
|
61
|
+
worker.on('error', ()=>{
|
|
62
|
+
this._logger.error(`Worker ${workerId} error`);
|
|
63
|
+
this._handleWorkerError(workerId);
|
|
64
|
+
});
|
|
65
|
+
worker.on('exit', (code)=>{
|
|
66
|
+
this._logger.info(`Worker ${workerId} exited with code ${code}`);
|
|
67
|
+
this._handleWorkerExit(workerId, code);
|
|
68
|
+
});
|
|
69
|
+
this._workers.set(workerId, worker);
|
|
70
|
+
}
|
|
71
|
+
_handleWorkerMessage(workerId, message) {
|
|
72
|
+
if ('result' === message.type && message.requestId) {
|
|
73
|
+
const callback = this._activeRequests.get(message.requestId);
|
|
74
|
+
if (callback) {
|
|
75
|
+
callback(message.result);
|
|
76
|
+
this._activeRequests.delete(message.requestId);
|
|
77
|
+
}
|
|
78
|
+
} else if ('error' === message.type && message.requestId) {
|
|
79
|
+
const callback = this._activeRequests.get(message.requestId);
|
|
80
|
+
if (callback) {
|
|
81
|
+
callback(new Error(message.error || 'Unknown error'));
|
|
82
|
+
this._activeRequests.delete(message.requestId);
|
|
83
|
+
}
|
|
84
|
+
} else if ('health' === message.type) this._workerRetryCount.delete(workerId);
|
|
85
|
+
}
|
|
86
|
+
_handleWorkerError(workerId) {
|
|
87
|
+
const retryCount = this._workerRetryCount.get(workerId) || 0;
|
|
88
|
+
if (retryCount < this._maxRetries) {
|
|
89
|
+
this._workerRetryCount.set(workerId, retryCount + 1);
|
|
90
|
+
this._logger.info(`Restarting worker ${workerId} (attempt ${retryCount + 1}/${this._maxRetries})`);
|
|
91
|
+
const worker = this._workers.get(workerId);
|
|
92
|
+
if (worker) {
|
|
93
|
+
try {
|
|
94
|
+
worker.terminate();
|
|
95
|
+
} catch {}
|
|
96
|
+
this._workers.delete(workerId);
|
|
97
|
+
}
|
|
98
|
+
setTimeout(()=>{
|
|
99
|
+
if (!this._terminated) this._spawnWorker(workerId).catch((spawnErr)=>{
|
|
100
|
+
this._logger.error(`Failed to restart worker ${workerId}`, {
|
|
101
|
+
error: spawnErr
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}, 1000 * (retryCount + 1));
|
|
105
|
+
} else {
|
|
106
|
+
this._logger.error(`Worker ${workerId} exceeded max retries, removing from pool`);
|
|
107
|
+
const worker = this._workers.get(workerId);
|
|
108
|
+
if (worker) {
|
|
109
|
+
try {
|
|
110
|
+
worker.terminate();
|
|
111
|
+
} catch {}
|
|
112
|
+
this._workers.delete(workerId);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
_handleWorkerExit(workerId, code) {
|
|
117
|
+
this._workers.delete(workerId);
|
|
118
|
+
if (!this._terminated && 0 !== code) {
|
|
119
|
+
this._logger.info(`Spawning replacement worker ${workerId}`);
|
|
120
|
+
this._spawnWorker(workerId).catch((err)=>{
|
|
121
|
+
this._logger.error("Failed to spawn replacement worker", err);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
_startHealthCheck() {
|
|
126
|
+
this._healthCheckTimer = setInterval(()=>{
|
|
127
|
+
for (const [, worker] of this._workers.entries())try {
|
|
128
|
+
worker.postMessage({
|
|
129
|
+
type: 'health-check'
|
|
130
|
+
});
|
|
131
|
+
} catch {}
|
|
132
|
+
}, this._healthCheckInterval);
|
|
133
|
+
}
|
|
134
|
+
async processThought(input) {
|
|
135
|
+
if (this._terminated) throw new Error('WorkerManager has been terminated');
|
|
136
|
+
if (0 === this._workers.size) throw new Error('No workers available');
|
|
137
|
+
const workerIds = Array.from(this._workers.keys());
|
|
138
|
+
const index = this._nextWorkerIndex % workerIds.length;
|
|
139
|
+
const workerId = workerIds[index];
|
|
140
|
+
const worker = this._workers.get(workerId);
|
|
141
|
+
if (!worker) throw new Error(`Worker ${workerId} not available`);
|
|
142
|
+
this._nextWorkerIndex = (this._nextWorkerIndex + 1) % workerIds.length;
|
|
143
|
+
const requestId = `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
144
|
+
return new Promise((resolve, reject)=>{
|
|
145
|
+
const timeout = setTimeout(()=>{
|
|
146
|
+
this._activeRequests.delete(requestId);
|
|
147
|
+
reject(new Error(`Worker timeout after ${this._workerTimeout}ms`));
|
|
148
|
+
}, this._workerTimeout);
|
|
149
|
+
this._activeRequests.set(requestId, (result)=>{
|
|
150
|
+
clearTimeout(timeout);
|
|
151
|
+
if (result instanceof Error) reject(result);
|
|
152
|
+
else resolve(result);
|
|
153
|
+
});
|
|
154
|
+
try {
|
|
155
|
+
worker.postMessage({
|
|
156
|
+
type: 'process-thought',
|
|
157
|
+
requestId,
|
|
158
|
+
input
|
|
159
|
+
});
|
|
160
|
+
} catch (error) {
|
|
161
|
+
clearTimeout(timeout);
|
|
162
|
+
this._activeRequests.delete(requestId);
|
|
163
|
+
reject(error);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
getStats() {
|
|
168
|
+
return {
|
|
169
|
+
activeWorkers: this._workers.size,
|
|
170
|
+
activeRequests: this._activeRequests.size,
|
|
171
|
+
maxWorkers: this._maxWorkers,
|
|
172
|
+
healthCheckEnabled: this._enableHealthCheck
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async terminate() {
|
|
176
|
+
if (this._terminated) return;
|
|
177
|
+
this._terminated = true;
|
|
178
|
+
if (this._healthCheckTimer) {
|
|
179
|
+
clearInterval(this._healthCheckTimer);
|
|
180
|
+
this._healthCheckTimer = null;
|
|
181
|
+
}
|
|
182
|
+
const terminatePromises = Array.from(this._workers.values()).map((worker)=>new Promise((resolve)=>{
|
|
183
|
+
worker.terminate().then(()=>resolve()).catch(()=>resolve());
|
|
184
|
+
}));
|
|
185
|
+
await Promise.all(terminatePromises);
|
|
186
|
+
this._workers.clear();
|
|
187
|
+
this._activeRequests.clear();
|
|
188
|
+
this._logger.info('WorkerManager terminated');
|
|
189
|
+
}
|
|
190
|
+
async dispose() {
|
|
191
|
+
await this.terminate();
|
|
192
|
+
}
|
|
193
|
+
isRunning() {
|
|
194
|
+
return !this._terminated && this._workers.size > 0;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function createWorkerManager(options) {
|
|
198
|
+
return new WorkerManager(options);
|
|
199
|
+
}
|
|
200
|
+
export { WorkerManager, createWorkerManager };
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=WorkerManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster/WorkerManager.js","sources":["../../src/cluster/WorkerManager.ts"],"sourcesContent":["/**\n * Multi-process Architecture for parallel thought processing.\n *\n * This module provides a WorkerManager that uses Node.js worker threads\n * to distribute thought processing across multiple CPU cores, enabling\n * horizontal scaling and improved performance.\n *\n * @example\n * ```typescript\n * const manager = new WorkerManager({\n * maxWorkers: 4,\n * workerScript: './dist/worker.js'\n * });\n * await manager.start();\n *\n * const result = await manager.processThought({ thought: 'test', thought_number: 1, total_thoughts: 1 });\n * ```\n */\n\nimport { existsSync } from 'node:fs';\nimport { cpus } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { Worker } from 'node:worker_threads';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { IDisposable } from '../types/disposable.js';\n\nexport interface WorkerManagerOptions {\n\t/**\n\t * Maximum number of worker processes to spawn\n\t * @default Number of CPU cores\n\t */\n\tmaxWorkers?: number;\n\n\t/**\n\t * Path to the worker script\n\t * @default './dist/worker.js'\n\t */\n\tworkerScript?: string;\n\n\t/**\n\t * Timeout for worker responses in milliseconds\n\t * @default 30000 (30 seconds)\n\t */\n\tworkerTimeout?: number;\n\n\t/**\n\t * Enable worker health monitoring and auto-restart\n\t * @default true\n\t */\n\tenableHealthCheck?: boolean;\n\n\t/**\n\t * Health check interval in milliseconds\n\t * @default 60000 (1 minute)\n\t */\n\thealthCheckInterval?: number;\n\n\t/**\n\t * Maximum number of retries for a failed worker\n\t * @default 3\n\t */\n\tmaxRetries?: number;\n\n\t/**\n\t * Logger instance\n\t */\n\tlogger?: Logger;\n}\n\nexport interface WorkerMessage {\n\ttype: 'process-thought' | 'health-check' | 'terminate';\n\trequestId?: string;\n\tinput?: unknown;\n}\n\nexport interface WorkerResponse {\n\ttype: 'result' | 'error' | 'health';\n\trequestId?: string;\n\tresult?: unknown;\n\terror?: string;\n}\n\n/**\n * WorkerManager manages a pool of worker processes for parallel thought processing.\n *\n * Each worker runs in a separate process and can process thoughts independently.\n * The manager distributes incoming requests across available workers.\n */\nexport class WorkerManager implements IDisposable {\n\tprivate _workers: Map<number, Worker> = new Map();\n\tprivate _nextWorkerId = 0;\n\tprivate _maxWorkers: number;\n\tprivate _workerScript: string;\n\tprivate _workerTimeout: number;\n\tprivate _enableHealthCheck: boolean;\n\tprivate _healthCheckInterval: number;\n\tprivate _maxRetries: number;\n\tprivate _activeRequests: Map<string, (result: unknown) => void> = new Map();\n\tprivate _workerRetryCount: Map<number, number> = new Map();\n\tprivate _healthCheckTimer: NodeJS.Timeout | null = null;\n\tprivate _nextWorkerIndex = 0;\n\n\tprivate _terminated = false;\n\tprivate _logger: Logger;\n\n\tconstructor(options: WorkerManagerOptions = {}) {\n\t\tthis._maxWorkers = options.maxWorkers ?? cpus().length;\n\t\tthis._workerScript =\n\t\t\toptions.workerScript ?? join(dirname(fileURLToPath(import.meta.url)), 'worker.js');\n\t\tthis._workerTimeout = options.workerTimeout ?? 30000;\n\t\tthis._enableHealthCheck = options.enableHealthCheck ?? true;\n\t\tthis._healthCheckInterval = options.healthCheckInterval ?? 60000;\n\t\tthis._maxRetries = options.maxRetries ?? 3;\n\t\tthis._logger = options.logger ?? this._createNoopLogger();\n\t}\n\n\t/**\n\t * Create a no-op logger when none is provided.\n\t */\n\tprivate _createNoopLogger(): Logger {\n\t\treturn {\n\t\t\tinfo: () => {},\n\t\t\twarn: () => {},\n\t\t\terror: () => {},\n\t\t\tdebug: () => {},\n\t\t\tsetLevel: () => {},\n\t\t\tgetLevel: () => 'info',\n\t\t};\n\t}\n\n\t/**\n\t * Start the worker manager and spawn all worker processes.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this._terminated) {\n\t\t\tthrow new Error('WorkerManager has been terminated');\n\t\t}\n\n\t\t// Verify worker script exists\n\t\tif (!existsSync(this._workerScript)) {\n\t\t\tthrow new Error(`Worker script not found: ${this._workerScript}`);\n\t\t}\n\n\t\t// Spawn workers\n\t\tfor (let i = 0; i < this._maxWorkers; i++) {\n\t\t\tawait this._spawnWorker();\n\t\t}\n\n\t\t// Start health check if enabled\n\t\tif (this._enableHealthCheck) {\n\t\t\tthis._startHealthCheck();\n\t\t}\n\n\t\tthis._logger.info(`WorkerManager started with ${this._workers.size} workers`);\n\t}\n\n\t/**\n\t * Spawn a single worker process.\n\t */\n\tprivate async _spawnWorker(reuseId?: number): Promise<void> {\n\t\tconst workerId = reuseId !== undefined ? reuseId : this._nextWorkerId++;\n\t\tconst worker = new Worker(this._workerScript, {\n\t\t\tresourceLimits: {\n\t\t\t\tmaxOldGenerationSizeMb: 100, // Limit memory usage\n\t\t\t},\n\t\t});\n\n\t\tworker.on('online', () => {\n\t\t\tthis._logger.info(`Worker ${workerId} is online`);\n\t\t\tthis._workerRetryCount.delete(workerId);\n\t\t});\n\n\t\tworker.on('message', (message: WorkerResponse) => {\n\t\t\tthis._handleWorkerMessage(workerId, message);\n\t\t});\n\n\t\tworker.on('error', () => {\n\t\t\tthis._logger.error(`Worker ${workerId} error`);\n\t\t\tthis._handleWorkerError(workerId);\n\t\t});\n\n\t\tworker.on('exit', (code) => {\n\t\t\tthis._logger.info(`Worker ${workerId} exited with code ${code}`);\n\t\t\tthis._handleWorkerExit(workerId, code);\n\t\t});\n\n\t\tthis._workers.set(workerId, worker);\n\t}\n\n\t/**\n\t * Handle incoming messages from workers.\n\t */\n\tprivate _handleWorkerMessage(workerId: number, message: WorkerResponse): void {\n\t\tif (message.type === 'result' && message.requestId) {\n\t\t\tconst callback = this._activeRequests.get(message.requestId);\n\t\t\tif (callback) {\n\t\t\t\tcallback(message.result);\n\t\t\t\tthis._activeRequests.delete(message.requestId);\n\t\t\t}\n\t\t} else if (message.type === 'error' && message.requestId) {\n\t\t\tconst callback = this._activeRequests.get(message.requestId);\n\t\t\tif (callback) {\n\t\t\t\tcallback(new Error(message.error || 'Unknown error'));\n\t\t\t\tthis._activeRequests.delete(message.requestId);\n\t\t\t}\n\t\t} else if (message.type === 'health') {\n\t\t\t// Health check response - worker is alive\n\t\t\tthis._workerRetryCount.delete(workerId);\n\t\t}\n\t}\n\n\t/**\n\t * Handle worker errors.\n\t */\n\tprivate _handleWorkerError(workerId: number): void {\n\t\tconst retryCount = this._workerRetryCount.get(workerId) || 0;\n\n\t\tif (retryCount < this._maxRetries) {\n\t\t\tthis._workerRetryCount.set(workerId, retryCount + 1);\n\t\t\tthis._logger.info(\n\t\t\t\t`Restarting worker ${workerId} (attempt ${retryCount + 1}/${this._maxRetries})`\n\t\t\t);\n\n\t\t\t// Remove failed worker\n\t\t\tconst worker = this._workers.get(workerId);\n\t\t\tif (worker) {\n\t\t\t\ttry {\n\t\t\t\t\tworker.terminate();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore\n\t\t\t\t}\n\t\t\t\tthis._workers.delete(workerId);\n\t\t\t}\n\n\t\t\t// Spawn new worker after delay\n\t\t\tsetTimeout(\n\t\t\t\t() => {\n\t\t\t\t\tif (!this._terminated) {\n\t\t\t\t\t\tthis._spawnWorker(workerId).catch((spawnErr) => {\n\t\t\t\t\t\t\tthis._logger.error(`Failed to restart worker ${workerId}`, { error: spawnErr });\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t1000 * (retryCount + 1)\n\t\t\t);\n\t\t} else {\n\t\t\tthis._logger.error(`Worker ${workerId} exceeded max retries, removing from pool`);\n\t\t\tconst worker = this._workers.get(workerId);\n\t\t\tif (worker) {\n\t\t\t\ttry {\n\t\t\t\t\tworker.terminate();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore\n\t\t\t\t}\n\t\t\t\tthis._workers.delete(workerId);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Handle worker exit.\n\t */\n\tprivate _handleWorkerExit(workerId: number, code: number): void {\n\t\tthis._workers.delete(workerId);\n\n\t\t// Spawn replacement worker if not terminated\n\t\tif (!this._terminated && code !== 0) {\n\t\t\tthis._logger.info(`Spawning replacement worker ${workerId}`);\n\t\t\tthis._spawnWorker(workerId).catch((err) => {\n\t\t\t\tthis._logger.error(`Failed to spawn replacement worker`, err);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Start periodic health checks for all workers.\n\t */\n\tprivate _startHealthCheck(): void {\n\t\tthis._healthCheckTimer = setInterval(() => {\n\t\t\tfor (const [, worker] of this._workers.entries()) {\n\t\t\t\ttry {\n\t\t\t\t\tworker.postMessage({ type: 'health-check' });\n\t\t\t\t} catch {\n\t\t\t\t\t// Worker is dead, will be handled by exit event\n\t\t\t\t}\n\t\t\t}\n\t\t}, this._healthCheckInterval);\n\t}\n\n\t/**\n\t * Process a thought using an available worker.\n\t *\n\t * @param input - The thought data to process\n\t * @returns Promise with the processing result\n\t */\n\tasync processThought(input: ThoughtData): Promise<unknown> {\n\t\tif (this._terminated) {\n\t\t\tthrow new Error('WorkerManager has been terminated');\n\t\t}\n\n\t\tif (this._workers.size === 0) {\n\t\t\tthrow new Error('No workers available');\n\t\t}\n\n\t\t// Get next available worker (round-robin over Map keys)\n\t\tconst workerIds = Array.from(this._workers.keys());\n\t\tconst index = this._nextWorkerIndex % workerIds.length;\n\t\tconst workerId = workerIds[index]!;\n\t\tconst worker = this._workers.get(workerId);\n\n\t\tif (!worker) {\n\t\t\tthrow new Error(`Worker ${workerId} not available`);\n\t\t}\n\n\t\t// Increment next worker index (round-robin)\n\t\tthis._nextWorkerIndex = (this._nextWorkerIndex + 1) % workerIds.length;\n\n\t\t// Generate unique request ID\n\t\tconst requestId = `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\t// Set timeout\n\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\tthis._activeRequests.delete(requestId);\n\t\t\t\treject(new Error(`Worker timeout after ${this._workerTimeout}ms`));\n\t\t\t}, this._workerTimeout);\n\n\t\t\t// Store callback\n\t\t\tthis._activeRequests.set(requestId, (result) => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tif (result instanceof Error) {\n\t\t\t\t\treject(result);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(result);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Send message to worker\n\t\t\ttry {\n\t\t\t\tworker.postMessage({\n\t\t\t\t\ttype: 'process-thought',\n\t\t\t\t\trequestId,\n\t\t\t\t\tinput,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tthis._activeRequests.delete(requestId);\n\t\t\t\treject(error);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Get statistics about the worker pool.\n\t */\n\tgetStats(): {\n\t\tactiveWorkers: number;\n\t\tactiveRequests: number;\n\t\tmaxWorkers: number;\n\t\thealthCheckEnabled: boolean;\n\t} {\n\t\treturn {\n\t\t\tactiveWorkers: this._workers.size,\n\t\t\tactiveRequests: this._activeRequests.size,\n\t\t\tmaxWorkers: this._maxWorkers,\n\t\t\thealthCheckEnabled: this._enableHealthCheck,\n\t\t};\n\t}\n\n\t/**\n\t * Terminate all workers and stop the health check.\n\t */\n\tasync terminate(): Promise<void> {\n\t\tif (this._terminated) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._terminated = true;\n\n\t\t// Stop health check\n\t\tif (this._healthCheckTimer) {\n\t\t\tclearInterval(this._healthCheckTimer);\n\t\t\tthis._healthCheckTimer = null;\n\t\t}\n\n\t\t// Terminate all workers\n\t\tconst terminatePromises = Array.from(this._workers.values()).map((worker) => {\n\t\t\treturn new Promise<void>((resolve) => {\n\t\t\t\tworker\n\t\t\t\t\t.terminate()\n\t\t\t\t\t.then(() => resolve())\n\t\t\t\t\t.catch(() => resolve());\n\t\t\t});\n\t\t});\n\n\t\tawait Promise.all(terminatePromises);\n\t\tthis._workers.clear();\n\t\tthis._activeRequests.clear();\n\n\t\tthis._logger.info('WorkerManager terminated');\n\t}\n\n\t/**\n\t * Dispose of the worker manager, releasing all resources.\n\t * Implements the IDisposable interface.\n\t * Delegates to terminate() for backward compatibility.\n\t */\n\tasync dispose(): Promise<void> {\n\t\tawait this.terminate();\n\t}\n\n\t/**\n\t * Check if worker manager is running.\n\t */\n\tisRunning(): boolean {\n\t\treturn !this._terminated && this._workers.size > 0;\n\t}\n}\n\n/**\n * Create a WorkerManager with the given options.\n *\n * @param options - Worker manager configuration\n * @returns A configured WorkerManager\n *\n * @example\n * ```typescript\n * const manager = createWorkerManager({\n * maxWorkers: 4,\n * workerScript: './dist/worker.js'\n * });\n * await manager.start();\n * ```\n */\nexport function createWorkerManager(options?: WorkerManagerOptions): WorkerManager {\n\treturn new WorkerManager(options);\n}\n"],"names":["WorkerManager","Map","options","cpus","join","dirname","fileURLToPath","Error","existsSync","i","reuseId","workerId","undefined","worker","Worker","message","code","callback","retryCount","setTimeout","spawnErr","err","setInterval","input","workerIds","Array","index","requestId","Date","Math","Promise","resolve","reject","timeout","result","clearTimeout","error","clearInterval","terminatePromises","createWorkerManager"],"mappings":";;;;;AA0FO,MAAMA;IACJ,WAAgC,IAAIC,MAAM;IAC1C,gBAAgB,EAAE;IAClB,YAAoB;IACpB,cAAsB;IACtB,eAAuB;IACvB,mBAA4B;IAC5B,qBAA6B;IAC7B,YAAoB;IACpB,kBAA0D,IAAIA,MAAM;IACpE,oBAAyC,IAAIA,MAAM;IACnD,oBAA2C,KAAK;IAChD,mBAAmB,EAAE;IAErB,cAAc,MAAM;IACpB,QAAgB;IAExB,YAAYC,UAAgC,CAAC,CAAC,CAAE;QAC/C,IAAI,CAAC,WAAW,GAAGA,QAAQ,UAAU,IAAIC,OAAO,MAAM;QACtD,IAAI,CAAC,aAAa,GACjBD,QAAQ,YAAY,IAAIE,KAAKC,QAAQC,cAAc,YAAY,GAAG,IAAI;QACvE,IAAI,CAAC,cAAc,GAAGJ,QAAQ,aAAa,IAAI;QAC/C,IAAI,CAAC,kBAAkB,GAAGA,QAAQ,iBAAiB,IAAI;QACvD,IAAI,CAAC,oBAAoB,GAAGA,QAAQ,mBAAmB,IAAI;QAC3D,IAAI,CAAC,WAAW,GAAGA,QAAQ,UAAU,IAAI;QACzC,IAAI,CAAC,OAAO,GAAGA,QAAQ,MAAM,IAAI,IAAI,CAAC,iBAAiB;IACxD;IAKQ,oBAA4B;QACnC,OAAO;YACN,MAAM,KAAO;YACb,MAAM,KAAO;YACb,OAAO,KAAO;YACd,OAAO,KAAO;YACd,UAAU,KAAO;YACjB,UAAU,IAAM;QACjB;IACD;IAKA,MAAM,QAAuB;QAC5B,IAAI,IAAI,CAAC,WAAW,EACnB,MAAM,IAAIK,MAAM;QAIjB,IAAI,CAACC,WAAW,IAAI,CAAC,aAAa,GACjC,MAAM,IAAID,MAAM,CAAC,yBAAyB,EAAE,IAAI,CAAC,aAAa,EAAE;QAIjE,IAAK,IAAIE,IAAI,GAAGA,IAAI,IAAI,CAAC,WAAW,EAAEA,IACrC,MAAM,IAAI,CAAC,YAAY;QAIxB,IAAI,IAAI,CAAC,kBAAkB,EAC1B,IAAI,CAAC,iBAAiB;QAGvB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,2BAA2B,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;IAC7E;IAKA,MAAc,aAAaC,OAAgB,EAAiB;QAC3D,MAAMC,WAAWD,AAAYE,WAAZF,UAAwBA,UAAU,IAAI,CAAC,aAAa;QACrE,MAAMG,SAAS,IAAIC,OAAO,IAAI,CAAC,aAAa,EAAE;YAC7C,gBAAgB;gBACf,wBAAwB;YACzB;QACD;QAEAD,OAAO,EAAE,CAAC,UAAU;YACnB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAEF,SAAS,UAAU,CAAC;YAChD,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAACA;QAC/B;QAEAE,OAAO,EAAE,CAAC,WAAW,CAACE;YACrB,IAAI,CAAC,oBAAoB,CAACJ,UAAUI;QACrC;QAEAF,OAAO,EAAE,CAAC,SAAS;YAClB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAEF,SAAS,MAAM,CAAC;YAC7C,IAAI,CAAC,kBAAkB,CAACA;QACzB;QAEAE,OAAO,EAAE,CAAC,QAAQ,CAACG;YAClB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAEL,SAAS,kBAAkB,EAAEK,MAAM;YAC/D,IAAI,CAAC,iBAAiB,CAACL,UAAUK;QAClC;QAEA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACL,UAAUE;IAC7B;IAKQ,qBAAqBF,QAAgB,EAAEI,OAAuB,EAAQ;QAC7E,IAAIA,AAAiB,aAAjBA,QAAQ,IAAI,IAAiBA,QAAQ,SAAS,EAAE;YACnD,MAAME,WAAW,IAAI,CAAC,eAAe,CAAC,GAAG,CAACF,QAAQ,SAAS;YAC3D,IAAIE,UAAU;gBACbA,SAASF,QAAQ,MAAM;gBACvB,IAAI,CAAC,eAAe,CAAC,MAAM,CAACA,QAAQ,SAAS;YAC9C;QACD,OAAO,IAAIA,AAAiB,YAAjBA,QAAQ,IAAI,IAAgBA,QAAQ,SAAS,EAAE;YACzD,MAAME,WAAW,IAAI,CAAC,eAAe,CAAC,GAAG,CAACF,QAAQ,SAAS;YAC3D,IAAIE,UAAU;gBACbA,SAAS,IAAIV,MAAMQ,QAAQ,KAAK,IAAI;gBACpC,IAAI,CAAC,eAAe,CAAC,MAAM,CAACA,QAAQ,SAAS;YAC9C;QACD,OAAO,IAAIA,AAAiB,aAAjBA,QAAQ,IAAI,EAEtB,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAACJ;IAEhC;IAKQ,mBAAmBA,QAAgB,EAAQ;QAClD,MAAMO,aAAa,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAACP,aAAa;QAE3D,IAAIO,aAAa,IAAI,CAAC,WAAW,EAAE;YAClC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAACP,UAAUO,aAAa;YAClD,IAAI,CAAC,OAAO,CAAC,IAAI,CAChB,CAAC,kBAAkB,EAAEP,SAAS,UAAU,EAAEO,aAAa,EAAE,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;YAIhF,MAAML,SAAS,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACF;YACjC,IAAIE,QAAQ;gBACX,IAAI;oBACHA,OAAO,SAAS;gBACjB,EAAE,OAAM,CAER;gBACA,IAAI,CAAC,QAAQ,CAAC,MAAM,CAACF;YACtB;YAGAQ,WACC;gBACC,IAAI,CAAC,IAAI,CAAC,WAAW,EACpB,IAAI,CAAC,YAAY,CAACR,UAAU,KAAK,CAAC,CAACS;oBAClC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,yBAAyB,EAAET,UAAU,EAAE;wBAAE,OAAOS;oBAAS;gBAC9E;YAEF,GACA,OAAQF,CAAAA,aAAa;QAEvB,OAAO;YACN,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAEP,SAAS,yCAAyC,CAAC;YAChF,MAAME,SAAS,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACF;YACjC,IAAIE,QAAQ;gBACX,IAAI;oBACHA,OAAO,SAAS;gBACjB,EAAE,OAAM,CAER;gBACA,IAAI,CAAC,QAAQ,CAAC,MAAM,CAACF;YACtB;QACD;IACD;IAKQ,kBAAkBA,QAAgB,EAAEK,IAAY,EAAQ;QAC/D,IAAI,CAAC,QAAQ,CAAC,MAAM,CAACL;QAGrB,IAAI,CAAC,IAAI,CAAC,WAAW,IAAIK,AAAS,MAATA,MAAY;YACpC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,4BAA4B,EAAEL,UAAU;YAC3D,IAAI,CAAC,YAAY,CAACA,UAAU,KAAK,CAAC,CAACU;gBAClC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,sCAAsCA;YAC1D;QACD;IACD;IAKQ,oBAA0B;QACjC,IAAI,CAAC,iBAAiB,GAAGC,YAAY;YACpC,KAAK,MAAM,GAAGT,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,GAC7C,IAAI;gBACHA,OAAO,WAAW,CAAC;oBAAE,MAAM;gBAAe;YAC3C,EAAE,OAAM,CAER;QAEF,GAAG,IAAI,CAAC,oBAAoB;IAC7B;IAQA,MAAM,eAAeU,KAAkB,EAAoB;QAC1D,IAAI,IAAI,CAAC,WAAW,EACnB,MAAM,IAAIhB,MAAM;QAGjB,IAAI,AAAuB,MAAvB,IAAI,CAAC,QAAQ,CAAC,IAAI,EACrB,MAAM,IAAIA,MAAM;QAIjB,MAAMiB,YAAYC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI;QAC/C,MAAMC,QAAQ,IAAI,CAAC,gBAAgB,GAAGF,UAAU,MAAM;QACtD,MAAMb,WAAWa,SAAS,CAACE,MAAM;QACjC,MAAMb,SAAS,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACF;QAEjC,IAAI,CAACE,QACJ,MAAM,IAAIN,MAAM,CAAC,OAAO,EAAEI,SAAS,cAAc,CAAC;QAInD,IAAI,CAAC,gBAAgB,GAAI,KAAI,CAAC,gBAAgB,GAAG,KAAKa,UAAU,MAAM;QAGtE,MAAMG,YAAY,CAAC,IAAI,EAAEC,KAAK,GAAG,GAAG,CAAC,EAAEC,KAAK,MAAM,GAAG,QAAQ,CAAC,IAAI,SAAS,CAAC,GAAG,KAAK;QAEpF,OAAO,IAAIC,QAAQ,CAACC,SAASC;YAE5B,MAAMC,UAAUd,WAAW;gBAC1B,IAAI,CAAC,eAAe,CAAC,MAAM,CAACQ;gBAC5BK,OAAO,IAAIzB,MAAM,CAAC,qBAAqB,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YACjE,GAAG,IAAI,CAAC,cAAc;YAGtB,IAAI,CAAC,eAAe,CAAC,GAAG,CAACoB,WAAW,CAACO;gBACpCC,aAAaF;gBACb,IAAIC,kBAAkB3B,OACrByB,OAAOE;qBAEPH,QAAQG;YAEV;YAGA,IAAI;gBACHrB,OAAO,WAAW,CAAC;oBAClB,MAAM;oBACNc;oBACAJ;gBACD;YACD,EAAE,OAAOa,OAAO;gBACfD,aAAaF;gBACb,IAAI,CAAC,eAAe,CAAC,MAAM,CAACN;gBAC5BK,OAAOI;YACR;QACD;IACD;IAKA,WAKE;QACD,OAAO;YACN,eAAe,IAAI,CAAC,QAAQ,CAAC,IAAI;YACjC,gBAAgB,IAAI,CAAC,eAAe,CAAC,IAAI;YACzC,YAAY,IAAI,CAAC,WAAW;YAC5B,oBAAoB,IAAI,CAAC,kBAAkB;QAC5C;IACD;IAKA,MAAM,YAA2B;QAChC,IAAI,IAAI,CAAC,WAAW,EACnB;QAGD,IAAI,CAAC,WAAW,GAAG;QAGnB,IAAI,IAAI,CAAC,iBAAiB,EAAE;YAC3BC,cAAc,IAAI,CAAC,iBAAiB;YACpC,IAAI,CAAC,iBAAiB,GAAG;QAC1B;QAGA,MAAMC,oBAAoBb,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAC,CAACZ,SAC1D,IAAIiB,QAAc,CAACC;gBACzBlB,OACE,SAAS,GACT,IAAI,CAAC,IAAMkB,WACX,KAAK,CAAC,IAAMA;YACf;QAGD,MAAMD,QAAQ,GAAG,CAACQ;QAClB,IAAI,CAAC,QAAQ,CAAC,KAAK;QACnB,IAAI,CAAC,eAAe,CAAC,KAAK;QAE1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IACnB;IAOA,MAAM,UAAyB;QAC9B,MAAM,IAAI,CAAC,SAAS;IACrB;IAKA,YAAqB;QACpB,OAAO,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG;IAClD;AACD;AAiBO,SAASC,oBAAoBrC,OAA8B;IACjE,OAAO,IAAIF,cAAcE;AAC1B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker thread entry point for parallel thought processing.
|
|
3
|
+
*
|
|
4
|
+
* This module is the entry point for worker processes that handle thought
|
|
5
|
+
* processing in parallel. Workers receive messages from the main process
|
|
6
|
+
* via the Worker messaging API and process them asynchronously.
|
|
7
|
+
*
|
|
8
|
+
* @module cluster
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/cluster/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { parentPort } from "node:worker_threads";
|
|
2
|
+
if (null !== parentPort) {
|
|
3
|
+
parentPort.on('message', async (message)=>{
|
|
4
|
+
try {
|
|
5
|
+
if ('process-thought' === message.type) {
|
|
6
|
+
const result = await handleProcessThought(message.input);
|
|
7
|
+
parentPort.postMessage({
|
|
8
|
+
type: 'result',
|
|
9
|
+
requestId: message.requestId,
|
|
10
|
+
result
|
|
11
|
+
});
|
|
12
|
+
} else if ('health-check' === message.type) parentPort.postMessage({
|
|
13
|
+
type: 'health'
|
|
14
|
+
});
|
|
15
|
+
} catch (error) {
|
|
16
|
+
parentPort.postMessage({
|
|
17
|
+
type: 'error',
|
|
18
|
+
requestId: message.requestId,
|
|
19
|
+
error: error instanceof Error ? error.message : String(error)
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
parentPort.postMessage({
|
|
24
|
+
type: 'ready'
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async function handleProcessThought(input) {
|
|
28
|
+
const result = {
|
|
29
|
+
success: true,
|
|
30
|
+
timestamp: Date.now(),
|
|
31
|
+
input
|
|
32
|
+
};
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster/worker.js","sources":["../../src/cluster/worker.ts"],"sourcesContent":["/**\n * Worker thread entry point for parallel thought processing.\n *\n * This module is the entry point for worker processes that handle thought\n * processing in parallel. Workers receive messages from the main process\n * via the Worker messaging API and process them asynchronously.\n *\n * @module cluster\n */\n\nimport { parentPort } from 'node:worker_threads';\n\n/**\n * Message types that can be sent to the worker from the main process.\n *\n * @example\n * ```typescript\n * const message: WorkerInput = {\n * type: 'process-thought',\n * requestId: 'req_123',\n * input: { thought: 'test', thought_number: 1, total_thoughts: 1 }\n * };\n * ```\n */\ninterface WorkerInput {\n\t/** The message type indicating the action to perform. */\n\ttype: 'process-thought' | 'health-check';\n\n\t/** Optional request identifier for correlating responses. */\n\trequestId?: string;\n\n\t/** The input data for processing (varies by type). */\n\tinput?: unknown;\n}\n\n/**\n * Response types sent from the worker back to the main process.\n *\n * @example\n * ```typescript\n * const response: WorkerResponse = {\n * type: 'result',\n * requestId: 'req_123',\n * result: { success: true, data: {...} }\n * };\n * ```\n */\ninterface WorkerResponse {\n\t/** The response type indicating the result status. */\n\ttype: 'result' | 'error' | 'health' | 'ready';\n\n\t/** Optional request identifier for correlating with the original request. */\n\trequestId?: string;\n\n\t/** The result data (present for successful results). */\n\tresult?: unknown;\n\n\t/** Error message (present for errors). */\n\terror?: string;\n}\n\n/**\n * Worker entry point for handling messages from the main process.\n *\n * This script runs in a separate process and receives messages from the main\n * process via the Worker messaging API. It processes thoughts and returns results.\n * The worker responds to 'process-thought' messages by processing thoughts and\n * to 'health-check' messages with a health status.\n *\n * @remarks\n * **Message Flow:**\n * 1. Worker starts and sends a 'ready' message\n * 2. Main process can send 'health-check' messages to verify worker status\n * 3. Main process sends 'process-thought' messages with thought data\n * 4. Worker processes the thought and sends back a 'result' message\n * 5. On errors, worker sends an 'error' message with details\n *\n * **Supported Message Types:**\n * - `process-thought` - Process a thought and return recommendations\n * - `health-check` - Return worker health status\n *\n * @example\n * ```typescript\n * // In main process:\n * const { Worker } = await import('node:worker_threads');\n * const worker = new Worker('./dist/cluster/worker.js');\n *\n * worker.on('message', (response: WorkerResponse) => {\n * if (response.type === 'result') {\n * console.log('Result:', response.result);\n * } else if (response.type === 'ready') {\n * console.log('Worker ready');\n * }\n * });\n *\n * // Send a thought for processing\n * worker.postMessage({\n * type: 'process-thought',\n * requestId: 'req_123',\n * input: {\n * thought: 'I need to analyze this problem',\n * thought_number: 1,\n * total_thoughts: 3,\n * next_thought_needed: true\n * }\n * });\n * ```\n */\nif (parentPort !== null) {\n\tparentPort.on('message', async (message: WorkerInput) => {\n\t\ttry {\n\t\t\tif (message.type === 'process-thought') {\n\t\t\t\tconst result = await handleProcessThought(message.input);\n\t\t\t\tparentPort!.postMessage({\n\t\t\t\t\ttype: 'result',\n\t\t\t\t\trequestId: message.requestId,\n\t\t\t\t\tresult,\n\t\t\t\t} as WorkerResponse);\n\t\t\t} else if (message.type === 'health-check') {\n\t\t\t\t// Respond to health check\n\t\t\t\tparentPort!.postMessage({\n\t\t\t\t\ttype: 'health',\n\t\t\t\t} as WorkerResponse);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tparentPort!.postMessage({\n\t\t\t\ttype: 'error',\n\t\t\t\trequestId: message.requestId,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t} as WorkerResponse);\n\t\t}\n\t});\n\n\t// Signal ready\n\tparentPort!.postMessage({ type: 'ready' } as WorkerResponse);\n}\n\n/**\n * Process a thought and generate recommendations.\n *\n * This function contains the core thought processing logic that runs in the\n * worker process. It validates the thought, adds it to history, generates\n * tool/skill recommendations, and formats the response.\n *\n * @remarks\n * **Processing Steps:**\n * 1. Validate the thought input\n * 2. Store the thought in history\n * 3. Generate tool and skill recommendations\n * 4. Format the output response\n *\n * This is currently a placeholder implementation that echoes back the input\n * with a timestamp. A full implementation would integrate with the\n * ThoughtProcessor, HistoryManager, and other components.\n *\n * @param input - The thought data to process\n * @returns A Promise resolving to the processing result\n *\n * @example\n * ```typescript\n * const result = await handleProcessThought({\n * thought: 'I should read the configuration file',\n * thought_number: 1,\n * total_thoughts: 2,\n * next_thought_needed: true\n * });\n *\n * // Result: { success: true, timestamp: 1705550000000, input: {...} }\n * ```\n */\nasync function handleProcessThought(input: unknown): Promise<unknown> {\n\t// This is a placeholder implementation\n\t// In a real scenario, this would use the ThoughtProcessor and other components\n\n\tconst result = {\n\t\tsuccess: true,\n\t\ttimestamp: Date.now(),\n\t\tinput,\n\t};\n\n\treturn result;\n}\n"],"names":["parentPort","message","result","handleProcessThought","error","Error","String","input","Date"],"mappings":";AA4GA,IAAIA,AAAe,SAAfA,YAAqB;IACxBA,WAAW,EAAE,CAAC,WAAW,OAAOC;QAC/B,IAAI;YACH,IAAIA,AAAiB,sBAAjBA,QAAQ,IAAI,EAAwB;gBACvC,MAAMC,SAAS,MAAMC,qBAAqBF,QAAQ,KAAK;gBACvDD,WAAAA,WAAuB,CAAC;oBACvB,MAAM;oBACN,WAAWC,QAAQ,SAAS;oBAC5BC;gBACD;YACD,OAAO,IAAID,AAAiB,mBAAjBA,QAAQ,IAAI,EAEtBD,WAAAA,WAAuB,CAAC;gBACvB,MAAM;YACP;QAEF,EAAE,OAAOI,OAAO;YACfJ,WAAAA,WAAuB,CAAC;gBACvB,MAAM;gBACN,WAAWC,QAAQ,SAAS;gBAC5B,OAAOG,iBAAiBC,QAAQD,MAAM,OAAO,GAAGE,OAAOF;YACxD;QACD;IACD;IAGAJ,WAAAA,WAAuB,CAAC;QAAE,MAAM;IAAQ;AACzC;AAmCA,eAAeG,qBAAqBI,KAAc;IAIjD,MAAML,SAAS;QACd,SAAS;QACT,WAAWM,KAAK,GAAG;QACnBD;IACD;IAEA,OAAOL;AACR"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration file loading with environment variable override support.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the `ConfigLoader` class which handles loading configuration
|
|
5
|
+
* from YAML and JSON files in standard locations, with automatic environment variable
|
|
6
|
+
* overrides for all settings.
|
|
7
|
+
*
|
|
8
|
+
* @module config
|
|
9
|
+
*/
|
|
10
|
+
import type { PersistenceConfig } from '../persistence/PersistenceBackend.js';
|
|
11
|
+
/**
|
|
12
|
+
* Configuration options loaded from config files.
|
|
13
|
+
*
|
|
14
|
+
* These options represent the structure of configuration files (JSON or YAML)
|
|
15
|
+
* that can be loaded from standard locations. All values can be overridden
|
|
16
|
+
* by environment variables.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```yaml
|
|
20
|
+
* # .claude/config.yaml
|
|
21
|
+
* maxHistorySize: 500
|
|
22
|
+
* maxBranches: 25
|
|
23
|
+
* logLevel: debug
|
|
24
|
+
* prettyLog: true
|
|
25
|
+
* skillDirs:
|
|
26
|
+
* - ./custom-skills
|
|
27
|
+
* discoveryCache:
|
|
28
|
+
* ttl: 600000
|
|
29
|
+
* maxSize: 200
|
|
30
|
+
* persistence:
|
|
31
|
+
* enabled: true
|
|
32
|
+
* backend: sqlite
|
|
33
|
+
* options:
|
|
34
|
+
* dbPath: ./data/history.db
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export interface ConfigFileOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Maximum number of thoughts to keep in history.
|
|
40
|
+
* Can be overridden by `MAX_HISTORY_SIZE` environment variable.
|
|
41
|
+
*/
|
|
42
|
+
maxHistorySize?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Maximum number of branches to maintain.
|
|
45
|
+
* Can be overridden by `MAX_BRANCHES` environment variable.
|
|
46
|
+
*/
|
|
47
|
+
maxBranches?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Maximum size of each branch.
|
|
50
|
+
* Can be overridden by `MAX_BRANCH_SIZE` environment variable.
|
|
51
|
+
*/
|
|
52
|
+
maxBranchSize?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Logging level for the application.
|
|
55
|
+
* Can be overridden by `LOG_LEVEL` environment variable.
|
|
56
|
+
*/
|
|
57
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
58
|
+
/**
|
|
59
|
+
* Whether to enable pretty (formatted) logging output.
|
|
60
|
+
* Can be overridden by `PRETTY_LOG` environment variable (set to "false" to disable).
|
|
61
|
+
*/
|
|
62
|
+
prettyLog?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Directory paths to search for skills.
|
|
65
|
+
* Can be overridden by `SKILL_DIRS` environment variable (colon-separated).
|
|
66
|
+
*/
|
|
67
|
+
skillDirs?: string[];
|
|
68
|
+
/**
|
|
69
|
+
* Discovery cache configuration.
|
|
70
|
+
* Can be overridden by `DISCOVERY_CACHE_TTL` and `DISCOVERY_CACHE_MAX_SIZE` environment variables.
|
|
71
|
+
*/
|
|
72
|
+
discoveryCache?: {
|
|
73
|
+
/**
|
|
74
|
+
* Time-to-live for cache entries in milliseconds.
|
|
75
|
+
* Environment variable `DISCOVERY_CACHE_TTL` accepts seconds.
|
|
76
|
+
*/
|
|
77
|
+
ttl?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Maximum number of entries in the cache.
|
|
80
|
+
*/
|
|
81
|
+
maxSize?: number;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Persistence configuration for storing history and state.
|
|
85
|
+
*/
|
|
86
|
+
persistence?: PersistenceConfig;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Loads configuration from files with environment variable overrides.
|
|
90
|
+
*
|
|
91
|
+
* This class searches for configuration files in standard locations and applies
|
|
92
|
+
* environment variable overrides. Files are searched in priority order, with the
|
|
93
|
+
* first match being used. Environment variables always take precedence over file values.
|
|
94
|
+
*
|
|
95
|
+
* @remarks
|
|
96
|
+
* **Config File Search Order (priority):**
|
|
97
|
+
* 1. Custom path (if provided to constructor)
|
|
98
|
+
* 2. `.claude/config.json` (project-local)
|
|
99
|
+
* 3. `.claude/config.yaml` (project-local)
|
|
100
|
+
* 4. `.claude/config.yml` (project-local)
|
|
101
|
+
* 5. `~/.claude/config.json` (user-global)
|
|
102
|
+
* 6. `~/.claude/config.yaml` (user-global)
|
|
103
|
+
* 7. `~/.claude/config.yml` (user-global)
|
|
104
|
+
*
|
|
105
|
+
* **Environment Variable Overrides:**
|
|
106
|
+
* | Variable | Type | Description |
|
|
107
|
+
* |----------|------|-------------|
|
|
108
|
+
* | `MAX_HISTORY_SIZE` | number | Max thoughts in history |
|
|
109
|
+
* | `MAX_BRANCHES` | number | Max number of branches |
|
|
110
|
+
* | `MAX_BRANCH_SIZE` | number | Max size of each branch |
|
|
111
|
+
* | `LOG_LEVEL` | string | Logging level (debug/info/warn/error) |
|
|
112
|
+
* | `PRETTY_LOG` | string | "false" to disable pretty logging |
|
|
113
|
+
* | `SKILL_DIRS` | string | Colon-separated directory paths |
|
|
114
|
+
* | `DISCOVERY_CACHE_TTL` | number | TTL in seconds (converted to ms) |
|
|
115
|
+
* | `DISCOVERY_CACHE_MAX_SIZE` | number | Max cache entries |
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* // Use default search paths
|
|
120
|
+
* const loader1 = new ConfigLoader();
|
|
121
|
+
* const config1 = loader1.load();
|
|
122
|
+
*
|
|
123
|
+
* // Use custom config path
|
|
124
|
+
* const loader2 = new ConfigLoader('./my-config.yaml');
|
|
125
|
+
* const config2 = loader2.load();
|
|
126
|
+
*
|
|
127
|
+
* // Convert to ServerConfig options
|
|
128
|
+
* const serverOptions = loader2.toServerConfigOptions(config2);
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export declare class ConfigLoader {
|
|
132
|
+
/** Array of config file paths to search, in priority order. */
|
|
133
|
+
private _configPaths;
|
|
134
|
+
/**
|
|
135
|
+
* Creates a new ConfigLoader instance.
|
|
136
|
+
*
|
|
137
|
+
* @param customPath - Optional custom config file path. If provided, only this path will be checked.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* // Use default search paths
|
|
142
|
+
* const loader1 = new ConfigLoader();
|
|
143
|
+
*
|
|
144
|
+
* // Use a specific config file
|
|
145
|
+
* const loader2 = new ConfigLoader('./custom-config.json');
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
constructor(customPath?: string);
|
|
149
|
+
/**
|
|
150
|
+
* Loads configuration from files and applies environment overrides.
|
|
151
|
+
*
|
|
152
|
+
* Searches for config files in the configured paths (in priority order),
|
|
153
|
+
* parses the first match, and applies environment variable overrides.
|
|
154
|
+
* Returns null if no config file is found and no environment overrides are set.
|
|
155
|
+
*
|
|
156
|
+
* @returns The loaded configuration with environment overrides applied, or null if no config found
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* const loader = new ConfigLoader();
|
|
161
|
+
* const config = loader.load();
|
|
162
|
+
*
|
|
163
|
+
* if (config) {
|
|
164
|
+
* console.log('Max history size:', config.maxHistorySize);
|
|
165
|
+
* console.log('Log level:', config.logLevel);
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
load(): ConfigFileOptions | null;
|
|
170
|
+
/**
|
|
171
|
+
* Applies environment variable overrides to the configuration.
|
|
172
|
+
*
|
|
173
|
+
* Environment variables take precedence over file-based configuration.
|
|
174
|
+
* Supported environment variables:
|
|
175
|
+
* - `MAX_HISTORY_SIZE`, `MAX_BRANCHES`, `MAX_BRANCH_SIZE` (numbers)
|
|
176
|
+
* - `LOG_LEVEL` (debug/info/warn/error)
|
|
177
|
+
* - `PRETTY_LOG` ("false" to disable)
|
|
178
|
+
* - `SKILL_DIRS` (colon-separated paths)
|
|
179
|
+
* - `DISCOVERY_CACHE_TTL` (in seconds, converted to ms)
|
|
180
|
+
* - `DISCOVERY_CACHE_MAX_SIZE` (number)
|
|
181
|
+
*
|
|
182
|
+
* @param config - The configuration to apply overrides to
|
|
183
|
+
* @returns A new configuration object with environment overrides applied
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
private applyEnvironmentOverrides;
|
|
187
|
+
/**
|
|
188
|
+
* Parses a configuration file (JSON or YAML).
|
|
189
|
+
*
|
|
190
|
+
* Detects the file type by extension and uses the appropriate parser.
|
|
191
|
+
* Supports `.json`, `.yaml`, and `.yml` file extensions.
|
|
192
|
+
*
|
|
193
|
+
* @param filePath - Path to the configuration file to parse
|
|
194
|
+
* @returns The parsed configuration object
|
|
195
|
+
* @throws {Error} If the file cannot be parsed
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
private parseConfig;
|
|
199
|
+
/**
|
|
200
|
+
* Converts file-based configuration to ServerConfig options.
|
|
201
|
+
*
|
|
202
|
+
* This is a convenience method for extracting the ServerConfig-relevant
|
|
203
|
+
* options from a file-based configuration.
|
|
204
|
+
*
|
|
205
|
+
* @param config - The configuration to convert
|
|
206
|
+
* @returns An object with ServerConfig-compatible options
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* const loader = new ConfigLoader();
|
|
211
|
+
* const config = loader.load();
|
|
212
|
+
* if (config) {
|
|
213
|
+
* const serverOpts = loader.toServerConfigOptions(config);
|
|
214
|
+
* const serverConfig = new ServerConfig(serverOpts);
|
|
215
|
+
* }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
toServerConfigOptions(config: ConfigFileOptions): {
|
|
219
|
+
maxHistorySize?: number;
|
|
220
|
+
maxBranches?: number;
|
|
221
|
+
maxBranchSize?: number;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=ConfigLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigLoader.d.ts","sourceRoot":"","sources":["../../src/config/ConfigLoader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAE/C;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;;OAGG;IACH,cAAc,CAAC,EAAE;QAChB;;;WAGG;QACH,GAAG,CAAC,EAAE,MAAM,CAAC;QACb;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF;;OAEG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qBAAa,YAAY;IACxB,+DAA+D;IAC/D,OAAO,CAAC,YAAY,CAAW;IAE/B;;;;;;;;;;;;;OAaG;gBACS,UAAU,CAAC,EAAE,MAAM;IAa/B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,IAAI,IAAI,iBAAiB,GAAG,IAAI;IAoBhC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,yBAAyB;IAmDjC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW;IAWnB;;;;;;;;;;;;;;;;;;OAkBG;IACH,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG;QACjD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,CAAC;KACvB;CAOD"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { parse } from "yaml";
|
|
5
|
+
class ConfigLoader {
|
|
6
|
+
_configPaths;
|
|
7
|
+
constructor(customPath){
|
|
8
|
+
this._configPaths = customPath ? [
|
|
9
|
+
customPath
|
|
10
|
+
] : [
|
|
11
|
+
'.claude/config.json',
|
|
12
|
+
'.claude/config.yaml',
|
|
13
|
+
'.claude/config.yml',
|
|
14
|
+
join(homedir(), '.claude/config.json'),
|
|
15
|
+
join(homedir(), '.claude/config.yaml'),
|
|
16
|
+
join(homedir(), '.claude/config.yml')
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
load() {
|
|
20
|
+
let config = null;
|
|
21
|
+
for (const configPath of this._configPaths)if (existsSync(configPath)) try {
|
|
22
|
+
config = this.parseConfig(configPath);
|
|
23
|
+
break;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error(`Failed to load config from ${configPath}:`, error instanceof Error ? error.message : String(error));
|
|
26
|
+
}
|
|
27
|
+
return this.applyEnvironmentOverrides(config || {});
|
|
28
|
+
}
|
|
29
|
+
applyEnvironmentOverrides(config) {
|
|
30
|
+
const result = {
|
|
31
|
+
...config
|
|
32
|
+
};
|
|
33
|
+
if (process.env.MAX_HISTORY_SIZE) {
|
|
34
|
+
const parsed = parseInt(process.env.MAX_HISTORY_SIZE, 10);
|
|
35
|
+
if (Number.isFinite(parsed)) result.maxHistorySize = parsed;
|
|
36
|
+
}
|
|
37
|
+
if (process.env.MAX_BRANCHES) {
|
|
38
|
+
const parsed = parseInt(process.env.MAX_BRANCHES, 10);
|
|
39
|
+
if (Number.isFinite(parsed)) result.maxBranches = parsed;
|
|
40
|
+
}
|
|
41
|
+
if (process.env.MAX_BRANCH_SIZE) {
|
|
42
|
+
const parsed = parseInt(process.env.MAX_BRANCH_SIZE, 10);
|
|
43
|
+
if (Number.isFinite(parsed)) result.maxBranchSize = parsed;
|
|
44
|
+
}
|
|
45
|
+
if (process.env.LOG_LEVEL && [
|
|
46
|
+
'debug',
|
|
47
|
+
'info',
|
|
48
|
+
'warn',
|
|
49
|
+
'error'
|
|
50
|
+
].includes(process.env.LOG_LEVEL)) result.logLevel = process.env.LOG_LEVEL;
|
|
51
|
+
if ('false' === process.env.PRETTY_LOG) result.prettyLog = false;
|
|
52
|
+
if (process.env.SKILL_DIRS) result.skillDirs = process.env.SKILL_DIRS.split(':');
|
|
53
|
+
if (process.env.DISCOVERY_CACHE_TTL) {
|
|
54
|
+
const parsed = parseInt(process.env.DISCOVERY_CACHE_TTL, 10);
|
|
55
|
+
if (Number.isFinite(parsed)) {
|
|
56
|
+
result.discoveryCache = result.discoveryCache || {};
|
|
57
|
+
result.discoveryCache.ttl = 1000 * parsed;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (process.env.DISCOVERY_CACHE_MAX_SIZE) {
|
|
61
|
+
const parsed = parseInt(process.env.DISCOVERY_CACHE_MAX_SIZE, 10);
|
|
62
|
+
if (Number.isFinite(parsed)) {
|
|
63
|
+
result.discoveryCache = result.discoveryCache || {};
|
|
64
|
+
result.discoveryCache.maxSize = parsed;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
parseConfig(filePath) {
|
|
70
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
71
|
+
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
72
|
+
if ('yaml' === ext || 'yml' === ext) return parse(content);
|
|
73
|
+
return JSON.parse(content);
|
|
74
|
+
}
|
|
75
|
+
toServerConfigOptions(config) {
|
|
76
|
+
return {
|
|
77
|
+
maxHistorySize: config.maxHistorySize,
|
|
78
|
+
maxBranches: config.maxBranches,
|
|
79
|
+
maxBranchSize: config.maxBranchSize
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export { ConfigLoader };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=ConfigLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config/ConfigLoader.js","sources":["../../src/config/ConfigLoader.ts"],"sourcesContent":["/**\n * Configuration file loading with environment variable override support.\n *\n * This module provides the `ConfigLoader` class which handles loading configuration\n * from YAML and JSON files in standard locations, with automatic environment variable\n * overrides for all settings.\n *\n * @module config\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport type { PersistenceConfig } from '../persistence/PersistenceBackend.js';\n\n/**\n * Configuration options loaded from config files.\n *\n * These options represent the structure of configuration files (JSON or YAML)\n * that can be loaded from standard locations. All values can be overridden\n * by environment variables.\n *\n * @example\n * ```yaml\n * # .claude/config.yaml\n * maxHistorySize: 500\n * maxBranches: 25\n * logLevel: debug\n * prettyLog: true\n * skillDirs:\n * - ./custom-skills\n * discoveryCache:\n * ttl: 600000\n * maxSize: 200\n * persistence:\n * enabled: true\n * backend: sqlite\n * options:\n * dbPath: ./data/history.db\n * ```\n */\nexport interface ConfigFileOptions {\n\t/**\n\t * Maximum number of thoughts to keep in history.\n\t * Can be overridden by `MAX_HISTORY_SIZE` environment variable.\n\t */\n\tmaxHistorySize?: number;\n\n\t/**\n\t * Maximum number of branches to maintain.\n\t * Can be overridden by `MAX_BRANCHES` environment variable.\n\t */\n\tmaxBranches?: number;\n\n\t/**\n\t * Maximum size of each branch.\n\t * Can be overridden by `MAX_BRANCH_SIZE` environment variable.\n\t */\n\tmaxBranchSize?: number;\n\n\t/**\n\t * Logging level for the application.\n\t * Can be overridden by `LOG_LEVEL` environment variable.\n\t */\n\tlogLevel?: 'debug' | 'info' | 'warn' | 'error';\n\n\t/**\n\t * Whether to enable pretty (formatted) logging output.\n\t * Can be overridden by `PRETTY_LOG` environment variable (set to \"false\" to disable).\n\t */\n\tprettyLog?: boolean;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * Can be overridden by `SKILL_DIRS` environment variable (colon-separated).\n\t */\n\tskillDirs?: string[];\n\n\t/**\n\t * Discovery cache configuration.\n\t * Can be overridden by `DISCOVERY_CACHE_TTL` and `DISCOVERY_CACHE_MAX_SIZE` environment variables.\n\t */\n\tdiscoveryCache?: {\n\t\t/**\n\t\t * Time-to-live for cache entries in milliseconds.\n\t\t * Environment variable `DISCOVERY_CACHE_TTL` accepts seconds.\n\t\t */\n\t\tttl?: number;\n\t\t/**\n\t\t * Maximum number of entries in the cache.\n\t\t */\n\t\tmaxSize?: number;\n\t};\n\n\t/**\n\t * Persistence configuration for storing history and state.\n\t */\n\tpersistence?: PersistenceConfig;\n}\n\n/**\n * Loads configuration from files with environment variable overrides.\n *\n * This class searches for configuration files in standard locations and applies\n * environment variable overrides. Files are searched in priority order, with the\n * first match being used. Environment variables always take precedence over file values.\n *\n * @remarks\n * **Config File Search Order (priority):**\n * 1. Custom path (if provided to constructor)\n * 2. `.claude/config.json` (project-local)\n * 3. `.claude/config.yaml` (project-local)\n * 4. `.claude/config.yml` (project-local)\n * 5. `~/.claude/config.json` (user-global)\n * 6. `~/.claude/config.yaml` (user-global)\n * 7. `~/.claude/config.yml` (user-global)\n *\n * **Environment Variable Overrides:**\n * | Variable | Type | Description |\n * |----------|------|-------------|\n * | `MAX_HISTORY_SIZE` | number | Max thoughts in history |\n * | `MAX_BRANCHES` | number | Max number of branches |\n * | `MAX_BRANCH_SIZE` | number | Max size of each branch |\n * | `LOG_LEVEL` | string | Logging level (debug/info/warn/error) |\n * | `PRETTY_LOG` | string | \"false\" to disable pretty logging |\n * | `SKILL_DIRS` | string | Colon-separated directory paths |\n * | `DISCOVERY_CACHE_TTL` | number | TTL in seconds (converted to ms) |\n * | `DISCOVERY_CACHE_MAX_SIZE` | number | Max cache entries |\n *\n * @example\n * ```typescript\n * // Use default search paths\n * const loader1 = new ConfigLoader();\n * const config1 = loader1.load();\n *\n * // Use custom config path\n * const loader2 = new ConfigLoader('./my-config.yaml');\n * const config2 = loader2.load();\n *\n * // Convert to ServerConfig options\n * const serverOptions = loader2.toServerConfigOptions(config2);\n * ```\n */\nexport class ConfigLoader {\n\t/** Array of config file paths to search, in priority order. */\n\tprivate _configPaths: string[];\n\n\t/**\n\t * Creates a new ConfigLoader instance.\n\t *\n\t * @param customPath - Optional custom config file path. If provided, only this path will be checked.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Use default search paths\n\t * const loader1 = new ConfigLoader();\n\t *\n\t * // Use a specific config file\n\t * const loader2 = new ConfigLoader('./custom-config.json');\n\t * ```\n\t */\n\tconstructor(customPath?: string) {\n\t\tthis._configPaths = customPath\n\t\t\t? [customPath]\n\t\t\t: [\n\t\t\t\t\t'.claude/config.json',\n\t\t\t\t\t'.claude/config.yaml',\n\t\t\t\t\t'.claude/config.yml',\n\t\t\t\t\tjoin(homedir(), '.claude/config.json'),\n\t\t\t\t\tjoin(homedir(), '.claude/config.yaml'),\n\t\t\t\t\tjoin(homedir(), '.claude/config.yml'),\n\t\t\t\t];\n\t}\n\n\t/**\n\t * Loads configuration from files and applies environment overrides.\n\t *\n\t * Searches for config files in the configured paths (in priority order),\n\t * parses the first match, and applies environment variable overrides.\n\t * Returns null if no config file is found and no environment overrides are set.\n\t *\n\t * @returns The loaded configuration with environment overrides applied, or null if no config found\n\t *\n\t * @example\n\t * ```typescript\n\t * const loader = new ConfigLoader();\n\t * const config = loader.load();\n\t *\n\t * if (config) {\n\t * console.log('Max history size:', config.maxHistorySize);\n\t * console.log('Log level:', config.logLevel);\n\t * }\n\t * ```\n\t */\n\tload(): ConfigFileOptions | null {\n\t\tlet config: ConfigFileOptions | null = null;\n\n\t\tfor (const configPath of this._configPaths) {\n\t\t\tif (existsSync(configPath)) {\n\t\t\t\ttry {\n\t\t\t\t\tconfig = this.parseConfig(configPath);\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Failed to load config from ${configPath}:`,\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.applyEnvironmentOverrides(config || {});\n\t}\n\n\t/**\n\t * Applies environment variable overrides to the configuration.\n\t *\n\t * Environment variables take precedence over file-based configuration.\n\t * Supported environment variables:\n\t * - `MAX_HISTORY_SIZE`, `MAX_BRANCHES`, `MAX_BRANCH_SIZE` (numbers)\n\t * - `LOG_LEVEL` (debug/info/warn/error)\n\t * - `PRETTY_LOG` (\"false\" to disable)\n\t * - `SKILL_DIRS` (colon-separated paths)\n\t * - `DISCOVERY_CACHE_TTL` (in seconds, converted to ms)\n\t * - `DISCOVERY_CACHE_MAX_SIZE` (number)\n\t *\n\t * @param config - The configuration to apply overrides to\n\t * @returns A new configuration object with environment overrides applied\n\t * @private\n\t */\n\tprivate applyEnvironmentOverrides(config: ConfigFileOptions): ConfigFileOptions {\n\t\tconst result: ConfigFileOptions = { ...config };\n\n\t\tif (process.env.MAX_HISTORY_SIZE) {\n\t\t\tconst parsed = parseInt(process.env.MAX_HISTORY_SIZE, 10);\n\t\t\tif (Number.isFinite(parsed)) {\n\t\t\t\tresult.maxHistorySize = parsed;\n\t\t\t}\n\t\t}\n\t\tif (process.env.MAX_BRANCHES) {\n\t\t\tconst parsed = parseInt(process.env.MAX_BRANCHES, 10);\n\t\t\tif (Number.isFinite(parsed)) {\n\t\t\t\tresult.maxBranches = parsed;\n\t\t\t}\n\t\t}\n\t\tif (process.env.MAX_BRANCH_SIZE) {\n\t\t\tconst parsed = parseInt(process.env.MAX_BRANCH_SIZE, 10);\n\t\t\tif (Number.isFinite(parsed)) {\n\t\t\t\tresult.maxBranchSize = parsed;\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\tprocess.env.LOG_LEVEL &&\n\t\t\t['debug', 'info', 'warn', 'error'].includes(process.env.LOG_LEVEL)\n\t\t) {\n\t\t\tresult.logLevel = process.env.LOG_LEVEL as 'debug' | 'info' | 'warn' | 'error';\n\t\t}\n\t\tif (process.env.PRETTY_LOG === 'false') {\n\t\t\tresult.prettyLog = false;\n\t\t}\n\t\tif (process.env.SKILL_DIRS) {\n\t\t\tresult.skillDirs = process.env.SKILL_DIRS.split(':');\n\t\t}\n\t\tif (process.env.DISCOVERY_CACHE_TTL) {\n\t\t\tconst parsed = parseInt(process.env.DISCOVERY_CACHE_TTL, 10);\n\t\t\tif (Number.isFinite(parsed)) {\n\t\t\t\tresult.discoveryCache = result.discoveryCache || {};\n\t\t\t\tresult.discoveryCache.ttl = parsed * 1000;\n\t\t\t}\n\t\t}\n\t\tif (process.env.DISCOVERY_CACHE_MAX_SIZE) {\n\t\t\tconst parsed = parseInt(process.env.DISCOVERY_CACHE_MAX_SIZE, 10);\n\t\t\tif (Number.isFinite(parsed)) {\n\t\t\t\tresult.discoveryCache = result.discoveryCache || {};\n\t\t\t\tresult.discoveryCache.maxSize = parsed;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Parses a configuration file (JSON or YAML).\n\t *\n\t * Detects the file type by extension and uses the appropriate parser.\n\t * Supports `.json`, `.yaml`, and `.yml` file extensions.\n\t *\n\t * @param filePath - Path to the configuration file to parse\n\t * @returns The parsed configuration object\n\t * @throws {Error} If the file cannot be parsed\n\t * @private\n\t */\n\tprivate parseConfig(filePath: string): ConfigFileOptions {\n\t\tconst content = readFileSync(filePath, 'utf-8');\n\t\tconst ext = filePath.split('.').pop()?.toLowerCase();\n\n\t\tif (ext === 'yaml' || ext === 'yml') {\n\t\t\treturn parseYaml(content) as ConfigFileOptions;\n\t\t}\n\n\t\treturn JSON.parse(content) as ConfigFileOptions;\n\t}\n\n\t/**\n\t * Converts file-based configuration to ServerConfig options.\n\t *\n\t * This is a convenience method for extracting the ServerConfig-relevant\n\t * options from a file-based configuration.\n\t *\n\t * @param config - The configuration to convert\n\t * @returns An object with ServerConfig-compatible options\n\t *\n\t * @example\n\t * ```typescript\n\t * const loader = new ConfigLoader();\n\t * const config = loader.load();\n\t * if (config) {\n\t * const serverOpts = loader.toServerConfigOptions(config);\n\t * const serverConfig = new ServerConfig(serverOpts);\n\t * }\n\t * ```\n\t */\n\ttoServerConfigOptions(config: ConfigFileOptions): {\n\t\tmaxHistorySize?: number;\n\t\tmaxBranches?: number;\n\t\tmaxBranchSize?: number;\n\t} {\n\t\treturn {\n\t\t\tmaxHistorySize: config.maxHistorySize,\n\t\t\tmaxBranches: config.maxBranches,\n\t\t\tmaxBranchSize: config.maxBranchSize,\n\t\t};\n\t}\n}\n"],"names":["ConfigLoader","customPath","join","homedir","config","configPath","existsSync","error","console","Error","String","result","process","parsed","parseInt","Number","filePath","content","readFileSync","ext","parseYaml","JSON"],"mappings":"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"}
|