@elliotding/ai-agent-mcp 0.1.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.
Files changed (191) hide show
  1. package/dist/api/cached-client.d.ts +48 -0
  2. package/dist/api/cached-client.d.ts.map +1 -0
  3. package/dist/api/cached-client.js +126 -0
  4. package/dist/api/cached-client.js.map +1 -0
  5. package/dist/api/client.d.ts +213 -0
  6. package/dist/api/client.d.ts.map +1 -0
  7. package/dist/api/client.js +326 -0
  8. package/dist/api/client.js.map +1 -0
  9. package/dist/auth/index.d.ts +8 -0
  10. package/dist/auth/index.d.ts.map +1 -0
  11. package/dist/auth/index.js +26 -0
  12. package/dist/auth/index.js.map +1 -0
  13. package/dist/auth/middleware.d.ts +36 -0
  14. package/dist/auth/middleware.d.ts.map +1 -0
  15. package/dist/auth/middleware.js +194 -0
  16. package/dist/auth/middleware.js.map +1 -0
  17. package/dist/auth/permissions.d.ts +60 -0
  18. package/dist/auth/permissions.d.ts.map +1 -0
  19. package/dist/auth/permissions.js +256 -0
  20. package/dist/auth/permissions.js.map +1 -0
  21. package/dist/auth/token-validator.d.ts +52 -0
  22. package/dist/auth/token-validator.d.ts.map +1 -0
  23. package/dist/auth/token-validator.js +217 -0
  24. package/dist/auth/token-validator.js.map +1 -0
  25. package/dist/cache/cache-manager.d.ts +49 -0
  26. package/dist/cache/cache-manager.d.ts.map +1 -0
  27. package/dist/cache/cache-manager.js +191 -0
  28. package/dist/cache/cache-manager.js.map +1 -0
  29. package/dist/cache/index.d.ts +6 -0
  30. package/dist/cache/index.d.ts.map +1 -0
  31. package/dist/cache/index.js +12 -0
  32. package/dist/cache/index.js.map +1 -0
  33. package/dist/cache/redis-client.d.ts +45 -0
  34. package/dist/cache/redis-client.d.ts.map +1 -0
  35. package/dist/cache/redis-client.js +210 -0
  36. package/dist/cache/redis-client.js.map +1 -0
  37. package/dist/config/constants.d.ts +28 -0
  38. package/dist/config/constants.d.ts.map +1 -0
  39. package/dist/config/constants.js +31 -0
  40. package/dist/config/constants.js.map +1 -0
  41. package/dist/config/index.d.ts +54 -0
  42. package/dist/config/index.d.ts.map +1 -0
  43. package/dist/config/index.js +168 -0
  44. package/dist/config/index.js.map +1 -0
  45. package/dist/filesystem/manager.d.ts +45 -0
  46. package/dist/filesystem/manager.d.ts.map +1 -0
  47. package/dist/filesystem/manager.js +246 -0
  48. package/dist/filesystem/manager.js.map +1 -0
  49. package/dist/git/multi-source-manager.d.ts +62 -0
  50. package/dist/git/multi-source-manager.d.ts.map +1 -0
  51. package/dist/git/multi-source-manager.js +293 -0
  52. package/dist/git/multi-source-manager.js.map +1 -0
  53. package/dist/git/operations.d.ts +27 -0
  54. package/dist/git/operations.d.ts.map +1 -0
  55. package/dist/git/operations.js +83 -0
  56. package/dist/git/operations.js.map +1 -0
  57. package/dist/index.d.ts +6 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +109 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/monitoring/health.d.ts +35 -0
  62. package/dist/monitoring/health.d.ts.map +1 -0
  63. package/dist/monitoring/health.js +105 -0
  64. package/dist/monitoring/health.js.map +1 -0
  65. package/dist/resources/index.d.ts +6 -0
  66. package/dist/resources/index.d.ts.map +1 -0
  67. package/dist/resources/index.js +10 -0
  68. package/dist/resources/index.js.map +1 -0
  69. package/dist/resources/loader.d.ts +87 -0
  70. package/dist/resources/loader.d.ts.map +1 -0
  71. package/dist/resources/loader.js +452 -0
  72. package/dist/resources/loader.js.map +1 -0
  73. package/dist/server/http.d.ts +57 -0
  74. package/dist/server/http.d.ts.map +1 -0
  75. package/dist/server/http.js +336 -0
  76. package/dist/server/http.js.map +1 -0
  77. package/dist/server.d.ts +13 -0
  78. package/dist/server.d.ts.map +1 -0
  79. package/dist/server.js +157 -0
  80. package/dist/server.js.map +1 -0
  81. package/dist/session/manager.d.ts +91 -0
  82. package/dist/session/manager.d.ts.map +1 -0
  83. package/dist/session/manager.js +251 -0
  84. package/dist/session/manager.js.map +1 -0
  85. package/dist/tools/index.d.ts +11 -0
  86. package/dist/tools/index.d.ts.map +1 -0
  87. package/dist/tools/index.js +27 -0
  88. package/dist/tools/index.js.map +1 -0
  89. package/dist/tools/manage-subscription.d.ts +43 -0
  90. package/dist/tools/manage-subscription.d.ts.map +1 -0
  91. package/dist/tools/manage-subscription.js +268 -0
  92. package/dist/tools/manage-subscription.js.map +1 -0
  93. package/dist/tools/registry.d.ts +40 -0
  94. package/dist/tools/registry.d.ts.map +1 -0
  95. package/dist/tools/registry.js +85 -0
  96. package/dist/tools/registry.js.map +1 -0
  97. package/dist/tools/search-resources.d.ts +31 -0
  98. package/dist/tools/search-resources.d.ts.map +1 -0
  99. package/dist/tools/search-resources.js +154 -0
  100. package/dist/tools/search-resources.js.map +1 -0
  101. package/dist/tools/sync-resources.d.ts +41 -0
  102. package/dist/tools/sync-resources.d.ts.map +1 -0
  103. package/dist/tools/sync-resources.js +606 -0
  104. package/dist/tools/sync-resources.js.map +1 -0
  105. package/dist/tools/uninstall-resource.d.ts +30 -0
  106. package/dist/tools/uninstall-resource.d.ts.map +1 -0
  107. package/dist/tools/uninstall-resource.js +259 -0
  108. package/dist/tools/uninstall-resource.js.map +1 -0
  109. package/dist/tools/upload-resource.d.ts +77 -0
  110. package/dist/tools/upload-resource.d.ts.map +1 -0
  111. package/dist/tools/upload-resource.js +252 -0
  112. package/dist/tools/upload-resource.js.map +1 -0
  113. package/dist/transport/sse.d.ts +29 -0
  114. package/dist/transport/sse.d.ts.map +1 -0
  115. package/dist/transport/sse.js +271 -0
  116. package/dist/transport/sse.js.map +1 -0
  117. package/dist/types/errors.d.ts +60 -0
  118. package/dist/types/errors.d.ts.map +1 -0
  119. package/dist/types/errors.js +112 -0
  120. package/dist/types/errors.js.map +1 -0
  121. package/dist/types/index.d.ts +7 -0
  122. package/dist/types/index.d.ts.map +1 -0
  123. package/dist/types/index.js +23 -0
  124. package/dist/types/index.js.map +1 -0
  125. package/dist/types/mcp.d.ts +50 -0
  126. package/dist/types/mcp.d.ts.map +1 -0
  127. package/dist/types/mcp.js +6 -0
  128. package/dist/types/mcp.js.map +1 -0
  129. package/dist/types/resources.d.ts +109 -0
  130. package/dist/types/resources.d.ts.map +1 -0
  131. package/dist/types/resources.js +7 -0
  132. package/dist/types/resources.js.map +1 -0
  133. package/dist/types/tools.d.ts +147 -0
  134. package/dist/types/tools.d.ts.map +1 -0
  135. package/dist/types/tools.js +6 -0
  136. package/dist/types/tools.js.map +1 -0
  137. package/dist/utils/cursor-paths.d.ts +49 -0
  138. package/dist/utils/cursor-paths.d.ts.map +1 -0
  139. package/dist/utils/cursor-paths.js +116 -0
  140. package/dist/utils/cursor-paths.js.map +1 -0
  141. package/dist/utils/log-cleaner.d.ts +18 -0
  142. package/dist/utils/log-cleaner.d.ts.map +1 -0
  143. package/dist/utils/log-cleaner.js +112 -0
  144. package/dist/utils/log-cleaner.js.map +1 -0
  145. package/dist/utils/logger.d.ts +59 -0
  146. package/dist/utils/logger.d.ts.map +1 -0
  147. package/dist/utils/logger.js +292 -0
  148. package/dist/utils/logger.js.map +1 -0
  149. package/dist/utils/validation.d.ts +58 -0
  150. package/dist/utils/validation.d.ts.map +1 -0
  151. package/dist/utils/validation.js +214 -0
  152. package/dist/utils/validation.js.map +1 -0
  153. package/package.json +58 -0
  154. package/src/api/cached-client.ts +144 -0
  155. package/src/api/client.ts +578 -0
  156. package/src/auth/index.ts +11 -0
  157. package/src/auth/middleware.ts +244 -0
  158. package/src/auth/permissions.ts +317 -0
  159. package/src/auth/token-validator.ts +294 -0
  160. package/src/cache/cache-manager.ts +243 -0
  161. package/src/cache/index.ts +6 -0
  162. package/src/cache/redis-client.ts +249 -0
  163. package/src/config/constants.ts +33 -0
  164. package/src/config/index.ts +228 -0
  165. package/src/filesystem/manager.ts +235 -0
  166. package/src/git/multi-source-manager.ts +333 -0
  167. package/src/git/operations.ts +93 -0
  168. package/src/index.ts +139 -0
  169. package/src/monitoring/health.ts +132 -0
  170. package/src/resources/index.ts +13 -0
  171. package/src/resources/loader.ts +530 -0
  172. package/src/server/http.ts +427 -0
  173. package/src/server.ts +191 -0
  174. package/src/session/manager.ts +296 -0
  175. package/src/tools/index.ts +11 -0
  176. package/src/tools/manage-subscription.ts +332 -0
  177. package/src/tools/registry.ts +97 -0
  178. package/src/tools/search-resources.ts +177 -0
  179. package/src/tools/sync-resources.ts +662 -0
  180. package/src/tools/uninstall-resource.ts +248 -0
  181. package/src/tools/upload-resource.ts +258 -0
  182. package/src/transport/sse.ts +308 -0
  183. package/src/types/errors.ts +146 -0
  184. package/src/types/index.ts +7 -0
  185. package/src/types/mcp.ts +61 -0
  186. package/src/types/resources.ts +141 -0
  187. package/src/types/tools.ts +175 -0
  188. package/src/utils/cursor-paths.ts +83 -0
  189. package/src/utils/log-cleaner.ts +92 -0
  190. package/src/utils/logger.ts +333 -0
  191. package/src/utils/validation.ts +262 -0
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Redis Client Module
3
+ * Connection management with retry strategy and basic cache operations
4
+ */
5
+
6
+ import Redis, { type Redis as RedisInstance } from 'ioredis';
7
+ import { logger, logError } from '../utils/logger';
8
+ import { config } from '../config';
9
+
10
+ const RETRY_BASE_MS = 100;
11
+ const RETRY_MAX_MS = 5000;
12
+ const RETRY_MAX_ATTEMPTS = 10;
13
+
14
+ /**
15
+ * Exponential backoff: base * 2^times, capped at max
16
+ */
17
+ function exponentialBackoff(times: number): number {
18
+ if (times > RETRY_MAX_ATTEMPTS) {
19
+ return 0; // Stop retrying
20
+ }
21
+ const delay = Math.min(RETRY_BASE_MS * Math.pow(2, times), RETRY_MAX_MS);
22
+ return delay;
23
+ }
24
+
25
+ export class RedisClient {
26
+ private static instance: RedisClient | null = null;
27
+ private client: RedisInstance | null = null;
28
+ private isConnecting = false;
29
+
30
+ private constructor() {}
31
+
32
+ static getInstance(): RedisClient {
33
+ if (RedisClient.instance === null) {
34
+ RedisClient.instance = new RedisClient();
35
+ }
36
+ return RedisClient.instance;
37
+ }
38
+
39
+ /**
40
+ * Connect to Redis with config and retry strategy
41
+ */
42
+ async connect(): Promise<void> {
43
+ const redisConfig = config.cache?.redis;
44
+ if (!redisConfig?.url) {
45
+ logger.warn(
46
+ { type: 'redis', event: 'connect_skipped' },
47
+ 'Redis URL not configured (REDIS_URL), skipping connection'
48
+ );
49
+ return;
50
+ }
51
+
52
+ if (this.client && this.isConnected()) {
53
+ logger.debug({ type: 'redis', event: 'connect' }, 'Redis already connected');
54
+ return;
55
+ }
56
+
57
+ if (this.isConnecting) {
58
+ logger.debug({ type: 'redis', event: 'connect' }, 'Redis connection in progress');
59
+ return;
60
+ }
61
+
62
+ this.isConnecting = true;
63
+
64
+ try {
65
+ this.client = new Redis(redisConfig.url, {
66
+ retryStrategy: (times: number) => {
67
+ const delay = exponentialBackoff(times);
68
+ if (delay === 0) {
69
+ logger.error(
70
+ { type: 'redis', event: 'retry_exhausted', times },
71
+ 'Redis retry exhausted, giving up'
72
+ );
73
+ return undefined;
74
+ }
75
+ logger.warn(
76
+ { type: 'redis', event: 'retry', times, delayMs: delay },
77
+ `Redis reconnecting in ${delay}ms (attempt ${times})`
78
+ );
79
+ return delay;
80
+ },
81
+ maxRetriesPerRequest: null,
82
+ connectTimeout: 10000,
83
+ lazyConnect: true,
84
+ });
85
+
86
+ this.client.on('connect', () => {
87
+ logger.info(
88
+ { type: 'redis', event: 'connect' },
89
+ 'Redis connection established'
90
+ );
91
+ });
92
+
93
+ this.client.on('ready', () => {
94
+ this.isConnecting = false;
95
+ logger.info(
96
+ { type: 'redis', event: 'ready' },
97
+ 'Redis client ready'
98
+ );
99
+ });
100
+
101
+ this.client.on('error', (err: Error) => {
102
+ logger.error(
103
+ { type: 'redis', event: 'error', error: err.message },
104
+ `Redis error: ${err.message}`
105
+ );
106
+ });
107
+
108
+ this.client.on('close', () => {
109
+ logger.info(
110
+ { type: 'redis', event: 'close' },
111
+ 'Redis connection closed'
112
+ );
113
+ });
114
+
115
+ await this.client.connect();
116
+ } catch (error) {
117
+ this.isConnecting = false;
118
+ if (this.client) {
119
+ this.client.disconnect();
120
+ this.client = null;
121
+ }
122
+ logError(error as Error, { type: 'redis', event: 'connect_failed' });
123
+ throw error;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Gracefully disconnect from Redis
129
+ */
130
+ async disconnect(): Promise<void> {
131
+ if (!this.client) {
132
+ logger.debug({ type: 'redis', event: 'disconnect' }, 'Redis not connected');
133
+ return;
134
+ }
135
+
136
+ try {
137
+ await this.client.quit();
138
+ this.client = null;
139
+ logger.info(
140
+ { type: 'redis', event: 'disconnect' },
141
+ 'Redis disconnected gracefully'
142
+ );
143
+ } catch (error) {
144
+ logError(error as Error, { type: 'redis', event: 'disconnect_failed' });
145
+ this.client = null;
146
+ throw error;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Check if Redis client is connected
152
+ */
153
+ isConnected(): boolean {
154
+ return this.client?.status === 'ready';
155
+ }
156
+
157
+ /**
158
+ * Get value by key
159
+ */
160
+ async get(key: string): Promise<string | null> {
161
+ if (!this.client) {
162
+ logger.warn({ type: 'redis', operation: 'get', key }, 'Redis not connected');
163
+ return null;
164
+ }
165
+
166
+ try {
167
+ const value = await this.client.get(key);
168
+ return value;
169
+ } catch (error) {
170
+ logError(error as Error, { type: 'redis', operation: 'get', key });
171
+ throw error;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Set value with optional TTL (seconds)
177
+ */
178
+ async set(key: string, value: string, ttl?: number): Promise<void> {
179
+ if (!this.client) {
180
+ logger.warn({ type: 'redis', operation: 'set', key }, 'Redis not connected');
181
+ throw new Error('Redis not connected');
182
+ }
183
+
184
+ try {
185
+ const ttlSeconds = ttl ?? config.cache?.redis?.ttl ?? 900;
186
+ await this.client.set(key, value, 'EX', ttlSeconds);
187
+ } catch (error) {
188
+ logError(error as Error, { type: 'redis', operation: 'set', key });
189
+ throw error;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Delete key
195
+ */
196
+ async del(key: string): Promise<void> {
197
+ if (!this.client) {
198
+ logger.warn({ type: 'redis', operation: 'del', key }, 'Redis not connected');
199
+ throw new Error('Redis not connected');
200
+ }
201
+
202
+ try {
203
+ await this.client.del(key);
204
+ } catch (error) {
205
+ logError(error as Error, { type: 'redis', operation: 'del', key });
206
+ throw error;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Clear all keys matching a pattern. Use with caution.
212
+ */
213
+ async clear(pattern = '*'): Promise<void> {
214
+ if (!this.client) {
215
+ logger.warn({ type: 'redis', operation: 'clear', pattern }, 'Redis not connected');
216
+ throw new Error('Redis not connected');
217
+ }
218
+
219
+ try {
220
+ const keys = await this.client.keys(pattern);
221
+ if (keys.length > 0) {
222
+ await this.client.del(...keys);
223
+ }
224
+ } catch (error) {
225
+ logError(error as Error, { type: 'redis', operation: 'clear', pattern });
226
+ throw error;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Check if key exists
232
+ */
233
+ async exists(key: string): Promise<boolean> {
234
+ if (!this.client) {
235
+ logger.warn({ type: 'redis', operation: 'exists', key }, 'Redis not connected');
236
+ return false;
237
+ }
238
+
239
+ try {
240
+ const result = await this.client.exists(key);
241
+ return result === 1;
242
+ } catch (error) {
243
+ logError(error as Error, { type: 'redis', operation: 'exists', key });
244
+ throw error;
245
+ }
246
+ }
247
+ }
248
+
249
+ export const redisClient = RedisClient.getInstance();
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Configuration Constants
3
+ */
4
+
5
+ export const APP_NAME = 'csp-ai-agent-mcp';
6
+ export const APP_VERSION = '0.1.0';
7
+
8
+ export const DEFAULT_PORT = 5090;
9
+ export const DEFAULT_LOG_LEVEL = 'info';
10
+ export const DEFAULT_API_TIMEOUT = 30000;
11
+
12
+ export const LOG_RETENTION_DAYS = 3;
13
+ export const LOG_DIR = 'logs';
14
+ export const LOG_FILE_PATTERN = 'app-{date}.log';
15
+
16
+ export const CACHE_TTL_SECONDS = 900; // 15 minutes
17
+ export const MEMORY_CACHE_SIZE = 20;
18
+
19
+ export const TOOL_NAMES = {
20
+ SYNC_RESOURCES: 'sync_resources',
21
+ MANAGE_SUBSCRIPTION: 'manage_subscription',
22
+ SEARCH_RESOURCES: 'search_resources',
23
+ UPLOAD_RESOURCE: 'upload_resource',
24
+ UNINSTALL_RESOURCE: 'uninstall_resource',
25
+ } as const;
26
+
27
+ export const TOOL_TIMEOUTS = {
28
+ [TOOL_NAMES.SEARCH_RESOURCES]: 10000, // 10s
29
+ [TOOL_NAMES.SYNC_RESOURCES]: 60000, // 60s
30
+ [TOOL_NAMES.UPLOAD_RESOURCE]: 120000, // 120s
31
+ [TOOL_NAMES.MANAGE_SUBSCRIPTION]: 15000, // 15s
32
+ [TOOL_NAMES.UNINSTALL_RESOURCE]: 5000, // 5s
33
+ } as const;
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Configuration Management Module
3
+ * Loads and validates configuration from environment variables
4
+ */
5
+
6
+ import * as dotenv from 'dotenv';
7
+ import * as path from 'path';
8
+ import * as fs from 'fs';
9
+
10
+ // Load .env file if exists
11
+ // Try multiple paths to find .env file
12
+ const possibleEnvPaths = [
13
+ path.resolve(process.cwd(), '.env'), // Current working directory
14
+ path.resolve(__dirname, '../../.env'), // Relative to compiled dist/config/
15
+ path.resolve(__dirname, '../../../.env'), // Project root (if deeper nesting)
16
+ ];
17
+
18
+ let envLoaded = false;
19
+ for (const envPath of possibleEnvPaths) {
20
+ if (fs.existsSync(envPath)) {
21
+ dotenv.config({ path: envPath });
22
+ console.log(`✓ Loaded .env from: ${envPath}`);
23
+ envLoaded = true;
24
+ break;
25
+ }
26
+ }
27
+
28
+ if (!envLoaded) {
29
+ console.warn('âš  No .env file found, using environment variables only');
30
+ }
31
+
32
+ export interface Config {
33
+ // Environment
34
+ nodeEnv: 'development' | 'production' | 'test';
35
+
36
+ // Server
37
+ port: number;
38
+ logLevel: 'debug' | 'info' | 'warn' | 'error';
39
+
40
+ // Transport
41
+ transport: {
42
+ mode: 'stdio' | 'sse';
43
+ };
44
+
45
+ // HTTP Server (for SSE transport)
46
+ http?: {
47
+ host: string;
48
+ port: number;
49
+ };
50
+
51
+ // Session (for SSE transport)
52
+ session?: {
53
+ timeout: number; // seconds
54
+ };
55
+
56
+ // CSP API
57
+ csp: {
58
+ apiBaseUrl: string;
59
+ apiToken: string;
60
+ timeout: number;
61
+ };
62
+
63
+ // Git — only commit author info; repo URLs/branches live in ai-resources-config.json
64
+ git: {
65
+ userName: string;
66
+ userEmail: string;
67
+ };
68
+
69
+ // Resource Storage — resolved at runtime via cursor-paths utility.
70
+ // No longer stored in config; use getCursorTypeDir() / getCursorResourcePath() directly.
71
+ // Kept as an empty marker so consumers know where to look.
72
+ resource: Record<string, never>;
73
+
74
+ // Cache (optional)
75
+ cache: {
76
+ enabled: boolean;
77
+ redis?: {
78
+ url: string;
79
+ ttl: number;
80
+ };
81
+ memory: {
82
+ maxSize: number;
83
+ };
84
+ };
85
+
86
+ // Database (optional)
87
+ database?: {
88
+ url: string;
89
+ };
90
+
91
+ // Monitoring (optional)
92
+ metrics: {
93
+ enabled: boolean;
94
+ port?: number;
95
+ };
96
+
97
+ // Logging
98
+ logging: {
99
+ dir: string;
100
+ retentionDays: number;
101
+ };
102
+ }
103
+
104
+ function getEnv(key: string, defaultValue?: string): string {
105
+ const value = process.env[key];
106
+ if (value === undefined) {
107
+ if (defaultValue !== undefined) {
108
+ return defaultValue;
109
+ }
110
+ throw new Error(`Missing required environment variable: ${key}`);
111
+ }
112
+ return value;
113
+ }
114
+
115
+ function getEnvNumber(key: string, defaultValue?: number): number {
116
+ const value = process.env[key];
117
+ if (value === undefined) {
118
+ if (defaultValue !== undefined) {
119
+ return defaultValue;
120
+ }
121
+ throw new Error(`Missing required environment variable: ${key}`);
122
+ }
123
+ const parsed = parseInt(value, 10);
124
+ if (isNaN(parsed)) {
125
+ throw new Error(`Environment variable ${key} must be a number, got: ${value}`);
126
+ }
127
+ return parsed;
128
+ }
129
+
130
+ function getEnvBoolean(key: string, defaultValue: boolean): boolean {
131
+ const value = process.env[key];
132
+ if (value === undefined) {
133
+ return defaultValue;
134
+ }
135
+ return value.toLowerCase() === 'true';
136
+ }
137
+
138
+ export function loadConfig(): Config {
139
+ const nodeEnv = (process.env.NODE_ENV || 'development') as Config['nodeEnv'];
140
+ const logLevel = (process.env.LOG_LEVEL || 'info') as Config['logLevel'];
141
+ const transportMode = (process.env.TRANSPORT_MODE || 'stdio') as 'stdio' | 'sse';
142
+
143
+ // Debug: Log CSP_API_TOKEN to verify it was loaded
144
+ console.log(`🔑 CSP_API_TOKEN from env: ${process.env.CSP_API_TOKEN ? process.env.CSP_API_TOKEN.substring(0, 20) + '...' : 'NOT SET'}`);
145
+
146
+ return {
147
+ nodeEnv,
148
+ port: getEnvNumber('PORT', 5090),
149
+ logLevel,
150
+
151
+ transport: {
152
+ mode: transportMode,
153
+ },
154
+
155
+ http: transportMode === 'sse' ? {
156
+ host: getEnv('HTTP_HOST', '0.0.0.0'),
157
+ port: getEnvNumber('HTTP_PORT', 3000),
158
+ } : undefined,
159
+
160
+ session: transportMode === 'sse' ? {
161
+ timeout: getEnvNumber('SESSION_TIMEOUT', 3600),
162
+ } : undefined,
163
+
164
+ csp: {
165
+ apiBaseUrl: getEnv('CSP_API_BASE_URL', 'https://csp.example.com'),
166
+ apiToken: getEnv('CSP_API_TOKEN', 'test-token-12345'),
167
+ timeout: getEnvNumber('CSP_API_TIMEOUT', 30000),
168
+ },
169
+
170
+ git: {
171
+ userName: getEnv('GIT_USER_NAME', 'CSP Agent'),
172
+ userEmail: getEnv('GIT_USER_EMAIL', 'agent@example.com'),
173
+ },
174
+
175
+ resource: {},
176
+
177
+ cache: {
178
+ enabled: getEnvBoolean('ENABLE_CACHE', true),
179
+ redis: process.env.REDIS_URL
180
+ ? {
181
+ url: process.env.REDIS_URL,
182
+ ttl: getEnvNumber('REDIS_TTL', 900),
183
+ }
184
+ : undefined,
185
+ memory: {
186
+ maxSize: getEnvNumber('CACHE_MAX_SIZE', 100),
187
+ },
188
+ },
189
+
190
+ database: process.env.DATABASE_URL
191
+ ? {
192
+ url: process.env.DATABASE_URL,
193
+ }
194
+ : undefined,
195
+
196
+ metrics: {
197
+ enabled: getEnvBoolean('ENABLE_METRICS', true),
198
+ port: process.env.METRICS_PORT ? getEnvNumber('METRICS_PORT') : undefined,
199
+ },
200
+
201
+ logging: {
202
+ dir: getEnv('LOG_DIR', '../Logs'),
203
+ retentionDays: getEnvNumber('LOG_RETENTION_DAYS', 3),
204
+ },
205
+ };
206
+ }
207
+
208
+ // Validate configuration on load
209
+ let config: Config;
210
+
211
+ try {
212
+ config = loadConfig();
213
+ } catch (error) {
214
+ // Use logger for consistent error reporting
215
+ const logger = require('../utils/logger').logger;
216
+ logger.error(
217
+ {
218
+ type: 'config',
219
+ operation: 'load_config',
220
+ error: (error as Error).message,
221
+ stack: (error as Error).stack
222
+ },
223
+ 'Configuration loading failed'
224
+ );
225
+ process.exit(1);
226
+ }
227
+
228
+ export { config };