@superdangerous/app-framework 4.9.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 (239) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +652 -0
  3. package/dist/api/logsRouter.d.ts +20 -0
  4. package/dist/api/logsRouter.d.ts.map +1 -0
  5. package/dist/api/logsRouter.js +515 -0
  6. package/dist/api/logsRouter.js.map +1 -0
  7. package/dist/cli/dev-server.d.ts +7 -0
  8. package/dist/cli/dev-server.d.ts.map +1 -0
  9. package/dist/cli/dev-server.js +640 -0
  10. package/dist/cli/dev-server.js.map +1 -0
  11. package/dist/cli/index.d.ts +7 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +26 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/core/StandardServer.d.ts +129 -0
  16. package/dist/core/StandardServer.d.ts.map +1 -0
  17. package/dist/core/StandardServer.js +453 -0
  18. package/dist/core/StandardServer.js.map +1 -0
  19. package/dist/core/apiResponse.d.ts +69 -0
  20. package/dist/core/apiResponse.d.ts.map +1 -0
  21. package/dist/core/apiResponse.js +127 -0
  22. package/dist/core/apiResponse.js.map +1 -0
  23. package/dist/core/healthCheck.d.ts +160 -0
  24. package/dist/core/healthCheck.d.ts.map +1 -0
  25. package/dist/core/healthCheck.js +398 -0
  26. package/dist/core/healthCheck.js.map +1 -0
  27. package/dist/core/index.d.ts +40 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +40 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/logger.d.ts +117 -0
  32. package/dist/core/logger.d.ts.map +1 -0
  33. package/dist/core/logger.js +826 -0
  34. package/dist/core/logger.js.map +1 -0
  35. package/dist/core/portUtils.d.ts +71 -0
  36. package/dist/core/portUtils.d.ts.map +1 -0
  37. package/dist/core/portUtils.js +240 -0
  38. package/dist/core/portUtils.js.map +1 -0
  39. package/dist/core/storageService.d.ts +119 -0
  40. package/dist/core/storageService.d.ts.map +1 -0
  41. package/dist/core/storageService.js +405 -0
  42. package/dist/core/storageService.js.map +1 -0
  43. package/dist/desktop/bundler.d.ts +40 -0
  44. package/dist/desktop/bundler.d.ts.map +1 -0
  45. package/dist/desktop/bundler.js +176 -0
  46. package/dist/desktop/bundler.js.map +1 -0
  47. package/dist/desktop/index.d.ts +25 -0
  48. package/dist/desktop/index.d.ts.map +1 -0
  49. package/dist/desktop/index.js +15 -0
  50. package/dist/desktop/index.js.map +1 -0
  51. package/dist/desktop/native-modules.d.ts +66 -0
  52. package/dist/desktop/native-modules.d.ts.map +1 -0
  53. package/dist/desktop/native-modules.js +200 -0
  54. package/dist/desktop/native-modules.js.map +1 -0
  55. package/dist/index.d.ts +29 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +39 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/logging/LogCategories.d.ts +87 -0
  60. package/dist/logging/LogCategories.d.ts.map +1 -0
  61. package/dist/logging/LogCategories.js +205 -0
  62. package/dist/logging/LogCategories.js.map +1 -0
  63. package/dist/middleware/aiErrorHandler.d.ts +31 -0
  64. package/dist/middleware/aiErrorHandler.d.ts.map +1 -0
  65. package/dist/middleware/aiErrorHandler.js +181 -0
  66. package/dist/middleware/aiErrorHandler.js.map +1 -0
  67. package/dist/middleware/auth.d.ts +101 -0
  68. package/dist/middleware/auth.d.ts.map +1 -0
  69. package/dist/middleware/auth.js +230 -0
  70. package/dist/middleware/auth.js.map +1 -0
  71. package/dist/middleware/cors.d.ts +56 -0
  72. package/dist/middleware/cors.d.ts.map +1 -0
  73. package/dist/middleware/cors.js +123 -0
  74. package/dist/middleware/cors.js.map +1 -0
  75. package/dist/middleware/errorHandler.d.ts +13 -0
  76. package/dist/middleware/errorHandler.d.ts.map +1 -0
  77. package/dist/middleware/errorHandler.js +85 -0
  78. package/dist/middleware/errorHandler.js.map +1 -0
  79. package/dist/middleware/fileUpload.d.ts +62 -0
  80. package/dist/middleware/fileUpload.d.ts.map +1 -0
  81. package/dist/middleware/fileUpload.js +175 -0
  82. package/dist/middleware/fileUpload.js.map +1 -0
  83. package/dist/middleware/health.d.ts +48 -0
  84. package/dist/middleware/health.d.ts.map +1 -0
  85. package/dist/middleware/health.js +143 -0
  86. package/dist/middleware/health.js.map +1 -0
  87. package/dist/middleware/index.d.ts +20 -0
  88. package/dist/middleware/index.d.ts.map +1 -0
  89. package/dist/middleware/index.js +18 -0
  90. package/dist/middleware/index.js.map +1 -0
  91. package/dist/middleware/openapi.d.ts +64 -0
  92. package/dist/middleware/openapi.d.ts.map +1 -0
  93. package/dist/middleware/openapi.js +258 -0
  94. package/dist/middleware/openapi.js.map +1 -0
  95. package/dist/middleware/requestLogging.d.ts +22 -0
  96. package/dist/middleware/requestLogging.d.ts.map +1 -0
  97. package/dist/middleware/requestLogging.js +61 -0
  98. package/dist/middleware/requestLogging.js.map +1 -0
  99. package/dist/middleware/session.d.ts +84 -0
  100. package/dist/middleware/session.d.ts.map +1 -0
  101. package/dist/middleware/session.js +189 -0
  102. package/dist/middleware/session.js.map +1 -0
  103. package/dist/middleware/validation.d.ts +1337 -0
  104. package/dist/middleware/validation.d.ts.map +1 -0
  105. package/dist/middleware/validation.js +483 -0
  106. package/dist/middleware/validation.js.map +1 -0
  107. package/dist/services/aiService.d.ts +180 -0
  108. package/dist/services/aiService.d.ts.map +1 -0
  109. package/dist/services/aiService.js +547 -0
  110. package/dist/services/aiService.js.map +1 -0
  111. package/dist/services/conversationStorage.d.ts +38 -0
  112. package/dist/services/conversationStorage.d.ts.map +1 -0
  113. package/dist/services/conversationStorage.js +158 -0
  114. package/dist/services/conversationStorage.js.map +1 -0
  115. package/dist/services/crossPlatformBuffer.d.ts +84 -0
  116. package/dist/services/crossPlatformBuffer.d.ts.map +1 -0
  117. package/dist/services/crossPlatformBuffer.js +246 -0
  118. package/dist/services/crossPlatformBuffer.js.map +1 -0
  119. package/dist/services/index.d.ts +17 -0
  120. package/dist/services/index.d.ts.map +1 -0
  121. package/dist/services/index.js +18 -0
  122. package/dist/services/index.js.map +1 -0
  123. package/dist/services/networkService.d.ts +81 -0
  124. package/dist/services/networkService.d.ts.map +1 -0
  125. package/dist/services/networkService.js +268 -0
  126. package/dist/services/networkService.js.map +1 -0
  127. package/dist/services/queueService.d.ts +112 -0
  128. package/dist/services/queueService.d.ts.map +1 -0
  129. package/dist/services/queueService.js +338 -0
  130. package/dist/services/queueService.js.map +1 -0
  131. package/dist/services/settingsService.d.ts +135 -0
  132. package/dist/services/settingsService.d.ts.map +1 -0
  133. package/dist/services/settingsService.js +425 -0
  134. package/dist/services/settingsService.js.map +1 -0
  135. package/dist/services/systemMonitor.d.ts +208 -0
  136. package/dist/services/systemMonitor.d.ts.map +1 -0
  137. package/dist/services/systemMonitor.js +693 -0
  138. package/dist/services/systemMonitor.js.map +1 -0
  139. package/dist/services/updateService.d.ts +78 -0
  140. package/dist/services/updateService.d.ts.map +1 -0
  141. package/dist/services/updateService.js +252 -0
  142. package/dist/services/updateService.js.map +1 -0
  143. package/dist/services/websocketEvents.d.ts +372 -0
  144. package/dist/services/websocketEvents.d.ts.map +1 -0
  145. package/dist/services/websocketEvents.js +338 -0
  146. package/dist/services/websocketEvents.js.map +1 -0
  147. package/dist/services/websocketServer.d.ts +80 -0
  148. package/dist/services/websocketServer.d.ts.map +1 -0
  149. package/dist/services/websocketServer.js +299 -0
  150. package/dist/services/websocketServer.js.map +1 -0
  151. package/dist/settings/SettingsSchema.d.ts +151 -0
  152. package/dist/settings/SettingsSchema.d.ts.map +1 -0
  153. package/dist/settings/SettingsSchema.js +424 -0
  154. package/dist/settings/SettingsSchema.js.map +1 -0
  155. package/dist/testing/TestServer.d.ts +69 -0
  156. package/dist/testing/TestServer.d.ts.map +1 -0
  157. package/dist/testing/TestServer.js +250 -0
  158. package/dist/testing/TestServer.js.map +1 -0
  159. package/dist/types/index.d.ts +137 -0
  160. package/dist/types/index.d.ts.map +1 -0
  161. package/dist/types/index.js +5 -0
  162. package/dist/types/index.js.map +1 -0
  163. package/dist/utils/appPaths.d.ts +74 -0
  164. package/dist/utils/appPaths.d.ts.map +1 -0
  165. package/dist/utils/appPaths.js +162 -0
  166. package/dist/utils/appPaths.js.map +1 -0
  167. package/dist/utils/fs-utils.d.ts +50 -0
  168. package/dist/utils/fs-utils.d.ts.map +1 -0
  169. package/dist/utils/fs-utils.js +114 -0
  170. package/dist/utils/fs-utils.js.map +1 -0
  171. package/dist/utils/index.d.ts +12 -0
  172. package/dist/utils/index.d.ts.map +1 -0
  173. package/dist/utils/index.js +10 -0
  174. package/dist/utils/index.js.map +1 -0
  175. package/dist/utils/standardConfig.d.ts +61 -0
  176. package/dist/utils/standardConfig.d.ts.map +1 -0
  177. package/dist/utils/standardConfig.js +109 -0
  178. package/dist/utils/standardConfig.js.map +1 -0
  179. package/dist/utils/startupBanner.d.ts +34 -0
  180. package/dist/utils/startupBanner.d.ts.map +1 -0
  181. package/dist/utils/startupBanner.js +169 -0
  182. package/dist/utils/startupBanner.js.map +1 -0
  183. package/dist/utils/startupLogger.d.ts +45 -0
  184. package/dist/utils/startupLogger.d.ts.map +1 -0
  185. package/dist/utils/startupLogger.js +200 -0
  186. package/dist/utils/startupLogger.js.map +1 -0
  187. package/package.json +151 -0
  188. package/src/api/logsRouter.ts +600 -0
  189. package/src/cli/dev-server.ts +803 -0
  190. package/src/cli/index.ts +31 -0
  191. package/src/core/StandardServer.ts +587 -0
  192. package/src/core/apiResponse.ts +202 -0
  193. package/src/core/healthCheck.ts +565 -0
  194. package/src/core/index.ts +80 -0
  195. package/src/core/logger.ts +1092 -0
  196. package/src/core/portUtils.ts +319 -0
  197. package/src/core/storageService.ts +595 -0
  198. package/src/desktop/bundler.ts +271 -0
  199. package/src/desktop/index.ts +18 -0
  200. package/src/desktop/native-modules.ts +289 -0
  201. package/src/index.ts +142 -0
  202. package/src/logging/LogCategories.ts +302 -0
  203. package/src/middleware/aiErrorHandler.ts +278 -0
  204. package/src/middleware/auth.ts +329 -0
  205. package/src/middleware/cors.ts +187 -0
  206. package/src/middleware/errorHandler.ts +103 -0
  207. package/src/middleware/fileUpload.ts +252 -0
  208. package/src/middleware/health.ts +206 -0
  209. package/src/middleware/index.ts +71 -0
  210. package/src/middleware/openapi.ts +305 -0
  211. package/src/middleware/requestLogging.ts +92 -0
  212. package/src/middleware/session.ts +238 -0
  213. package/src/middleware/validation.ts +603 -0
  214. package/src/services/aiService.ts +789 -0
  215. package/src/services/conversationStorage.ts +232 -0
  216. package/src/services/crossPlatformBuffer.ts +341 -0
  217. package/src/services/index.ts +47 -0
  218. package/src/services/networkService.ts +351 -0
  219. package/src/services/queueService.ts +446 -0
  220. package/src/services/settingsService.ts +549 -0
  221. package/src/services/systemMonitor.ts +936 -0
  222. package/src/services/updateService.ts +334 -0
  223. package/src/services/websocketEvents.ts +409 -0
  224. package/src/services/websocketServer.ts +394 -0
  225. package/src/settings/SettingsSchema.ts +664 -0
  226. package/src/testing/TestServer.ts +312 -0
  227. package/src/types/index.ts +154 -0
  228. package/src/utils/appPaths.ts +196 -0
  229. package/src/utils/fs-utils.ts +130 -0
  230. package/src/utils/index.ts +15 -0
  231. package/src/utils/standardConfig.ts +178 -0
  232. package/src/utils/startupBanner.ts +287 -0
  233. package/src/utils/startupLogger.ts +268 -0
  234. package/ui/dist/index.d.mts +1221 -0
  235. package/ui/dist/index.d.ts +1221 -0
  236. package/ui/dist/index.js +73 -0
  237. package/ui/dist/index.js.map +1 -0
  238. package/ui/dist/index.mjs +73 -0
  239. package/ui/dist/index.mjs.map +1 -0
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Log Categories System
3
+ * Provides a declarative way to define log categories with automatic UI generation
4
+ */
5
+
6
+ export interface LogLevel {
7
+ name: "error" | "warn" | "info" | "debug" | "verbose";
8
+ color: string;
9
+ badge: string;
10
+ priority: number;
11
+ }
12
+
13
+ export const LogLevels: Record<string, LogLevel> = {
14
+ error: {
15
+ name: "error",
16
+ color: "text-red-600 dark:text-red-400",
17
+ badge: "bg-red-600 text-white",
18
+ priority: 0,
19
+ },
20
+ warn: {
21
+ name: "warn",
22
+ color: "text-amber-600 dark:text-amber-400",
23
+ badge: "bg-amber-500 text-white",
24
+ priority: 1,
25
+ },
26
+ info: {
27
+ name: "info",
28
+ color: "text-blue-600 dark:text-blue-400",
29
+ badge: "bg-blue-600 text-white",
30
+ priority: 2,
31
+ },
32
+ debug: {
33
+ name: "debug",
34
+ color: "text-gray-600 dark:text-gray-400",
35
+ badge: "bg-gray-600 text-white",
36
+ priority: 3,
37
+ },
38
+ verbose: {
39
+ name: "verbose",
40
+ color: "text-gray-500 dark:text-gray-500",
41
+ badge: "bg-gray-500 text-white",
42
+ priority: 4,
43
+ },
44
+ };
45
+
46
+ export interface LogCategory {
47
+ id: string;
48
+ label: string;
49
+ description?: string;
50
+ color?: string;
51
+ icon?: string;
52
+ enabled?: boolean;
53
+ }
54
+
55
+ export interface LogFilter {
56
+ levels?: string[];
57
+ categories?: string[];
58
+ search?: string;
59
+ startDate?: Date;
60
+ endDate?: Date;
61
+ }
62
+
63
+ export interface LogViewerConfig {
64
+ categories: LogCategory[];
65
+ defaultLevel: string;
66
+ maxLogEntries: number;
67
+ enableRealtime: boolean;
68
+ enableExport: boolean;
69
+ enableArchives: boolean;
70
+ archiveRetentionDays: number;
71
+ horizontalScroll: boolean;
72
+ timestampFormat: string;
73
+ showCategories: boolean;
74
+ showMetadata: boolean;
75
+ theme?: "light" | "dark" | "system";
76
+ }
77
+
78
+ /**
79
+ * Default log viewer configuration
80
+ */
81
+ export const defaultLogViewerConfig: LogViewerConfig = {
82
+ categories: [
83
+ {
84
+ id: "system",
85
+ label: "System",
86
+ description: "Core system operations",
87
+ enabled: true,
88
+ },
89
+ {
90
+ id: "api",
91
+ label: "API",
92
+ description: "API requests and responses",
93
+ enabled: true,
94
+ },
95
+ {
96
+ id: "auth",
97
+ label: "Authentication",
98
+ description: "Authentication and authorization",
99
+ enabled: true,
100
+ },
101
+ {
102
+ id: "database",
103
+ label: "Database",
104
+ description: "Database operations",
105
+ enabled: true,
106
+ },
107
+ {
108
+ id: "service",
109
+ label: "Services",
110
+ description: "Background services",
111
+ enabled: true,
112
+ },
113
+ ],
114
+ defaultLevel: "info",
115
+ maxLogEntries: 1000,
116
+ enableRealtime: true,
117
+ enableExport: true,
118
+ enableArchives: true,
119
+ archiveRetentionDays: 7,
120
+ horizontalScroll: true,
121
+ timestampFormat: "YYYY-MM-DD HH:mm:ss.SSS",
122
+ showCategories: true,
123
+ showMetadata: true,
124
+ theme: "system",
125
+ };
126
+
127
+ /**
128
+ * Log entry interface
129
+ */
130
+ export interface LogEntry {
131
+ id: string;
132
+ timestamp: string;
133
+ level: keyof typeof LogLevels;
134
+ category?: string;
135
+ message: string;
136
+ metadata?: Record<string, any>;
137
+ source?: string;
138
+ userId?: string;
139
+ sessionId?: string;
140
+ requestId?: string;
141
+ stack?: string;
142
+ }
143
+
144
+ /**
145
+ * Log aggregation for summary statistics
146
+ */
147
+ export interface LogStats {
148
+ total: number;
149
+ byLevel: Record<string, number>;
150
+ byCategory: Record<string, number>;
151
+ errorRate: number;
152
+ warnRate: number;
153
+ }
154
+
155
+ /**
156
+ * Helper to parse log entries from different formats
157
+ */
158
+ export function parseLogEntry(raw: string | object): LogEntry | null {
159
+ try {
160
+ if (typeof raw === "string") {
161
+ // Try to parse JSON log format
162
+ if (raw.trim().startsWith("{")) {
163
+ return JSON.parse(raw);
164
+ }
165
+
166
+ // Parse text log format: "2023-10-20 10:30:45.123 [INFO] [Category] Message"
167
+ const match = raw.match(
168
+ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+\[(\w+)\]\s+(?:\[([^\]]+)\]\s+)?(.*)$/,
169
+ );
170
+ if (match) {
171
+ const [, timestamp, level, category, message] = match;
172
+ return {
173
+ id: `${Date.now()}-${Math.random()}`,
174
+ timestamp: timestamp || "",
175
+ level: (level?.toLowerCase() || "info") as keyof typeof LogLevels,
176
+ category: category || "",
177
+ message: message || "",
178
+ };
179
+ }
180
+ } else if (typeof raw === "object") {
181
+ return raw as LogEntry;
182
+ }
183
+ } catch (_error) {
184
+ console.error("Failed to parse log entry:", _error);
185
+ }
186
+
187
+ return null;
188
+ }
189
+
190
+ /**
191
+ * Helper to format log entries for display
192
+ */
193
+ export function formatLogEntry(
194
+ entry: LogEntry,
195
+ format: "full" | "compact" = "full",
196
+ ): string {
197
+ if (format === "compact") {
198
+ return `${entry.timestamp} [${entry.level.toUpperCase()}] ${entry.message}`;
199
+ }
200
+
201
+ let formatted = `${entry.timestamp} [${entry.level.toUpperCase()}]`;
202
+ if (entry.category) {
203
+ formatted += ` [${entry.category}]`;
204
+ }
205
+ formatted += ` ${entry.message}`;
206
+
207
+ if (entry.metadata && Object.keys(entry.metadata).length > 0) {
208
+ formatted += "\n Metadata: " + JSON.stringify(entry.metadata, null, 2);
209
+ }
210
+
211
+ if (entry.stack) {
212
+ formatted += "\n Stack: " + entry.stack;
213
+ }
214
+
215
+ return formatted;
216
+ }
217
+
218
+ /**
219
+ * Calculate log statistics
220
+ */
221
+ export function calculateLogStats(entries: LogEntry[]): LogStats {
222
+ const stats: LogStats = {
223
+ total: entries.length,
224
+ byLevel: {},
225
+ byCategory: {},
226
+ errorRate: 0,
227
+ warnRate: 0,
228
+ };
229
+
230
+ for (const entry of entries) {
231
+ // Count by level
232
+ stats.byLevel[entry.level] = (stats.byLevel[entry.level] || 0) + 1;
233
+
234
+ // Count by category
235
+ if (entry.category) {
236
+ stats.byCategory[entry.category] =
237
+ (stats.byCategory[entry.category] || 0) + 1;
238
+ }
239
+ }
240
+
241
+ // Calculate rates
242
+ if (stats.total > 0) {
243
+ stats.errorRate = (stats.byLevel.error || 0) / stats.total;
244
+ stats.warnRate = (stats.byLevel.warn || 0) / stats.total;
245
+ }
246
+
247
+ return stats;
248
+ }
249
+
250
+ /**
251
+ * Filter log entries
252
+ */
253
+ export function filterLogEntries(
254
+ entries: LogEntry[],
255
+ filter: LogFilter,
256
+ ): LogEntry[] {
257
+ return entries.filter((entry) => {
258
+ // Filter by level
259
+ if (
260
+ filter.levels &&
261
+ filter.levels.length > 0 &&
262
+ !filter.levels.includes(entry.level)
263
+ ) {
264
+ return false;
265
+ }
266
+
267
+ // Filter by category
268
+ if (
269
+ filter.categories &&
270
+ filter.categories.length > 0 &&
271
+ entry.category &&
272
+ !filter.categories.includes(entry.category)
273
+ ) {
274
+ return false;
275
+ }
276
+
277
+ // Filter by search term
278
+ if (filter.search) {
279
+ const searchLower = filter.search.toLowerCase();
280
+ const inMessage = entry.message.toLowerCase().includes(searchLower);
281
+ const inCategory = entry.category?.toLowerCase().includes(searchLower);
282
+ const inMetadata = entry.metadata
283
+ ? JSON.stringify(entry.metadata).toLowerCase().includes(searchLower)
284
+ : false;
285
+
286
+ if (!inMessage && !inCategory && !inMetadata) {
287
+ return false;
288
+ }
289
+ }
290
+
291
+ // Filter by date range
292
+ const entryDate = new Date(entry.timestamp);
293
+ if (filter.startDate && entryDate < filter.startDate) {
294
+ return false;
295
+ }
296
+ if (filter.endDate && entryDate > filter.endDate) {
297
+ return false;
298
+ }
299
+
300
+ return true;
301
+ });
302
+ }
@@ -0,0 +1,278 @@
1
+ /**
2
+ * AI Error Handler Middleware
3
+ * Provides user-friendly error messages for AI API errors
4
+ */
5
+
6
+ import { Request, Response, NextFunction } from "express";
7
+ import { createLogger } from "../core/index.js";
8
+ import { ApiResponse } from "../types/index.js";
9
+ import { sendError, sendUnauthorized } from "../core/apiResponse.js";
10
+ import { AIError } from "../services/aiService.js";
11
+
12
+ let logger: any; // Will be initialized when needed
13
+
14
+ function ensureLogger() {
15
+ if (!logger) {
16
+ logger = createLogger("AIError");
17
+ }
18
+ return logger;
19
+ }
20
+
21
+ interface AIErrorResponse {
22
+ error: string;
23
+ details?: string[];
24
+ action?: string;
25
+ retryAfter?: number;
26
+ code?: string;
27
+ }
28
+
29
+ /**
30
+ * AI-specific error handler middleware
31
+ */
32
+ export function aiErrorHandler(
33
+ err: any,
34
+ _req: Request,
35
+ res: Response<ApiResponse<any>>,
36
+ next: NextFunction,
37
+ ): void {
38
+ // Only handle AI-related errors
39
+ if (!_req.path.startsWith("/api/ai/")) {
40
+ return next(err);
41
+ }
42
+
43
+ ensureLogger().error("AI Error:", {
44
+ path: _req.path,
45
+ method: _req.method,
46
+ error: err.message,
47
+ status: err.statusCode || err.status,
48
+ stack: err.stack,
49
+ errorType: err.errorType,
50
+ provider: err.provider,
51
+ });
52
+
53
+ // Handle our custom AIError instances
54
+ if (err instanceof AIError) {
55
+ const response: AIErrorResponse = {
56
+ error: err.message,
57
+ code: err.errorType,
58
+ };
59
+
60
+ // Add provider-specific information
61
+ if (err.provider) {
62
+ response.details = [`Provider: ${err.provider}`];
63
+ }
64
+
65
+ // Handle specific error types
66
+ switch (err.errorType) {
67
+ case "AUTH_ERROR":
68
+ sendUnauthorized(res, err.message);
69
+ return;
70
+ case "RATE_LIMIT":
71
+ sendError(res, err.message, 429, { retryAfter: 60 });
72
+ return;
73
+ case "SERVICE_UNAVAILABLE":
74
+ sendError(res, err.message, 503);
75
+ return;
76
+ case "TIMEOUT":
77
+ sendError(res, err.message, 408);
78
+ return;
79
+ default:
80
+ sendError(res, err.message, err.statusCode, response);
81
+ return;
82
+ }
83
+ }
84
+
85
+ // Handle legacy structured AI errors with user-friendly messages
86
+ if (err.userMessage) {
87
+ const response: AIErrorResponse = {
88
+ error: err.userMessage,
89
+ };
90
+
91
+ if (err.details) {
92
+ response.details = err.details;
93
+ }
94
+
95
+ if (err.actionRequired) {
96
+ response.action = err.actionRequired;
97
+ }
98
+
99
+ if (err.code) {
100
+ response.code = err.code;
101
+ }
102
+
103
+ sendError(res, err.userMessage, err.status || 500, {
104
+ details: err.details,
105
+ action: err.actionRequired,
106
+ code: err.code,
107
+ });
108
+ return;
109
+ }
110
+
111
+ // Handle OpenAI specific errors
112
+ if (err.message && err.message.includes("401")) {
113
+ sendUnauthorized(
114
+ res,
115
+ "OpenAI API authentication failed: Invalid or missing API key. Check your OpenAI API key configuration",
116
+ );
117
+ return;
118
+ }
119
+
120
+ if (err.message && err.message.includes("429")) {
121
+ sendError(
122
+ res,
123
+ "OpenAI API rate limit exceeded: Too many requests. Please wait a moment before trying again",
124
+ 429,
125
+ );
126
+ return;
127
+ }
128
+
129
+ if (err.message && err.message.includes("insufficient_quota")) {
130
+ sendError(
131
+ res,
132
+ "OpenAI API quota exceeded: Your account has insufficient quota. Check your OpenAI account billing and usage limits",
133
+ 402,
134
+ );
135
+ return;
136
+ }
137
+
138
+ // Handle Anthropic specific errors
139
+ if (err.message && err.message.includes("anthropic")) {
140
+ if (err.message.includes("401")) {
141
+ sendUnauthorized(
142
+ res,
143
+ "Anthropic API authentication failed: Invalid or missing API key",
144
+ );
145
+ return;
146
+ }
147
+
148
+ if (err.message.includes("rate")) {
149
+ sendError(
150
+ res,
151
+ "Anthropic API rate limit exceeded: Too many requests",
152
+ 429,
153
+ );
154
+ return;
155
+ }
156
+ }
157
+
158
+ // Handle JSON parse errors
159
+ if (err.message && err.message.includes("JSON")) {
160
+ sendError(
161
+ res,
162
+ "Failed to generate valid template format: The AI response was not in the expected format",
163
+ 500,
164
+ );
165
+ return;
166
+ }
167
+
168
+ // Handle missing required fields
169
+ if (err.message && err.message.includes("missing required fields")) {
170
+ sendError(
171
+ res,
172
+ "Generated template is incomplete: Missing required fields",
173
+ 500,
174
+ );
175
+ return;
176
+ }
177
+
178
+ // Handle API key errors
179
+ if (err.message && err.message.includes("API key")) {
180
+ sendUnauthorized(
181
+ res,
182
+ "AI API key not configured: Please configure your API key",
183
+ );
184
+ return;
185
+ }
186
+
187
+ // Handle timeout errors
188
+ if (
189
+ err.message &&
190
+ (err.message.includes("timeout") || err.message.includes("ETIMEDOUT"))
191
+ ) {
192
+ sendError(
193
+ res,
194
+ "AI request timed out: The request took too long to complete",
195
+ 504,
196
+ );
197
+ return;
198
+ }
199
+
200
+ // Handle network errors
201
+ if (
202
+ err.message &&
203
+ (err.message.includes("ECONNREFUSED") || err.message.includes("ENOTFOUND"))
204
+ ) {
205
+ sendError(res, "AI service unavailable: Cannot connect to AI service", 503);
206
+ return;
207
+ }
208
+
209
+ // Default error response
210
+ sendError(
211
+ res,
212
+ err.message || "An error occurred while processing your AI request",
213
+ err.status || 500,
214
+ );
215
+ }
216
+
217
+ /**
218
+ * Rate limit handler for AI endpoints
219
+ */
220
+ export function aiRateLimitHandler(
221
+ _req: Request,
222
+ res: Response<ApiResponse<any>>,
223
+ ): void {
224
+ sendError(
225
+ res,
226
+ "Too many AI requests: Rate limit exceeded. Please wait a moment before trying again",
227
+ 429,
228
+ );
229
+ }
230
+
231
+ /**
232
+ * Create AI error with user-friendly message
233
+ */
234
+ export function createAIError(
235
+ message: string,
236
+ userMessage: string,
237
+ status: number = 500,
238
+ details?: string[],
239
+ action?: string,
240
+ ): AIError {
241
+ const error = new AIError(message, status, "CUSTOM_ERROR", "unknown");
242
+ // Additional legacy properties for compatibility
243
+ (error as any).userMessage = userMessage;
244
+ (error as any).details = details;
245
+ (error as any).actionRequired = action;
246
+ return error;
247
+ }
248
+
249
+ /**
250
+ * Wrap async AI handlers with error handling
251
+ */
252
+ export function wrapAIHandler(
253
+ handler: (req: Request, res: Response, next: NextFunction) => Promise<void>,
254
+ ) {
255
+ return async (req: Request, res: Response, next: NextFunction) => {
256
+ try {
257
+ await handler(req, res, next);
258
+ } catch (_error: any) {
259
+ // Convert to AI error if needed
260
+ if (!_error.userMessage && _error.response?.data) {
261
+ // Handle API response errors
262
+ const apiError = _error.response.data;
263
+ _error.userMessage =
264
+ apiError.error?.message || apiError.message || "AI request failed";
265
+ _error.status = _error.response.status;
266
+ _error.details = apiError.error?.details || [apiError.error?.type];
267
+ }
268
+ next(_error);
269
+ }
270
+ };
271
+ }
272
+
273
+ export default {
274
+ aiErrorHandler,
275
+ aiRateLimitHandler,
276
+ createAIError,
277
+ wrapAIHandler,
278
+ };