@loxia-labs/loxia-autopilot-one 1.0.1 → 1.0.3

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 (120) hide show
  1. package/README.md +44 -54
  2. package/bin/cli.js +1 -115
  3. package/bin/loxia-terminal-v2.js +3 -0
  4. package/bin/loxia-terminal.js +3 -0
  5. package/bin/start-with-terminal.js +3 -0
  6. package/package.json +14 -15
  7. package/scripts/install-scanners.js +1 -235
  8. package/src/analyzers/CSSAnalyzer.js +1 -297
  9. package/src/analyzers/ConfigValidator.js +1 -690
  10. package/src/analyzers/ESLintAnalyzer.js +1 -320
  11. package/src/analyzers/JavaScriptAnalyzer.js +1 -261
  12. package/src/analyzers/PrettierFormatter.js +1 -247
  13. package/src/analyzers/PythonAnalyzer.js +1 -266
  14. package/src/analyzers/SecurityAnalyzer.js +1 -729
  15. package/src/analyzers/TypeScriptAnalyzer.js +1 -247
  16. package/src/analyzers/codeCloneDetector/analyzer.js +1 -344
  17. package/src/analyzers/codeCloneDetector/detector.js +1 -203
  18. package/src/analyzers/codeCloneDetector/index.js +1 -160
  19. package/src/analyzers/codeCloneDetector/parser.js +1 -199
  20. package/src/analyzers/codeCloneDetector/reporter.js +1 -148
  21. package/src/analyzers/codeCloneDetector/scanner.js +1 -59
  22. package/src/core/agentPool.js +1 -1474
  23. package/src/core/agentScheduler.js +1 -2147
  24. package/src/core/contextManager.js +1 -709
  25. package/src/core/messageProcessor.js +1 -732
  26. package/src/core/orchestrator.js +1 -548
  27. package/src/core/stateManager.js +1 -877
  28. package/src/index.js +1 -631
  29. package/src/interfaces/cli.js +1 -549
  30. package/src/interfaces/terminal/__tests__/smoke/advancedFeatures.test.js +1 -0
  31. package/src/interfaces/terminal/__tests__/smoke/agentControl.test.js +1 -0
  32. package/src/interfaces/terminal/__tests__/smoke/agents.test.js +1 -0
  33. package/src/interfaces/terminal/__tests__/smoke/components.test.js +1 -0
  34. package/src/interfaces/terminal/__tests__/smoke/connection.test.js +1 -0
  35. package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +1 -0
  36. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +1 -0
  37. package/src/interfaces/terminal/__tests__/smoke/messages.test.js +1 -0
  38. package/src/interfaces/terminal/__tests__/smoke/tools.test.js +1 -0
  39. package/src/interfaces/terminal/api/apiClient.js +1 -0
  40. package/src/interfaces/terminal/api/messageRouter.js +1 -0
  41. package/src/interfaces/terminal/api/session.js +1 -0
  42. package/src/interfaces/terminal/api/websocket.js +1 -0
  43. package/src/interfaces/terminal/components/AgentCreator.js +1 -0
  44. package/src/interfaces/terminal/components/AgentEditor.js +1 -0
  45. package/src/interfaces/terminal/components/AgentSwitcher.js +1 -0
  46. package/src/interfaces/terminal/components/ErrorBoundary.js +1 -0
  47. package/src/interfaces/terminal/components/ErrorPanel.js +1 -0
  48. package/src/interfaces/terminal/components/Header.js +1 -0
  49. package/src/interfaces/terminal/components/HelpPanel.js +1 -0
  50. package/src/interfaces/terminal/components/InputBox.js +1 -0
  51. package/src/interfaces/terminal/components/Layout.js +1 -0
  52. package/src/interfaces/terminal/components/LoadingSpinner.js +1 -0
  53. package/src/interfaces/terminal/components/MessageList.js +1 -0
  54. package/src/interfaces/terminal/components/MultilineTextInput.js +1 -0
  55. package/src/interfaces/terminal/components/SearchPanel.js +1 -0
  56. package/src/interfaces/terminal/components/SettingsPanel.js +1 -0
  57. package/src/interfaces/terminal/components/StatusBar.js +1 -0
  58. package/src/interfaces/terminal/components/TextInput.js +1 -0
  59. package/src/interfaces/terminal/config/agentEditorConstants.js +1 -0
  60. package/src/interfaces/terminal/config/constants.js +1 -0
  61. package/src/interfaces/terminal/index.js +1 -0
  62. package/src/interfaces/terminal/state/useAgentControl.js +1 -0
  63. package/src/interfaces/terminal/state/useAgents.js +1 -0
  64. package/src/interfaces/terminal/state/useConnection.js +1 -0
  65. package/src/interfaces/terminal/state/useMessages.js +1 -0
  66. package/src/interfaces/terminal/state/useTools.js +1 -0
  67. package/src/interfaces/terminal/utils/debugLogger.js +1 -0
  68. package/src/interfaces/terminal/utils/settingsStorage.js +1 -0
  69. package/src/interfaces/terminal/utils/theme.js +1 -0
  70. package/src/interfaces/webServer.js +1 -2162
  71. package/src/modules/fileExplorer/controller.js +1 -280
  72. package/src/modules/fileExplorer/index.js +1 -37
  73. package/src/modules/fileExplorer/middleware.js +1 -92
  74. package/src/modules/fileExplorer/routes.js +1 -125
  75. package/src/modules/fileExplorer/types.js +1 -44
  76. package/src/services/aiService.js +1 -1232
  77. package/src/services/apiKeyManager.js +1 -164
  78. package/src/services/benchmarkService.js +1 -366
  79. package/src/services/budgetService.js +1 -539
  80. package/src/services/contextInjectionService.js +1 -247
  81. package/src/services/conversationCompactionService.js +1 -637
  82. package/src/services/errorHandler.js +1 -810
  83. package/src/services/fileAttachmentService.js +1 -544
  84. package/src/services/modelRouterService.js +1 -366
  85. package/src/services/modelsService.js +1 -322
  86. package/src/services/qualityInspector.js +1 -796
  87. package/src/services/tokenCountingService.js +1 -536
  88. package/src/tools/agentCommunicationTool.js +1 -1344
  89. package/src/tools/agentDelayTool.js +1 -485
  90. package/src/tools/asyncToolManager.js +1 -604
  91. package/src/tools/baseTool.js +1 -800
  92. package/src/tools/browserTool.js +1 -920
  93. package/src/tools/cloneDetectionTool.js +1 -621
  94. package/src/tools/dependencyResolverTool.js +1 -1215
  95. package/src/tools/fileContentReplaceTool.js +1 -875
  96. package/src/tools/fileSystemTool.js +1 -1107
  97. package/src/tools/fileTreeTool.js +1 -853
  98. package/src/tools/imageTool.js +1 -901
  99. package/src/tools/importAnalyzerTool.js +1 -1060
  100. package/src/tools/jobDoneTool.js +1 -248
  101. package/src/tools/seekTool.js +1 -956
  102. package/src/tools/staticAnalysisTool.js +1 -1778
  103. package/src/tools/taskManagerTool.js +1 -2873
  104. package/src/tools/terminalTool.js +1 -2304
  105. package/src/tools/webTool.js +1 -1430
  106. package/src/types/agent.js +1 -519
  107. package/src/types/contextReference.js +1 -972
  108. package/src/types/conversation.js +1 -730
  109. package/src/types/toolCommand.js +1 -747
  110. package/src/utilities/attachmentValidator.js +1 -292
  111. package/src/utilities/configManager.js +1 -582
  112. package/src/utilities/constants.js +1 -722
  113. package/src/utilities/directoryAccessManager.js +1 -535
  114. package/src/utilities/fileProcessor.js +1 -307
  115. package/src/utilities/logger.js +1 -436
  116. package/src/utilities/tagParser.js +1 -1246
  117. package/src/utilities/toolConstants.js +1 -317
  118. package/web-ui/build/index.html +2 -2
  119. package/web-ui/build/static/{index-Dy2bYbOa.css → index-CClD1090.css} +1 -1
  120. package/web-ui/build/static/{index-CjkkcnFA.js → index-lCBai6dX.js} +66 -67
@@ -1,2162 +1 @@
1
- /**
2
- * Web Server - HTTP and WebSocket server for web interface
3
- *
4
- * Purpose:
5
- * - Serve React frontend application
6
- * - Handle HTTP API requests
7
- * - Manage WebSocket connections for real-time updates
8
- * - File upload and project management
9
- */
10
-
11
- import express from 'express';
12
- import { createServer } from 'http';
13
- import { WebSocketServer } from 'ws';
14
- import path from 'path';
15
- import { promises as fs } from 'fs';
16
- import { fileURLToPath } from 'url';
17
-
18
- import {
19
- INTERFACE_TYPES,
20
- ORCHESTRATOR_ACTIONS,
21
- HTTP_STATUS,
22
- AGENT_MODES
23
- } from '../utilities/constants.js';
24
-
25
- // Import file explorer module
26
- import { initFileExplorerModule } from '../modules/fileExplorer/index.js';
27
-
28
- const __filename = fileURLToPath(import.meta.url);
29
- const __dirname = path.dirname(__filename);
30
-
31
- class WebServer {
32
- constructor(orchestrator, logger, config = {}) {
33
- this.orchestrator = orchestrator;
34
- this.logger = logger;
35
- this.config = config;
36
-
37
- this.port = config.port || 3000;
38
- this.host = config.host || 'localhost';
39
-
40
- // Express app
41
- this.app = express();
42
- this.server = createServer(this.app);
43
-
44
- // WebSocket server with CORS support
45
- this.wss = new WebSocketServer({
46
- server: this.server,
47
- // Allow all origins for WebSocket connections
48
- verifyClient: (info) => {
49
- // Log connection attempt for debugging
50
- this.logger?.info('WebSocket connection attempt', {
51
- origin: info.origin,
52
- host: info.req.headers.host,
53
- userAgent: info.req.headers['user-agent'],
54
- url: info.req.url
55
- });
56
-
57
- // Allow all origins (you can restrict this later if needed)
58
- return true;
59
- }
60
- });
61
-
62
- // Active WebSocket connections
63
- this.connections = new Map();
64
-
65
- // Session management
66
- this.sessions = new Map();
67
-
68
- // API Key Manager reference (will be set by LoxiaSystem)
69
- this.apiKeyManager = null;
70
-
71
- this.isRunning = false;
72
- }
73
-
74
- /**
75
- * Initialize web server
76
- * @returns {Promise<void>}
77
- */
78
- async initialize() {
79
- try {
80
- this.setupMiddleware();
81
- this.setupRoutes();
82
- this.setupWebSocket();
83
-
84
- await this.startServer();
85
-
86
- this.logger.info('Web server initialized', {
87
- port: this.port,
88
- host: this.host,
89
- url: `http://${this.host}:${this.port}`
90
- });
91
-
92
- } catch (error) {
93
- this.logger.error('Web server initialization failed', {
94
- error: error.message,
95
- stack: error.stack
96
- });
97
- throw error;
98
- }
99
- }
100
-
101
- /**
102
- * Setup Express middleware
103
- * @private
104
- */
105
- setupMiddleware() {
106
- // CORS middleware
107
- this.app.use((req, res, next) => {
108
- res.header('Access-Control-Allow-Origin', '*');
109
- res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
110
- res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
111
-
112
- if (req.method === 'OPTIONS') {
113
- res.sendStatus(HTTP_STATUS.OK);
114
- } else {
115
- next();
116
- }
117
- });
118
-
119
- // JSON parsing
120
- this.app.use(express.json({ limit: '10mb' }));
121
- this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));
122
-
123
- // Initialize file explorer module
124
- this.fileExplorerModule = initFileExplorerModule({
125
- showHidden: false,
126
- restrictedPaths: [] // Can be configured as needed
127
- });
128
-
129
- // Mount file explorer routes
130
- this.app.use('/api/file-explorer', this.fileExplorerModule.router);
131
-
132
- // Static files (React build)
133
- const staticPath = path.join(__dirname, '../../web-ui/build');
134
- this.app.use(express.static(staticPath));
135
-
136
- // Request logging
137
- this.app.use((req, res, next) => {
138
- this.logger.debug(`${req.method} ${req.path}`, {
139
- ip: req.ip,
140
- userAgent: req.get('User-Agent')
141
- });
142
- next();
143
- });
144
- }
145
-
146
- /**
147
- * Setup API routes
148
- * @private
149
- */
150
- setupRoutes() {
151
- // Health check
152
- this.app.get('/api/health', async (req, res) => {
153
- try {
154
- const packageJsonPath = path.join(__dirname, '../../package.json');
155
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
156
-
157
- res.json({
158
- status: 'healthy',
159
- version: packageJson.version || '1.0.0',
160
- timestamp: new Date().toISOString()
161
- });
162
- } catch (error) {
163
- res.json({
164
- status: 'healthy',
165
- version: '1.0.0',
166
- timestamp: new Date().toISOString()
167
- });
168
- }
169
- });
170
-
171
- // Session creation
172
- this.app.post('/api/sessions', async (req, res) => {
173
- try {
174
- const sessionId = `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
175
- const projectDir = req.body.projectDir || process.cwd();
176
-
177
- const session = {
178
- id: sessionId,
179
- projectDir,
180
- createdAt: new Date().toISOString(),
181
- lastActivity: new Date().toISOString()
182
- };
183
-
184
- this.sessions.set(sessionId, session);
185
-
186
- res.json({
187
- success: true,
188
- session
189
- });
190
-
191
- } catch (error) {
192
- res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
193
- success: false,
194
- error: error.message
195
- });
196
- }
197
- });
198
-
199
- // Orchestrator API proxy
200
- this.app.post('/api/orchestrator', async (req, res) => {
201
- try {
202
- const request = {
203
- interface: INTERFACE_TYPES.WEB,
204
- sessionId: req.body.sessionId,
205
- action: req.body.action,
206
- payload: req.body.payload,
207
- projectDir: req.body.projectDir || process.cwd()
208
- };
209
-
210
- const response = await this.orchestrator.processRequest(request);
211
-
212
- // Broadcast updates via WebSocket
213
- this.broadcastToSession(request.sessionId, {
214
- type: 'orchestrator_response',
215
- action: request.action,
216
- response
217
- });
218
-
219
- res.json(response);
220
-
221
- } catch (error) {
222
- this.logger.error('Orchestrator API error', {
223
- error: error.message,
224
- body: req.body
225
- });
226
-
227
- res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
228
- success: false,
229
- error: error.message
230
- });
231
- }
232
- });
233
-
234
- // File operations
235
- this.app.get('/api/files', async (req, res) => {
236
- try {
237
- const { path: filePath, projectDir } = req.query;
238
- const fullPath = path.resolve(projectDir || process.cwd(), filePath || '.');
239
-
240
- const stats = await fs.stat(fullPath);
241
-
242
- if (stats.isDirectory()) {
243
- const entries = await fs.readdir(fullPath, { withFileTypes: true });
244
- const files = entries.map(entry => ({
245
- name: entry.name,
246
- type: entry.isDirectory() ? 'directory' : 'file',
247
- path: path.join(filePath || '.', entry.name)
248
- }));
249
-
250
- res.json({ success: true, files });
251
- } else {
252
- const content = await fs.readFile(fullPath, 'utf8');
253
- res.json({ success: true, content, type: 'file' });
254
- }
255
-
256
- } catch (error) {
257
- res.status(HTTP_STATUS.NOT_FOUND).json({
258
- success: false,
259
- error: error.message
260
- });
261
- }
262
- });
263
-
264
- // File upload
265
- this.app.post('/api/files/upload', async (req, res) => {
266
- try {
267
- const { fileName, content, projectDir } = req.body;
268
- const targetDir = projectDir || process.cwd();
269
- const fullPath = path.resolve(targetDir, fileName);
270
-
271
- // Ensure the directory exists
272
- await fs.mkdir(targetDir, { recursive: true });
273
-
274
- // Write the file
275
- await fs.writeFile(fullPath, content, 'utf8');
276
-
277
- res.json({
278
- success: true,
279
- message: 'File uploaded successfully',
280
- path: fullPath
281
- });
282
-
283
- } catch (error) {
284
- res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
285
- success: false,
286
- error: error.message
287
- });
288
- }
289
- });
290
-
291
- // Enhanced folder explorer endpoint
292
- this.app.get('/api/explorer', async (req, res) => {
293
- try {
294
- const { path: requestedPath, showHidden = false } = req.query;
295
- const basePath = requestedPath || process.cwd();
296
- const fullPath = path.resolve(basePath);
297
-
298
- // Security: Basic path traversal protection
299
- const normalizedPath = path.normalize(fullPath);
300
-
301
- const stats = await fs.stat(normalizedPath);
302
-
303
- if (!stats.isDirectory()) {
304
- return res.status(HTTP_STATUS.BAD_REQUEST).json({
305
- success: false,
306
- error: 'Path is not a directory'
307
- });
308
- }
309
-
310
- const entries = await fs.readdir(normalizedPath, { withFileTypes: true });
311
-
312
- // Process entries
313
- const items = await Promise.all(
314
- entries
315
- .filter(entry => showHidden === 'true' || !entry.name.startsWith('.'))
316
- .map(async (entry) => {
317
- const itemPath = path.join(normalizedPath, entry.name);
318
- const itemStats = await fs.stat(itemPath).catch(() => null);
319
-
320
- return {
321
- name: entry.name,
322
- type: entry.isDirectory() ? 'directory' : 'file',
323
- path: itemPath,
324
- relativePath: path.relative(process.cwd(), itemPath),
325
- size: itemStats?.size || 0,
326
- modified: itemStats?.mtime || null,
327
- isHidden: entry.name.startsWith('.'),
328
- permissions: {
329
- readable: true, // Could check with fs.access
330
- writable: true // Could check with fs.access
331
- }
332
- };
333
- })
334
- );
335
-
336
- // Sort: directories first, then files, both alphabetically
337
- items.sort((a, b) => {
338
- if (a.type !== b.type) {
339
- return a.type === 'directory' ? -1 : 1;
340
- }
341
- return a.name.localeCompare(b.name);
342
- });
343
-
344
- // Get parent directory info
345
- const parentPath = path.dirname(normalizedPath);
346
- const hasParent = parentPath !== normalizedPath;
347
-
348
- res.json({
349
- success: true,
350
- currentPath: normalizedPath,
351
- currentRelativePath: path.relative(process.cwd(), normalizedPath),
352
- parentPath: hasParent ? parentPath : null,
353
- items,
354
- totalItems: items.length,
355
- directories: items.filter(item => item.type === 'directory').length,
356
- files: items.filter(item => item.type === 'file').length
357
- });
358
-
359
- } catch (error) {
360
- res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
361
- success: false,
362
- error: error.message,
363
- code: error.code
364
- });
365
- }
366
- });
367
-
368
- // Create directory endpoint
369
- this.app.post('/api/explorer/mkdir', async (req, res) => {
370
- try {
371
- const { path: dirPath, name } = req.body;
372
- const fullPath = path.resolve(dirPath, name);
373
-
374
- await fs.mkdir(fullPath, { recursive: false });
375
-
376
- res.json({
377
- success: true,
378
- path: fullPath,
379
- relativePath: path.relative(process.cwd(), fullPath)
380
- });
381
-
382
- } catch (error) {
383
- res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
384
- success: false,
385
- error: error.message,
386
- code: error.code
387
- });
388
- }
389
- });
390
-
391
- // Get directory info endpoint
392
- this.app.get('/api/explorer/info', async (req, res) => {
393
- try {
394
- const { path: requestedPath } = req.query;
395
- const fullPath = path.resolve(requestedPath);
396
-
397
- const stats = await fs.stat(fullPath);
398
-
399
- res.json({
400
- success: true,
401
- path: fullPath,
402
- relativePath: path.relative(process.cwd(), fullPath),
403
- isDirectory: stats.isDirectory(),
404
- isFile: stats.isFile(),
405
- size: stats.size,
406
- created: stats.birthtime,
407
- modified: stats.mtime,
408
- accessed: stats.atime
409
- });
410
-
411
- } catch (error) {
412
- res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
413
- success: false,
414
- error: error.message,
415
- code: error.code
416
- });
417
- }
418
- });
419
-
420
- // LLM Chat endpoint - proxy to Loxia Azure backend
421
- this.app.post('/api/llm/chat', async (req, res) => {
422
- try {
423
- const { sessionId, model, platformProvided } = req.body;
424
-
425
- if (!sessionId) {
426
- return res.status(400).json({
427
- error: 'Session ID is required'
428
- });
429
- }
430
-
431
- // Get API keys from session-based storage
432
- let apiKey = null;
433
- let vendorApiKey = null;
434
-
435
- if (this.apiKeyManager) {
436
- const keys = this.apiKeyManager.getKeysForRequest(sessionId, {
437
- platformProvided: platformProvided || false,
438
- vendor: this._getVendorFromModel(model)
439
- });
440
-
441
- apiKey = keys.loxiaApiKey;
442
- vendorApiKey = keys.vendorApiKey;
443
- }
444
-
445
- // Fallback to config/environment if no session keys
446
- if (!apiKey) {
447
- apiKey = this.config.loxiaApiKey || process.env.LOXIA_API_KEY;
448
- }
449
-
450
- // Also try API key from request body for backward compatibility
451
- if (!apiKey && req.body.apiKey) {
452
- apiKey = req.body.apiKey;
453
- }
454
-
455
- if (!apiKey) {
456
- this.logger.warn('No API key available for chat request', {
457
- sessionId,
458
- model,
459
- platformProvided,
460
- hasSessionManager: !!this.apiKeyManager
461
- });
462
-
463
- return res.status(401).json({
464
- error: 'No API key configured. Please configure your Loxia API key in Settings.'
465
- });
466
- }
467
-
468
- // Make request to Loxia Azure backend
469
- const azureBackendUrl = 'https://autopilot-api.azurewebsites.net/llm/chat';
470
-
471
- // Prepare request payload with API keys
472
- const requestPayload = {
473
- ...req.body,
474
- apiKey, // Loxia platform API key
475
- ...(vendorApiKey && { vendorApiKey }) // Include vendor key if available
476
- };
477
-
478
- const fetchOptions = {
479
- method: 'POST',
480
- headers: {
481
- 'Content-Type': 'application/json',
482
- 'Authorization': `Bearer ${apiKey}`
483
- },
484
- body: JSON.stringify(requestPayload)
485
- };
486
-
487
- this.logger.info('Proxying chat request to Loxia Azure backend', {
488
- url: azureBackendUrl,
489
- model: req.body.model,
490
- hasApiKey: !!apiKey
491
- });
492
-
493
- const response = await fetch(azureBackendUrl, fetchOptions);
494
-
495
- if (response.ok) {
496
- const data = await response.json();
497
- this.logger.info('Successfully received response from Azure backend', {
498
- model: data.model,
499
- contentLength: data.content?.length || 0
500
- });
501
-
502
- res.json(data);
503
- } else {
504
- const errorText = await response.text();
505
- this.logger.warn('Azure backend chat request failed', {
506
- status: response.status,
507
- statusText: response.statusText,
508
- error: errorText
509
- });
510
-
511
- res.status(response.status).json({
512
- error: `Azure backend error: ${response.status} ${response.statusText}`,
513
- details: errorText
514
- });
515
- }
516
-
517
- } catch (error) {
518
- this.logger.error('Failed to proxy chat request to Azure backend', {
519
- error: error.message,
520
- stack: error.stack
521
- });
522
-
523
- res.status(500).json({
524
- error: 'Failed to connect to AI service',
525
- details: error.message
526
- });
527
- }
528
- });
529
-
530
- // LLM Models endpoint - proxy to Loxia Azure backend
531
- this.app.get('/api/llm/models', async (req, res) => {
532
- try {
533
- // Get API key from authorization header if provided
534
- const authHeader = req.headers.authorization;
535
- let apiKey = null;
536
-
537
- if (authHeader && authHeader.startsWith('Bearer ')) {
538
- apiKey = authHeader.substring(7);
539
- }
540
-
541
- // If no API key, try to get from config or environment
542
- if (!apiKey) {
543
- apiKey = this.config.loxiaApiKey || process.env.LOXIA_API_KEY;
544
- }
545
-
546
- // Make request to Loxia Azure backend
547
- const azureBackendUrl = 'https://autopilot-api.azurewebsites.net/llm/models';
548
-
549
- const fetchOptions = {
550
- method: 'GET',
551
- headers: {
552
- 'Content-Type': 'application/json',
553
- ...(apiKey && { 'Authorization': `Bearer ${apiKey}` })
554
- }
555
- };
556
-
557
- this.logger.info('Fetching models from Loxia Azure backend', {
558
- url: azureBackendUrl,
559
- hasApiKey: !!apiKey
560
- });
561
-
562
- const response = await fetch(azureBackendUrl, fetchOptions);
563
-
564
- if (response.ok) {
565
- const data = await response.json();
566
- this.logger.info('Successfully fetched models from Azure backend', {
567
- modelCount: data.models?.length || 0
568
- });
569
-
570
- res.json(data);
571
- } else {
572
- // If authentication failed or other error, return fallback models
573
- const errorText = await response.text();
574
- this.logger.warn('Azure backend request failed, using fallback models', {
575
- status: response.status,
576
- statusText: response.statusText,
577
- error: errorText
578
- });
579
-
580
- res.json({
581
- models: this.getDefaultModels(),
582
- total: this.getDefaultModels().length,
583
- fallback: true,
584
- reason: `Azure backend error: ${response.status} ${response.statusText}`
585
- });
586
- }
587
-
588
- } catch (error) {
589
- this.logger.error('Failed to fetch models from Azure backend', {
590
- error: error.message,
591
- stack: error.stack
592
- });
593
-
594
- // Return fallback models on network or other errors
595
- res.json({
596
- models: this.getDefaultModels(),
597
- total: this.getDefaultModels().length,
598
- fallback: true,
599
- error: error.message
600
- });
601
- }
602
- });
603
-
604
- // Tools information endpoint - get available tools from registry
605
- this.app.get('/api/tools', async (req, res) => {
606
- try {
607
- // Get tools registry (passed from LoxiaSystem)
608
- const toolsRegistry = this.toolsRegistry;
609
-
610
- if (!toolsRegistry) {
611
- return res.status(500).json({
612
- error: 'Tools registry not available'
613
- });
614
- }
615
-
616
- // Get available tools for UI
617
- const tools = toolsRegistry.getAvailableToolsForUI();
618
-
619
- this.logger.info('Serving tools information', {
620
- toolCount: tools.length,
621
- tools: tools.map(t => ({ id: t.id, name: t.name, category: t.category }))
622
- });
623
-
624
- res.json({
625
- success: true,
626
- tools,
627
- total: tools.length
628
- });
629
-
630
- } catch (error) {
631
- this.logger.error('Failed to get tools information', {
632
- error: error.message,
633
- stack: error.stack
634
- });
635
-
636
- res.status(500).json({
637
- error: 'Failed to retrieve tools information',
638
- message: error.message
639
- });
640
- }
641
- });
642
-
643
- // API Key Management Endpoints
644
-
645
- // Set API keys for current session
646
- this.app.post('/api/keys', async (req, res) => {
647
- try {
648
- const { sessionId, loxiaApiKey, vendorKeys } = req.body;
649
-
650
- if (!sessionId) {
651
- return res.status(400).json({
652
- success: false,
653
- error: 'Session ID is required'
654
- });
655
- }
656
-
657
- if (!this.apiKeyManager) {
658
- return res.status(500).json({
659
- success: false,
660
- error: 'API key manager not available'
661
- });
662
- }
663
-
664
- // Set API keys for the session
665
- this.apiKeyManager.setSessionKeys(sessionId, {
666
- loxiaApiKey,
667
- vendorKeys: vendorKeys || {}
668
- });
669
-
670
- this.logger.info('API keys updated for session', {
671
- sessionId,
672
- hasLoxiaKey: !!loxiaApiKey,
673
- vendorKeys: Object.keys(vendorKeys || {})
674
- });
675
-
676
- // Refresh models with the new API key context
677
- if (this.orchestrator && this.orchestrator.modelsService && loxiaApiKey) {
678
- try {
679
- await this.orchestrator.modelsService.refresh({ sessionId, apiKey: loxiaApiKey });
680
- this.logger.info('Models refreshed with new API key', { sessionId });
681
- } catch (error) {
682
- this.logger.warn('Failed to refresh models after API key update', {
683
- error: error.message,
684
- sessionId
685
- });
686
- }
687
- }
688
-
689
- res.json({
690
- success: true,
691
- message: 'API keys updated successfully',
692
- sessionId,
693
- hasLoxiaKey: !!loxiaApiKey,
694
- vendorKeys: Object.keys(vendorKeys || {})
695
- });
696
-
697
- } catch (error) {
698
- this.logger.error('Failed to set API keys', {
699
- error: error.message,
700
- sessionId: req.body.sessionId
701
- });
702
-
703
- res.status(500).json({
704
- success: false,
705
- error: error.message
706
- });
707
- }
708
- });
709
-
710
- // Get API keys for current session (returns only presence, not values)
711
- this.app.get('/api/keys/:sessionId', async (req, res) => {
712
- try {
713
- const { sessionId } = req.params;
714
-
715
- if (!this.apiKeyManager) {
716
- return res.status(500).json({
717
- success: false,
718
- error: 'API key manager not available'
719
- });
720
- }
721
-
722
- const keys = this.apiKeyManager.getSessionKeys(sessionId);
723
-
724
- // Return key presence only, not actual values
725
- res.json({
726
- success: true,
727
- sessionId,
728
- hasLoxiaKey: !!keys.loxiaApiKey,
729
- vendorKeys: Object.keys(keys.vendorKeys || {}),
730
- updatedAt: keys.updatedAt
731
- });
732
-
733
- } catch (error) {
734
- this.logger.error('Failed to get API key status', {
735
- error: error.message,
736
- sessionId: req.params.sessionId
737
- });
738
-
739
- res.status(500).json({
740
- success: false,
741
- error: error.message
742
- });
743
- }
744
- });
745
-
746
- // Remove API keys for current session
747
- this.app.delete('/api/keys/:sessionId', async (req, res) => {
748
- try {
749
- const { sessionId } = req.params;
750
-
751
- if (!this.apiKeyManager) {
752
- return res.status(500).json({
753
- success: false,
754
- error: 'API key manager not available'
755
- });
756
- }
757
-
758
- const removed = this.apiKeyManager.removeSessionKeys(sessionId);
759
-
760
- res.json({
761
- success: true,
762
- removed,
763
- message: removed ? 'API keys removed successfully' : 'No API keys found for session'
764
- });
765
-
766
- } catch (error) {
767
- this.logger.error('Failed to remove API keys', {
768
- error: error.message,
769
- sessionId: req.params.sessionId
770
- });
771
-
772
- res.status(500).json({
773
- success: false,
774
- error: error.message
775
- });
776
- }
777
- });
778
-
779
- // Get active sessions with API keys (admin endpoint)
780
- this.app.get('/api/keys', async (req, res) => {
781
- try {
782
- if (!this.apiKeyManager) {
783
- return res.status(500).json({
784
- success: false,
785
- error: 'API key manager not available'
786
- });
787
- }
788
-
789
- const activeSessions = this.apiKeyManager.getActiveSessions();
790
-
791
- res.json({
792
- success: true,
793
- sessions: activeSessions,
794
- total: activeSessions.length
795
- });
796
-
797
- } catch (error) {
798
- this.logger.error('Failed to get active sessions', {
799
- error: error.message
800
- });
801
-
802
- res.status(500).json({
803
- success: false,
804
- error: error.message
805
- });
806
- }
807
- });
808
-
809
- // File Attachments Management Endpoints
810
-
811
- // Upload file attachment for agent
812
- this.app.post('/api/agents/:agentId/attachments/upload', async (req, res) => {
813
- try {
814
- const { agentId } = req.params;
815
- const { filePath, mode, fileName } = req.body;
816
-
817
- if (!this.orchestrator?.fileAttachmentService) {
818
- return res.status(500).json({
819
- success: false,
820
- error: 'File attachment service not available'
821
- });
822
- }
823
-
824
- const result = await this.orchestrator.fileAttachmentService.uploadFile({
825
- agentId,
826
- filePath,
827
- mode: mode || 'content',
828
- fileName
829
- });
830
-
831
- this.logger.info('File attachment uploaded', {
832
- agentId,
833
- fileId: result.fileId,
834
- fileName: result.fileName
835
- });
836
-
837
- res.json({
838
- success: true,
839
- attachment: result
840
- });
841
-
842
- } catch (error) {
843
- this.logger.error('Failed to upload file attachment', {
844
- agentId: req.params.agentId,
845
- error: error.message
846
- });
847
-
848
- res.status(500).json({
849
- success: false,
850
- error: error.message
851
- });
852
- }
853
- });
854
-
855
- // Get all attachments for an agent
856
- this.app.get('/api/agents/:agentId/attachments', async (req, res) => {
857
- try {
858
- const { agentId } = req.params;
859
- const { mode, active } = req.query;
860
-
861
- if (!this.orchestrator?.fileAttachmentService) {
862
- return res.status(500).json({
863
- success: false,
864
- error: 'File attachment service not available'
865
- });
866
- }
867
-
868
- const filters = {};
869
- if (mode) filters.mode = mode;
870
- if (active !== undefined) filters.active = active === 'true';
871
-
872
- const attachments = await this.orchestrator.fileAttachmentService.getAttachments(agentId, filters);
873
-
874
- res.json({
875
- success: true,
876
- attachments,
877
- total: attachments.length
878
- });
879
-
880
- } catch (error) {
881
- this.logger.error('Failed to get attachments', {
882
- agentId: req.params.agentId,
883
- error: error.message
884
- });
885
-
886
- res.status(500).json({
887
- success: false,
888
- error: error.message
889
- });
890
- }
891
- });
892
-
893
- // Get single attachment by ID
894
- this.app.get('/api/attachments/:fileId', async (req, res) => {
895
- try {
896
- const { fileId } = req.params;
897
-
898
- if (!this.orchestrator?.fileAttachmentService) {
899
- return res.status(500).json({
900
- success: false,
901
- error: 'File attachment service not available'
902
- });
903
- }
904
-
905
- const attachment = await this.orchestrator.fileAttachmentService.getAttachment(fileId);
906
-
907
- if (!attachment) {
908
- return res.status(404).json({
909
- success: false,
910
- error: 'Attachment not found'
911
- });
912
- }
913
-
914
- res.json({
915
- success: true,
916
- attachment
917
- });
918
-
919
- } catch (error) {
920
- this.logger.error('Failed to get attachment', {
921
- fileId: req.params.fileId,
922
- error: error.message
923
- });
924
-
925
- res.status(500).json({
926
- success: false,
927
- error: error.message
928
- });
929
- }
930
- });
931
-
932
- // Toggle attachment active status
933
- this.app.patch('/api/attachments/:fileId/toggle', async (req, res) => {
934
- try {
935
- const { fileId } = req.params;
936
-
937
- if (!this.orchestrator?.fileAttachmentService) {
938
- return res.status(500).json({
939
- success: false,
940
- error: 'File attachment service not available'
941
- });
942
- }
943
-
944
- const result = await this.orchestrator.fileAttachmentService.toggleActive(fileId);
945
-
946
- this.logger.info('Attachment active status toggled', {
947
- fileId,
948
- active: result.active
949
- });
950
-
951
- res.json({
952
- success: true,
953
- attachment: result
954
- });
955
-
956
- } catch (error) {
957
- this.logger.error('Failed to toggle attachment', {
958
- fileId: req.params.fileId,
959
- error: error.message
960
- });
961
-
962
- res.status(500).json({
963
- success: false,
964
- error: error.message
965
- });
966
- }
967
- });
968
-
969
- // Update attachment metadata
970
- this.app.patch('/api/attachments/:fileId', async (req, res) => {
971
- try {
972
- const { fileId } = req.params;
973
- const { mode, active } = req.body;
974
-
975
- if (!this.orchestrator?.fileAttachmentService) {
976
- return res.status(500).json({
977
- success: false,
978
- error: 'File attachment service not available'
979
- });
980
- }
981
-
982
- const updates = {};
983
- if (mode !== undefined) updates.mode = mode;
984
- if (active !== undefined) updates.active = active;
985
-
986
- const result = await this.orchestrator.fileAttachmentService.updateAttachment(fileId, updates);
987
-
988
- this.logger.info('Attachment updated', {
989
- fileId,
990
- updates
991
- });
992
-
993
- res.json({
994
- success: true,
995
- attachment: result
996
- });
997
-
998
- } catch (error) {
999
- this.logger.error('Failed to update attachment', {
1000
- fileId: req.params.fileId,
1001
- error: error.message
1002
- });
1003
-
1004
- res.status(500).json({
1005
- success: false,
1006
- error: error.message
1007
- });
1008
- }
1009
- });
1010
-
1011
- // Delete attachment
1012
- this.app.delete('/api/attachments/:fileId', async (req, res) => {
1013
- try {
1014
- const { fileId } = req.params;
1015
- const { agentId } = req.query;
1016
-
1017
- if (!agentId) {
1018
- return res.status(400).json({
1019
- success: false,
1020
- error: 'agentId query parameter is required'
1021
- });
1022
- }
1023
-
1024
- if (!this.orchestrator?.fileAttachmentService) {
1025
- return res.status(500).json({
1026
- success: false,
1027
- error: 'File attachment service not available'
1028
- });
1029
- }
1030
-
1031
- const result = await this.orchestrator.fileAttachmentService.deleteAttachment(fileId, agentId);
1032
-
1033
- this.logger.info('Attachment deleted', {
1034
- fileId,
1035
- agentId,
1036
- physicallyDeleted: result.physicallyDeleted
1037
- });
1038
-
1039
- res.json({
1040
- success: true,
1041
- message: result.physicallyDeleted ? 'Attachment deleted' : 'Reference removed',
1042
- physicallyDeleted: result.physicallyDeleted
1043
- });
1044
-
1045
- } catch (error) {
1046
- this.logger.error('Failed to delete attachment', {
1047
- fileId: req.params.fileId,
1048
- error: error.message
1049
- });
1050
-
1051
- res.status(500).json({
1052
- success: false,
1053
- error: error.message
1054
- });
1055
- }
1056
- });
1057
-
1058
- // Import attachment from another agent
1059
- this.app.post('/api/attachments/:fileId/import', async (req, res) => {
1060
- try {
1061
- const { fileId } = req.params;
1062
- const { targetAgentId } = req.body;
1063
-
1064
- if (!targetAgentId) {
1065
- return res.status(400).json({
1066
- success: false,
1067
- error: 'targetAgentId is required'
1068
- });
1069
- }
1070
-
1071
- if (!this.orchestrator?.fileAttachmentService) {
1072
- return res.status(500).json({
1073
- success: false,
1074
- error: 'File attachment service not available'
1075
- });
1076
- }
1077
-
1078
- const result = await this.orchestrator.fileAttachmentService.importFromAgent(fileId, targetAgentId);
1079
-
1080
- this.logger.info('Attachment imported', {
1081
- fileId,
1082
- targetAgentId
1083
- });
1084
-
1085
- res.json({
1086
- success: true,
1087
- attachment: result
1088
- });
1089
-
1090
- } catch (error) {
1091
- this.logger.error('Failed to import attachment', {
1092
- fileId: req.params.fileId,
1093
- error: error.message
1094
- });
1095
-
1096
- res.status(500).json({
1097
- success: false,
1098
- error: error.message
1099
- });
1100
- }
1101
- });
1102
-
1103
- // Get attachment preview
1104
- this.app.get('/api/attachments/:fileId/preview', async (req, res) => {
1105
- try {
1106
- const { fileId } = req.params;
1107
-
1108
- if (!this.orchestrator?.fileAttachmentService) {
1109
- return res.status(500).json({
1110
- success: false,
1111
- error: 'File attachment service not available'
1112
- });
1113
- }
1114
-
1115
- const preview = await this.orchestrator.fileAttachmentService.getAttachmentPreview(fileId);
1116
-
1117
- if (!preview) {
1118
- return res.status(404).json({
1119
- success: false,
1120
- error: 'Attachment not found'
1121
- });
1122
- }
1123
-
1124
- res.json({
1125
- success: true,
1126
- preview
1127
- });
1128
-
1129
- } catch (error) {
1130
- this.logger.error('Failed to get attachment preview', {
1131
- fileId: req.params.fileId,
1132
- error: error.message
1133
- });
1134
-
1135
- res.status(500).json({
1136
- success: false,
1137
- error: error.message
1138
- });
1139
- }
1140
- });
1141
-
1142
- // Image serving endpoint for generated images
1143
- this.app.get('/api/images/:sessionId/:filename', async (req, res) => {
1144
- try {
1145
- const { sessionId, filename } = req.params;
1146
-
1147
- // Security validation: Check if sessionId exists
1148
- const session = this.sessions.get(sessionId);
1149
- if (!session) {
1150
- return res.status(HTTP_STATUS.FORBIDDEN).json({
1151
- success: false,
1152
- error: 'Invalid session'
1153
- });
1154
- }
1155
-
1156
- // Security validation: Check filename for path traversal attempts
1157
- const normalizedFilename = path.basename(filename);
1158
- if (normalizedFilename !== filename || filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
1159
- return res.status(HTTP_STATUS.BAD_REQUEST).json({
1160
- success: false,
1161
- error: 'Invalid filename'
1162
- });
1163
- }
1164
-
1165
- // Try to locate the image in multiple possible locations
1166
- let imagePath = null;
1167
- const searchPaths = [
1168
- // 1. Session's project directory images folder
1169
- path.join(session.projectDir || process.cwd(), 'images', normalizedFilename),
1170
- // 2. Temp directory for this session
1171
- path.join('/tmp/loxia-images', sessionId, normalizedFilename),
1172
- // 3. General temp images directory
1173
- path.join('/tmp/loxia-images', normalizedFilename)
1174
- ];
1175
-
1176
- // 4. Search in agent working directories for this session
1177
- // Get all agents and check their workingDirectory
1178
- if (this.orchestrator?.agentPool) {
1179
- try {
1180
- const agents = await this.orchestrator.agentPool.getAllAgents();
1181
- for (const agent of agents) {
1182
- if (agent.directoryAccess?.workingDirectory) {
1183
- searchPaths.push(
1184
- path.join(agent.directoryAccess.workingDirectory, 'images', normalizedFilename)
1185
- );
1186
- }
1187
- }
1188
- } catch (error) {
1189
- this.logger.warn('Failed to get agent working directories for image search', {
1190
- error: error.message
1191
- });
1192
- }
1193
- }
1194
-
1195
- // Find the first existing file
1196
- for (const searchPath of searchPaths) {
1197
- try {
1198
- const stats = await fs.stat(searchPath);
1199
- if (stats.isFile()) {
1200
- imagePath = searchPath;
1201
- this.logger.info('Image found', {
1202
- filename: normalizedFilename,
1203
- path: imagePath,
1204
- sessionId
1205
- });
1206
- break;
1207
- }
1208
- } catch (error) {
1209
- // File doesn't exist at this path, try next one
1210
- continue;
1211
- }
1212
- }
1213
-
1214
- if (!imagePath) {
1215
- this.logger.warn('Image not found in any search path', {
1216
- filename: normalizedFilename,
1217
- sessionId,
1218
- searchPaths
1219
- });
1220
-
1221
- return res.status(HTTP_STATUS.NOT_FOUND).json({
1222
- success: false,
1223
- error: 'Image not found'
1224
- });
1225
- }
1226
-
1227
- // Serve the image file
1228
- res.sendFile(imagePath, (err) => {
1229
- if (err) {
1230
- this.logger.error('Failed to send image file', {
1231
- error: err.message,
1232
- imagePath,
1233
- filename: normalizedFilename
1234
- });
1235
-
1236
- if (!res.headersSent) {
1237
- res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
1238
- success: false,
1239
- error: 'Failed to serve image'
1240
- });
1241
- }
1242
- } else {
1243
- this.logger.info('Image served successfully', {
1244
- filename: normalizedFilename,
1245
- path: imagePath,
1246
- sessionId
1247
- });
1248
- }
1249
- });
1250
-
1251
- } catch (error) {
1252
- this.logger.error('Image serving error', {
1253
- error: error.message,
1254
- sessionId: req.params.sessionId,
1255
- filename: req.params.filename
1256
- });
1257
-
1258
- res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
1259
- success: false,
1260
- error: error.message
1261
- });
1262
- }
1263
- });
1264
-
1265
- // Agent mode control endpoints
1266
- this.app.post('/api/agents/:agentId/mode', async (req, res) => {
1267
- try {
1268
- const { agentId } = req.params;
1269
- const { mode, lockMode = false, sessionId: bodySessionId } = req.body;
1270
-
1271
- // Validate mode
1272
- if (!Object.values(AGENT_MODES).includes(mode)) {
1273
- return res.status(400).json({
1274
- success: false,
1275
- error: `Invalid mode. Must be one of: ${Object.values(AGENT_MODES).join(', ')}`
1276
- });
1277
- }
1278
-
1279
- // CRITICAL FIX: Use session ID from body first, then req.sessionID
1280
- const sessionId = bodySessionId || req.sessionID;
1281
-
1282
- if (!sessionId) {
1283
- this.logger.warn('Agent mode update requested without session ID', {
1284
- agentId,
1285
- hasBodySessionId: !!bodySessionId,
1286
- hasReqSessionId: !!req.sessionID
1287
- });
1288
- }
1289
-
1290
- // Update agent mode
1291
- const request = {
1292
- interface: INTERFACE_TYPES.WEB,
1293
- sessionId: sessionId,
1294
- action: 'update_agent',
1295
- payload: {
1296
- agentId,
1297
- updates: {
1298
- mode: mode // lockMode is no longer used, only CHAT and AGENT modes
1299
- }
1300
- }
1301
- };
1302
-
1303
- const response = await this.orchestrator.processRequest(request);
1304
-
1305
- if (response.success) {
1306
- // Extract agent from response.data (orchestrator wraps result in data property)
1307
- const updatedAgent = response.data;
1308
-
1309
- this.logger.info(`Agent mode updated: ${agentId}`, {
1310
- newMode: mode,
1311
- lockMode,
1312
- finalMode: updatedAgent?.mode
1313
- });
1314
-
1315
- // Broadcast mode change via WebSocket
1316
- this.broadcastToSession(request.sessionId, {
1317
- type: 'agent_mode_changed',
1318
- agentId,
1319
- mode: updatedAgent?.mode,
1320
- modeState: updatedAgent?.modeState
1321
- });
1322
-
1323
- res.json({
1324
- success: true,
1325
- agent: updatedAgent,
1326
- message: `Agent mode switched to ${updatedAgent?.mode}`
1327
- });
1328
- } else {
1329
- res.status(400).json({
1330
- success: false,
1331
- error: response.error || 'Failed to update agent mode'
1332
- });
1333
- }
1334
-
1335
- } catch (error) {
1336
- this.logger.error('Failed to update agent mode', {
1337
- agentId: req.params.agentId,
1338
- error: error.message
1339
- });
1340
-
1341
- res.status(500).json({
1342
- success: false,
1343
- error: error.message
1344
- });
1345
- }
1346
- });
1347
-
1348
- // Stop autonomous execution
1349
- this.app.post('/api/agents/:agentId/stop', async (req, res) => {
1350
- try {
1351
- const { agentId } = req.params;
1352
-
1353
- // Get message processor from orchestrator
1354
- const messageProcessor = this.orchestrator.messageProcessor;
1355
- if (!messageProcessor) {
1356
- return res.status(500).json({
1357
- success: false,
1358
- error: 'Message processor not available'
1359
- });
1360
- }
1361
-
1362
- const result = await messageProcessor.stopAutonomousExecution(agentId);
1363
-
1364
- this.logger.info(`Autonomous execution stop requested: ${agentId}`);
1365
-
1366
- // Broadcast stop event via WebSocket with updated agent state
1367
- // TODO: Get session ID from request body or agent context
1368
- const broadcastSessionId = req.sessionID || result.agent?.sessionId;
1369
- if (broadcastSessionId) {
1370
- this.broadcastToSession(broadcastSessionId, {
1371
- type: 'agent_mode_changed',
1372
- agentId,
1373
- mode: result.agent?.mode,
1374
- modeState: result.agent?.modeState
1375
- });
1376
- }
1377
-
1378
- res.json(result);
1379
-
1380
- } catch (error) {
1381
- this.logger.error('Failed to stop autonomous execution', {
1382
- agentId: req.params.agentId,
1383
- error: error.message
1384
- });
1385
-
1386
- res.status(500).json({
1387
- success: false,
1388
- error: error.message
1389
- });
1390
- }
1391
- });
1392
-
1393
- // Get agent mode status
1394
- this.app.get('/api/agents/:agentId/mode', async (req, res) => {
1395
- try {
1396
- const { agentId } = req.params;
1397
-
1398
- // TODO: Get session ID from query params or headers
1399
- const request = {
1400
- interface: INTERFACE_TYPES.WEB,
1401
- sessionId: req.sessionID,
1402
- action: 'get_agent_status',
1403
- payload: { agentId }
1404
- };
1405
-
1406
- const response = await this.orchestrator.processRequest(request);
1407
-
1408
- if (response.success && response.agent) {
1409
- res.json({
1410
- success: true,
1411
- mode: response.agent.mode,
1412
- modeState: response.agent.modeState,
1413
- currentTask: response.agent.currentTask,
1414
- iterationCount: response.agent.iterationCount,
1415
- taskStartTime: response.agent.taskStartTime,
1416
- stopRequested: response.agent.stopRequested
1417
- });
1418
- } else {
1419
- res.status(404).json({
1420
- success: false,
1421
- error: 'Agent not found'
1422
- });
1423
- }
1424
-
1425
- } catch (error) {
1426
- this.logger.error('Failed to get agent mode status', {
1427
- agentId: req.params.agentId,
1428
- error: error.message
1429
- });
1430
-
1431
- res.status(500).json({
1432
- success: false,
1433
- error: error.message
1434
- });
1435
- }
1436
- });
1437
-
1438
- // Agent Import/Resume Endpoints
1439
-
1440
- // Get all available agents (active + archived)
1441
- this.app.get('/api/agents/available', async (req, res) => {
1442
- try {
1443
- const projectDir = this.orchestrator.config.project?.directory || process.cwd();
1444
- const agents = await this.orchestrator.stateManager.getAllAvailableAgents(
1445
- projectDir,
1446
- this.orchestrator.agentPool
1447
- );
1448
-
1449
- res.json({
1450
- success: true,
1451
- agents: agents,
1452
- total: agents.length,
1453
- active: agents.filter(a => a.isLoaded).length,
1454
- archived: agents.filter(a => !a.isLoaded).length
1455
- });
1456
- } catch (error) {
1457
- this.logger.error('Failed to get available agents', {
1458
- error: error.message,
1459
- stack: error.stack
1460
- });
1461
-
1462
- res.status(500).json({
1463
- success: false,
1464
- error: error.message
1465
- });
1466
- }
1467
- });
1468
-
1469
- // Get agent metadata for preview
1470
- this.app.get('/api/agents/:agentId/metadata', async (req, res) => {
1471
- try {
1472
- const { agentId } = req.params;
1473
- const projectDir = this.orchestrator.config.project?.directory || process.cwd();
1474
-
1475
- const metadata = await this.orchestrator.stateManager.getAgentMetadata(
1476
- agentId,
1477
- projectDir
1478
- );
1479
-
1480
- res.json({
1481
- success: true,
1482
- metadata
1483
- });
1484
- } catch (error) {
1485
- this.logger.error('Failed to get agent metadata', {
1486
- agentId: req.params.agentId,
1487
- error: error.message
1488
- });
1489
-
1490
- res.status(404).json({
1491
- success: false,
1492
- error: error.message
1493
- });
1494
- }
1495
- });
1496
-
1497
- // Import archived agent
1498
- this.app.post('/api/agents/import', async (req, res) => {
1499
- try {
1500
- const { agentId } = req.body;
1501
-
1502
- if (!agentId) {
1503
- return res.status(400).json({
1504
- success: false,
1505
- error: 'agentId is required in request body'
1506
- });
1507
- }
1508
-
1509
- const projectDir = this.orchestrator.config.project?.directory || process.cwd();
1510
-
1511
- // Import the agent
1512
- const agent = await this.orchestrator.stateManager.importArchivedAgent(
1513
- agentId,
1514
- projectDir,
1515
- this.orchestrator.agentPool
1516
- );
1517
-
1518
- // Broadcast agent added event via WebSocket
1519
- if (this.wsManager) {
1520
- this.wsManager.broadcast({
1521
- type: 'agent-imported',
1522
- agent: {
1523
- id: agent.id,
1524
- name: agent.name,
1525
- status: agent.status,
1526
- capabilities: agent.capabilities,
1527
- model: agent.currentModel || agent.preferredModel
1528
- }
1529
- });
1530
- }
1531
-
1532
- this.logger.info('Agent imported successfully', {
1533
- agentId: agent.id,
1534
- name: agent.name
1535
- });
1536
-
1537
- res.json({
1538
- success: true,
1539
- agent: {
1540
- id: agent.id,
1541
- name: agent.name,
1542
- status: agent.status,
1543
- model: agent.currentModel || agent.preferredModel,
1544
- capabilities: agent.capabilities,
1545
- lastActivity: agent.lastActivity
1546
- },
1547
- message: `Agent ${agent.name} imported successfully`
1548
- });
1549
- } catch (error) {
1550
- this.logger.error('Failed to import agent', {
1551
- agentId: req.body.agentId,
1552
- error: error.message,
1553
- stack: error.stack
1554
- });
1555
-
1556
- // Determine appropriate status code
1557
- const statusCode = error.message.includes('already active') ? 409 :
1558
- error.message.includes('not found') ? 404 :
1559
- error.message.includes('Invalid') ? 400 : 500;
1560
-
1561
- res.status(statusCode).json({
1562
- success: false,
1563
- error: error.message
1564
- });
1565
- }
1566
- });
1567
-
1568
- // Serve React app for all other routes
1569
- this.app.get('*', (req, res) => {
1570
- const indexPath = path.join(__dirname, '../../web-ui/build/index.html');
1571
- res.sendFile(indexPath, (err) => {
1572
- if (err) {
1573
- res.status(HTTP_STATUS.NOT_FOUND).send('Web UI not built. Run: npm run build:ui');
1574
- }
1575
- });
1576
- });
1577
- }
1578
-
1579
- /**
1580
- * Setup WebSocket server
1581
- * @private
1582
- */
1583
- setupWebSocket() {
1584
- this.logger.info('Setting up WebSocket server', {
1585
- port: this.port,
1586
- host: this.host,
1587
- wsServerExists: !!this.wss,
1588
- httpServerExists: !!this.server
1589
- });
1590
-
1591
- // Add error handler for WebSocket server
1592
- this.wss.on('error', (error) => {
1593
- this.logger.error('WebSocket server error:', {
1594
- error: error.message,
1595
- stack: error.stack,
1596
- port: this.port
1597
- });
1598
- });
1599
-
1600
- // Log when WebSocket server is ready
1601
- this.wss.on('listening', () => {
1602
- this.logger.info('WebSocket server is now listening', {
1603
- port: this.port,
1604
- host: this.host
1605
- });
1606
- });
1607
-
1608
- this.wss.on('connection', (ws, req) => {
1609
- const connectionId = `conn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1610
-
1611
- this.logger.info('WebSocket connection established', {
1612
- connectionId,
1613
- ip: req.socket.remoteAddress,
1614
- origin: req.headers.origin,
1615
- host: req.headers.host,
1616
- userAgent: req.headers['user-agent'],
1617
- url: req.url
1618
- });
1619
-
1620
- // Store connection
1621
- const connection = {
1622
- id: connectionId,
1623
- ws,
1624
- sessionId: null,
1625
- connectedAt: new Date().toISOString(),
1626
- lastActivity: new Date().toISOString()
1627
- };
1628
-
1629
- this.connections.set(connectionId, connection);
1630
-
1631
- // Handle messages
1632
- ws.on('message', async (data) => {
1633
- try {
1634
- const message = JSON.parse(data.toString());
1635
- await this.handleWebSocketMessage(connectionId, message);
1636
- } catch (error) {
1637
- this.logger.error('WebSocket message error', {
1638
- connectionId,
1639
- error: error.message
1640
- });
1641
-
1642
- ws.send(JSON.stringify({
1643
- type: 'error',
1644
- error: error.message
1645
- }));
1646
- }
1647
- });
1648
-
1649
- // Handle disconnect
1650
- ws.on('close', () => {
1651
- this.logger.info('WebSocket connection closed', { connectionId });
1652
- this.connections.delete(connectionId);
1653
- });
1654
-
1655
- // Send welcome message
1656
- ws.send(JSON.stringify({
1657
- type: 'connected',
1658
- connectionId,
1659
- timestamp: new Date().toISOString()
1660
- }));
1661
- });
1662
- }
1663
-
1664
- /**
1665
- * Handle WebSocket message
1666
- * @private
1667
- */
1668
- async handleWebSocketMessage(connectionId, message) {
1669
- const connection = this.connections.get(connectionId);
1670
- if (!connection) return;
1671
-
1672
- connection.lastActivity = new Date().toISOString();
1673
-
1674
- switch (message.type) {
1675
- case 'join_session':
1676
- const sessionId = message.sessionId;
1677
- connection.sessionId = sessionId;
1678
-
1679
- this.logger.info('WebSocket joined session', {
1680
- connectionId,
1681
- sessionId,
1682
- totalConnectionsForSession: Array.from(this.connections.values()).filter(c => c.sessionId === sessionId).length
1683
- });
1684
-
1685
- connection.ws.send(JSON.stringify({
1686
- type: 'session_joined',
1687
- sessionId: sessionId
1688
- }));
1689
- break;
1690
-
1691
- case 'ping':
1692
- connection.ws.send(JSON.stringify({
1693
- type: 'pong',
1694
- timestamp: new Date().toISOString()
1695
- }));
1696
- break;
1697
-
1698
- case 'orchestrator_request':
1699
- // Handle real-time orchestrator requests
1700
- try {
1701
- const request = {
1702
- interface: INTERFACE_TYPES.WEB,
1703
- sessionId: connection.sessionId,
1704
- action: message.action,
1705
- payload: message.payload,
1706
- projectDir: message.projectDir || process.cwd()
1707
- };
1708
-
1709
- const response = await this.orchestrator.processRequest(request);
1710
-
1711
- connection.ws.send(JSON.stringify({
1712
- type: 'orchestrator_response',
1713
- requestId: message.requestId,
1714
- response
1715
- }));
1716
-
1717
- } catch (error) {
1718
- connection.ws.send(JSON.stringify({
1719
- type: 'error',
1720
- requestId: message.requestId,
1721
- error: error.message
1722
- }));
1723
- }
1724
- break;
1725
-
1726
- default:
1727
- this.logger.warn('Unknown WebSocket message type', {
1728
- connectionId,
1729
- type: message.type
1730
- });
1731
- }
1732
- }
1733
-
1734
- /**
1735
- * Broadcast message to all connections in a session
1736
- * @private
1737
- */
1738
- broadcastToSession(sessionId, message) {
1739
- const sessionConnections = Array.from(this.connections.values())
1740
- .filter(conn => conn.sessionId === sessionId);
1741
-
1742
- // If no connections found for this session, try broadcasting to all connections
1743
- // This handles cases where session IDs might be mismatched
1744
- let allConnections = [];
1745
- if (sessionConnections.length === 0) {
1746
- allConnections = Array.from(this.connections.values());
1747
-
1748
- this.logger?.warn('🔄 No connections for session, trying all connections:', {
1749
- targetSessionId: sessionId,
1750
- totalConnections: this.connections.size,
1751
- allSessionIds: Array.from(this.connections.values()).map(c => c.sessionId).filter(Boolean)
1752
- });
1753
- }
1754
-
1755
- const targetConnections = sessionConnections.length > 0 ? sessionConnections : allConnections;
1756
-
1757
- this.logger?.info('📡 WebSocket broadcastToSession called:', {
1758
- sessionId,
1759
- messageType: message.type,
1760
- agentId: message.agentId,
1761
- totalConnections: this.connections.size,
1762
- sessionConnections: sessionConnections.length,
1763
- targetConnections: targetConnections.length,
1764
- connectionIds: targetConnections.map(c => c.id),
1765
- usingFallback: sessionConnections.length === 0,
1766
- messagePreview: message.type === 'autonomous_update' && message.message ? {
1767
- messageId: message.message.id,
1768
- messageRole: message.message.role,
1769
- contentLength: message.message.content?.length,
1770
- hasToolResults: !!message.message.toolResults
1771
- } : undefined
1772
- });
1773
-
1774
- for (const connection of targetConnections) {
1775
- try {
1776
- const fullMessage = {
1777
- ...message,
1778
- timestamp: new Date().toISOString()
1779
- };
1780
-
1781
- connection.ws.send(JSON.stringify(fullMessage));
1782
-
1783
- this.logger?.info('✅ WebSocket message sent to connection:', {
1784
- connectionId: connection.id,
1785
- messageType: message.type,
1786
- agentId: message.agentId
1787
- });
1788
- } catch (error) {
1789
- this.logger.warn('❌ Failed to send WebSocket message', {
1790
- connectionId: connection.id,
1791
- sessionId,
1792
- messageType: message.type,
1793
- error: error.message
1794
- });
1795
- }
1796
- }
1797
- }
1798
-
1799
- /**
1800
- * Start the HTTP server
1801
- * @private
1802
- */
1803
- async startServer() {
1804
- return new Promise((resolve, reject) => {
1805
- this.server.listen(this.port, this.host, (error) => {
1806
- if (error) {
1807
- this.logger.error('Failed to start HTTP server', {
1808
- error: error.message,
1809
- port: this.port,
1810
- host: this.host
1811
- });
1812
- reject(error);
1813
- } else {
1814
- this.isRunning = true;
1815
-
1816
- // Verify WebSocket server status
1817
- this.logger.info('HTTP server started successfully', {
1818
- port: this.port,
1819
- host: this.host,
1820
- httpUrl: `http://${this.host}:${this.port}`,
1821
- wsUrl: `ws://${this.host}:${this.port}`,
1822
- wsServerAttached: !!this.wss,
1823
- wsConnections: this.connections.size
1824
- });
1825
-
1826
- // Test WebSocket server availability
1827
- setTimeout(async () => {
1828
- await this.testWebSocketServer();
1829
- }, 1000);
1830
-
1831
- resolve();
1832
- }
1833
- });
1834
-
1835
- // Add server error handler
1836
- this.server.on('error', (error) => {
1837
- this.logger.error('HTTP server error:', {
1838
- error: error.message,
1839
- stack: error.stack,
1840
- port: this.port
1841
- });
1842
- });
1843
- });
1844
- }
1845
-
1846
- /**
1847
- * Test WebSocket server availability
1848
- * @private
1849
- */
1850
- async testWebSocketServer() {
1851
- try {
1852
- const { default: WebSocket } = await import('ws');
1853
- const testWs = new WebSocket(`ws://localhost:${this.port}`);
1854
-
1855
- testWs.on('open', () => {
1856
- this.logger.info('✅ WebSocket server test: SUCCESSFUL', {
1857
- port: this.port,
1858
- url: `ws://localhost:${this.port}`
1859
- });
1860
- testWs.close();
1861
- });
1862
-
1863
- testWs.on('error', (error) => {
1864
- this.logger.error('❌ WebSocket server test: FAILED', {
1865
- port: this.port,
1866
- url: `ws://localhost:${this.port}`,
1867
- error: error.message,
1868
- code: error.code
1869
- });
1870
- });
1871
-
1872
- testWs.on('close', (code, reason) => {
1873
- if (code === 1000) {
1874
- this.logger.info('WebSocket test connection closed cleanly');
1875
- }
1876
- });
1877
-
1878
- // Timeout the test
1879
- setTimeout(() => {
1880
- if (testWs.readyState === WebSocket.CONNECTING) {
1881
- this.logger.error('❌ WebSocket server test: TIMEOUT', {
1882
- port: this.port,
1883
- url: `ws://localhost:${this.port}`,
1884
- readyState: testWs.readyState
1885
- });
1886
- testWs.terminate();
1887
- }
1888
- }, 5000);
1889
-
1890
- } catch (error) {
1891
- this.logger.error('❌ WebSocket server test: EXCEPTION', {
1892
- error: error.message,
1893
- stack: error.stack
1894
- });
1895
- }
1896
- }
1897
-
1898
- /**
1899
- * Get default models when API is unavailable
1900
- * @returns {Array} Default model list
1901
- * @private
1902
- */
1903
- getDefaultModels() {
1904
- return [
1905
- {
1906
- name: 'anthropic-sonnet',
1907
- category: 'anthropic',
1908
- type: 'chat',
1909
- maxTokens: 10000,
1910
- supportsVision: true,
1911
- supportsSystem: true,
1912
- pricing: {
1913
- input: 0.003,
1914
- output: 0.015,
1915
- unit: '1K tokens'
1916
- }
1917
- },
1918
- {
1919
- name: 'anthropic-opus',
1920
- category: 'anthropic',
1921
- type: 'chat',
1922
- maxTokens: 10000,
1923
- supportsVision: true,
1924
- supportsSystem: true,
1925
- pricing: {
1926
- input: 0.015,
1927
- output: 0.075,
1928
- unit: '1K tokens'
1929
- }
1930
- },
1931
- {
1932
- name: 'anthropic-haiku',
1933
- category: 'anthropic',
1934
- type: 'chat',
1935
- maxTokens: 10000,
1936
- supportsVision: false,
1937
- supportsSystem: true,
1938
- pricing: {
1939
- input: 0.0025,
1940
- output: 0.0125,
1941
- unit: '1K tokens'
1942
- }
1943
- },
1944
- {
1945
- name: 'gpt-4',
1946
- category: 'openai',
1947
- type: 'chat',
1948
- maxTokens: 8000,
1949
- supportsVision: true,
1950
- supportsSystem: true,
1951
- pricing: {
1952
- input: 0.03,
1953
- output: 0.06,
1954
- unit: '1K tokens'
1955
- }
1956
- },
1957
- {
1958
- name: 'gpt-4-mini',
1959
- category: 'openai',
1960
- type: 'chat',
1961
- maxTokens: 16000,
1962
- supportsVision: false,
1963
- supportsSystem: true,
1964
- pricing: {
1965
- input: 0.0015,
1966
- output: 0.006,
1967
- unit: '1K tokens'
1968
- }
1969
- },
1970
- {
1971
- name: 'deepseek-r1',
1972
- category: 'deepseek',
1973
- type: 'chat',
1974
- maxTokens: 8000,
1975
- supportsVision: false,
1976
- supportsSystem: true,
1977
- pricing: {
1978
- input: 0.002,
1979
- output: 0.008,
1980
- unit: '1K tokens'
1981
- }
1982
- },
1983
- {
1984
- name: 'phi-4',
1985
- category: 'microsoft',
1986
- type: 'chat',
1987
- maxTokens: 4000,
1988
- supportsVision: false,
1989
- supportsSystem: true,
1990
- pricing: {
1991
- input: 0.001,
1992
- output: 0.004,
1993
- unit: '1K tokens'
1994
- }
1995
- }
1996
- ];
1997
- }
1998
-
1999
- /**
2000
- * Set API key manager instance
2001
- * @param {ApiKeyManager} apiKeyManager - API key manager instance
2002
- */
2003
- setApiKeyManager(apiKeyManager) {
2004
- this.apiKeyManager = apiKeyManager;
2005
-
2006
- this.logger?.info('API key manager set for web server', {
2007
- hasManager: !!apiKeyManager
2008
- });
2009
- }
2010
-
2011
- /**
2012
- * Extract vendor name from model name
2013
- * @param {string} model - Model name
2014
- * @returns {string|null} Vendor name
2015
- * @private
2016
- */
2017
- _getVendorFromModel(model) {
2018
- if (!model) return null;
2019
-
2020
- const modelName = model.toLowerCase();
2021
-
2022
- if (modelName.includes('anthropic') || modelName.includes('claude')) {
2023
- return 'anthropic';
2024
- } else if (modelName.includes('openai') || modelName.includes('gpt')) {
2025
- return 'openai';
2026
- } else if (modelName.includes('deepseek')) {
2027
- return 'deepseek';
2028
- } else if (modelName.includes('phi')) {
2029
- return 'microsoft';
2030
- }
2031
-
2032
- return null;
2033
- }
2034
-
2035
- /**
2036
- * Get server status
2037
- * @returns {Object} Server status
2038
- */
2039
- getStatus() {
2040
- return {
2041
- isRunning: this.isRunning,
2042
- port: this.port,
2043
- host: this.host,
2044
- connections: this.connections.size,
2045
- sessions: this.sessions.size,
2046
- url: `http://${this.host}:${this.port}`
2047
- };
2048
- }
2049
-
2050
- /**
2051
- * Shutdown the web server
2052
- * @returns {Promise<void>}
2053
- */
2054
- async shutdown() {
2055
- if (!this.isRunning) return;
2056
-
2057
- this.logger.info('Shutting down web server...');
2058
-
2059
- // Close all WebSocket connections
2060
- for (const connection of this.connections.values()) {
2061
- connection.ws.close();
2062
- }
2063
- this.connections.clear();
2064
-
2065
- // Close WebSocket server
2066
- this.wss.close();
2067
-
2068
- // Close HTTP server
2069
- return new Promise((resolve) => {
2070
- this.server.close(() => {
2071
- this.isRunning = false;
2072
- this.logger.info('Web server shutdown complete');
2073
- resolve();
2074
- });
2075
- });
2076
- }
2077
- }
2078
-
2079
- export default WebServer;
2080
-
2081
- // Main execution block - start server if run directly
2082
- if (import.meta.url === `file://${process.argv[1]}`) {
2083
- // Simple console logger for standalone mode
2084
- const simpleLogger = {
2085
- info: (msg, data) => console.log(`[INFO] ${msg}`, data ? JSON.stringify(data, null, 2) : ''),
2086
- error: (msg, data) => console.error(`[ERROR] ${msg}`, data ? JSON.stringify(data, null, 2) : ''),
2087
- warn: (msg, data) => console.warn(`[WARN] ${msg}`, data ? JSON.stringify(data, null, 2) : ''),
2088
- debug: (msg, data) => console.log(`[DEBUG] ${msg}`, data ? JSON.stringify(data, null, 2) : '')
2089
- };
2090
-
2091
- // Simple orchestrator mock for standalone mode
2092
- const mockOrchestrator = {
2093
- processAction: async (action, data) => {
2094
- simpleLogger.info('Mock orchestrator action', { action, data });
2095
-
2096
- // Mock responses for different actions
2097
- switch (action) {
2098
- case ORCHESTRATOR_ACTIONS.LIST_AGENTS:
2099
- return {
2100
- success: true,
2101
- data: []
2102
- };
2103
-
2104
- case ORCHESTRATOR_ACTIONS.CREATE_AGENT:
2105
- return {
2106
- success: true,
2107
- data: {
2108
- id: `agent-${Date.now()}`,
2109
- name: data.name || 'New Agent',
2110
- status: 'active',
2111
- model: data.model || 'anthropic-sonnet',
2112
- systemPrompt: data.systemPrompt || 'You are a helpful AI assistant.'
2113
- }
2114
- };
2115
-
2116
- case ORCHESTRATOR_ACTIONS.SEND_MESSAGE:
2117
- return {
2118
- success: true,
2119
- data: {
2120
- message: {
2121
- id: `msg-${Date.now()}`,
2122
- content: `Echo: ${data.message}`,
2123
- timestamp: new Date().toISOString()
2124
- }
2125
- }
2126
- };
2127
-
2128
- default:
2129
- return {
2130
- success: false,
2131
- error: `Unknown action: ${action}`
2132
- };
2133
- }
2134
- }
2135
- };
2136
-
2137
- const server = new WebServer(mockOrchestrator, simpleLogger, {
2138
- port: 8080,
2139
- host: '0.0.0.0'
2140
- });
2141
-
2142
- console.log('🚀 Starting Loxia Web Server in standalone mode...');
2143
-
2144
- server.startServer()
2145
- .then(() => {
2146
- const status = server.getStatus();
2147
- console.log(`✅ Web Server running at ${status.url}`);
2148
- console.log('📱 Web UI available at: http://localhost:3001 (if running)');
2149
- console.log('🔧 API available at: http://localhost:8080/api');
2150
- })
2151
- .catch(error => {
2152
- console.error('❌ Failed to start web server:', error.message);
2153
- process.exit(1);
2154
- });
2155
-
2156
- // Graceful shutdown
2157
- process.on('SIGINT', async () => {
2158
- console.log('\n🛑 Shutting down web server...');
2159
- await server.shutdown();
2160
- process.exit(0);
2161
- });
2162
- }
1
+ const a0_0x433fed=a0_0x5128;(function(_0xeec459,_0x4839c5){const _0x218ca3=a0_0x5128,_0x1b53a7=_0xeec459();while(!![]){try{const _0x35f7bf=parseInt(_0x218ca3(0x257))/0x1+-parseInt(_0x218ca3(0x28c))/0x2*(parseInt(_0x218ca3(0x225))/0x3)+parseInt(_0x218ca3(0x2a4))/0x4+parseInt(_0x218ca3(0x1e4))/0x5*(-parseInt(_0x218ca3(0x2c0))/0x6)+-parseInt(_0x218ca3(0x28d))/0x7*(-parseInt(_0x218ca3(0x2c3))/0x8)+-parseInt(_0x218ca3(0x271))/0x9*(-parseInt(_0x218ca3(0x29e))/0xa)+-parseInt(_0x218ca3(0x25c))/0xb;if(_0x35f7bf===_0x4839c5)break;else _0x1b53a7['push'](_0x1b53a7['shift']());}catch(_0x414b22){_0x1b53a7['push'](_0x1b53a7['shift']());}}}(a0_0x269f,0xc1269));import a0_0x2ec7b9 from'express';import{createServer}from'http';import{WebSocketServer}from'ws';import a0_0x17b783 from'path';import{promises as a0_0x321fe9}from'fs';import{fileURLToPath}from'url';import{INTERFACE_TYPES,ORCHESTRATOR_ACTIONS,HTTP_STATUS,AGENT_MODES}from'../utilities/constants.js';import{initFileExplorerModule}from'../modules/fileExplorer/index.js';const __filename=fileURLToPath(import.meta.url),__dirname=a0_0x17b783[a0_0x433fed(0x24f)](__filename);class WebServer{constructor(_0x1f7e32,_0x2842d3,_0x41cfa1={}){const _0x20bcb1=a0_0x433fed;this['orchestrator']=_0x1f7e32,this['logger']=_0x2842d3,this[_0x20bcb1(0x2b7)]=_0x41cfa1,this['port']=_0x41cfa1['port']||0xbb8,this[_0x20bcb1(0x270)]=_0x41cfa1[_0x20bcb1(0x270)]||'localhost',this['app']=a0_0x2ec7b9(),this[_0x20bcb1(0x22e)]=createServer(this['app']),this['wss']=new WebSocketServer({'server':this[_0x20bcb1(0x22e)],'verifyClient':_0x3cbbd8=>{const _0x29a346=_0x20bcb1;return this['logger']?.['info'](_0x29a346(0x221),{'origin':_0x3cbbd8[_0x29a346(0x278)],'host':_0x3cbbd8[_0x29a346(0x292)][_0x29a346(0x297)][_0x29a346(0x270)],'userAgent':_0x3cbbd8[_0x29a346(0x292)]['headers']['user-agent'],'url':_0x3cbbd8[_0x29a346(0x292)]['url']}),!![];}}),this[_0x20bcb1(0x1eb)]=new Map(),this[_0x20bcb1(0x2b1)]=new Map(),this[_0x20bcb1(0x2b5)]=null,this[_0x20bcb1(0x2a5)]=![];}async[a0_0x433fed(0x273)](){const _0x31ed56=a0_0x433fed;try{this['setupMiddleware'](),this[_0x31ed56(0x242)](),this['setupWebSocket'](),await this['startServer'](),this['logger'][_0x31ed56(0x286)]('Web\x20server\x20initialized',{'port':this[_0x31ed56(0x249)],'host':this[_0x31ed56(0x270)],'url':'http://'+this['host']+':'+this['port']});}catch(_0x119a95){this['logger']['error']('Web\x20server\x20initialization\x20failed',{'error':_0x119a95[_0x31ed56(0x230)],'stack':_0x119a95['stack']});throw _0x119a95;}}['setupMiddleware'](){const _0x3f1877=a0_0x433fed;this[_0x3f1877(0x1e3)]['use']((_0x4e4e2b,_0x9716f6,_0x468264)=>{const _0x21efc6=_0x3f1877;_0x9716f6['header'](_0x21efc6(0x2c9),'*'),_0x9716f6['header'](_0x21efc6(0x2ca),'GET,\x20POST,\x20PUT,\x20DELETE,\x20OPTIONS'),_0x9716f6['header'](_0x21efc6(0x243),'Origin,\x20X-Requested-With,\x20Content-Type,\x20Accept,\x20Authorization'),_0x4e4e2b[_0x21efc6(0x2af)]===_0x21efc6(0x20f)?_0x9716f6[_0x21efc6(0x2c8)](HTTP_STATUS['OK']):_0x468264();}),this[_0x3f1877(0x1e3)]['use'](a0_0x2ec7b9['json']({'limit':'10mb'})),this['app'][_0x3f1877(0x1f9)](a0_0x2ec7b9['urlencoded']({'extended':!![],'limit':_0x3f1877(0x276)})),this['fileExplorerModule']=initFileExplorerModule({'showHidden':![],'restrictedPaths':[]}),this['app'][_0x3f1877(0x1f9)](_0x3f1877(0x29b),this[_0x3f1877(0x1ed)][_0x3f1877(0x2cd)]);const _0x4157bc=a0_0x17b783[_0x3f1877(0x21f)](__dirname,_0x3f1877(0x293));this['app'][_0x3f1877(0x1f9)](a0_0x2ec7b9['static'](_0x4157bc)),this['app'][_0x3f1877(0x1f9)]((_0x549e60,_0x351ecd,_0x5c02a9)=>{const _0x254b52=_0x3f1877;this[_0x254b52(0x21d)][_0x254b52(0x2bd)](_0x549e60['method']+'\x20'+_0x549e60[_0x254b52(0x259)],{'ip':_0x549e60['ip'],'userAgent':_0x549e60[_0x254b52(0x233)]('User-Agent')}),_0x5c02a9();});}['setupRoutes'](){const _0x3aaad6=a0_0x433fed;this['app']['get'](_0x3aaad6(0x290),async(_0x2e9c83,_0x9dace6)=>{const _0x35af80=_0x3aaad6;try{const _0x1da4f8=a0_0x17b783['join'](__dirname,'../../package.json'),_0x1e40db=JSON[_0x35af80(0x254)](await a0_0x321fe9[_0x35af80(0x212)](_0x1da4f8,_0x35af80(0x1da)));_0x9dace6[_0x35af80(0x213)]({'status':_0x35af80(0x226),'version':_0x1e40db['version']||'1.0.0','timestamp':new Date()[_0x35af80(0x27c)]()});}catch(_0x25e6a5){_0x9dace6['json']({'status':_0x35af80(0x226),'version':_0x35af80(0x1dd),'timestamp':new Date()['toISOString']()});}}),this['app'][_0x3aaad6(0x20c)](_0x3aaad6(0x220),async(_0x1eee42,_0x1b6160)=>{const _0x4e6127=_0x3aaad6;try{const _0x509887='web-'+Date[_0x4e6127(0x29d)]()+'-'+Math['random']()['toString'](0x24)[_0x4e6127(0x22d)](0x2,0x9),_0x3d970d=_0x1eee42[_0x4e6127(0x1e7)][_0x4e6127(0x20a)]||process[_0x4e6127(0x1df)](),_0x1ea4a8={'id':_0x509887,'projectDir':_0x3d970d,'createdAt':new Date()['toISOString'](),'lastActivity':new Date()[_0x4e6127(0x27c)]()};this[_0x4e6127(0x2b1)]['set'](_0x509887,_0x1ea4a8),_0x1b6160['json']({'success':!![],'session':_0x1ea4a8});}catch(_0xa1a29d){_0x1b6160[_0x4e6127(0x29c)](HTTP_STATUS[_0x4e6127(0x27a)])['json']({'success':![],'error':_0xa1a29d['message']});}}),this['app'][_0x3aaad6(0x20c)](_0x3aaad6(0x2ae),async(_0x5e42a9,_0x2d780d)=>{const _0x731e75=_0x3aaad6;try{const _0x1067be={'interface':INTERFACE_TYPES['WEB'],'sessionId':_0x5e42a9[_0x731e75(0x1e7)][_0x731e75(0x23f)],'action':_0x5e42a9['body']['action'],'payload':_0x5e42a9[_0x731e75(0x1e7)][_0x731e75(0x2c2)],'projectDir':_0x5e42a9['body'][_0x731e75(0x20a)]||process['cwd']()},_0x539243=await this['orchestrator'][_0x731e75(0x1ec)](_0x1067be);this['broadcastToSession'](_0x1067be['sessionId'],{'type':'orchestrator_response','action':_0x1067be['action'],'response':_0x539243}),_0x2d780d['json'](_0x539243);}catch(_0x1fc281){this['logger'][_0x731e75(0x256)](_0x731e75(0x27d),{'error':_0x1fc281['message'],'body':_0x5e42a9['body']}),_0x2d780d['status'](HTTP_STATUS['INTERNAL_SERVER_ERROR'])[_0x731e75(0x213)]({'success':![],'error':_0x1fc281[_0x731e75(0x230)]});}}),this[_0x3aaad6(0x1e3)]['get'](_0x3aaad6(0x26d),async(_0x3b2bf9,_0x52a503)=>{const _0x122f3f=_0x3aaad6;try{const {path:_0x34d871,projectDir:_0x5be697}=_0x3b2bf9[_0x122f3f(0x2d6)],_0x2cda94=a0_0x17b783['resolve'](_0x5be697||process[_0x122f3f(0x1df)](),_0x34d871||'.'),_0x558781=await a0_0x321fe9['stat'](_0x2cda94);if(_0x558781['isDirectory']()){const _0x6f4d8=await a0_0x321fe9['readdir'](_0x2cda94,{'withFileTypes':!![]}),_0x19e889=_0x6f4d8['map'](_0x4c8d0f=>({'name':_0x4c8d0f[_0x122f3f(0x294)],'type':_0x4c8d0f['isDirectory']()?_0x122f3f(0x2a0):'file','path':a0_0x17b783['join'](_0x34d871||'.',_0x4c8d0f['name'])}));_0x52a503['json']({'success':!![],'files':_0x19e889});}else{const _0xfbe063=await a0_0x321fe9[_0x122f3f(0x212)](_0x2cda94,_0x122f3f(0x1da));_0x52a503['json']({'success':!![],'content':_0xfbe063,'type':'file'});}}catch(_0x51597b){_0x52a503[_0x122f3f(0x29c)](HTTP_STATUS['NOT_FOUND'])['json']({'success':![],'error':_0x51597b[_0x122f3f(0x230)]});}}),this['app']['post'](_0x3aaad6(0x2cf),async(_0x39c401,_0x1eb459)=>{const _0x254337=_0x3aaad6;try{const {fileName:_0x2d10e6,content:_0x4f717a,projectDir:_0x134995}=_0x39c401[_0x254337(0x1e7)],_0x2bb4aa=_0x134995||process['cwd'](),_0x4a042e=a0_0x17b783[_0x254337(0x295)](_0x2bb4aa,_0x2d10e6);await a0_0x321fe9['mkdir'](_0x2bb4aa,{'recursive':!![]}),await a0_0x321fe9[_0x254337(0x200)](_0x4a042e,_0x4f717a,'utf8'),_0x1eb459[_0x254337(0x213)]({'success':!![],'message':'File\x20uploaded\x20successfully','path':_0x4a042e});}catch(_0x231a87){_0x1eb459['status'](HTTP_STATUS[_0x254337(0x27a)])['json']({'success':![],'error':_0x231a87[_0x254337(0x230)]});}}),this[_0x3aaad6(0x1e3)][_0x3aaad6(0x233)](_0x3aaad6(0x25f),async(_0x56715d,_0x2adf7f)=>{const _0xa6dbc6=_0x3aaad6;try{const {path:_0x983059,showHidden:showHidden=![]}=_0x56715d['query'],_0x427645=_0x983059||process[_0xa6dbc6(0x1df)](),_0x46688e=a0_0x17b783[_0xa6dbc6(0x295)](_0x427645),_0xbfd896=a0_0x17b783['normalize'](_0x46688e),_0x1def6c=await a0_0x321fe9['stat'](_0xbfd896);if(!_0x1def6c[_0xa6dbc6(0x2b8)]())return _0x2adf7f[_0xa6dbc6(0x29c)](HTTP_STATUS[_0xa6dbc6(0x24e)])[_0xa6dbc6(0x213)]({'success':![],'error':_0xa6dbc6(0x264)});const _0x56d25f=await a0_0x321fe9[_0xa6dbc6(0x267)](_0xbfd896,{'withFileTypes':!![]}),_0x48e5d4=await Promise[_0xa6dbc6(0x2da)](_0x56d25f['filter'](_0x250617=>showHidden==='true'||!_0x250617[_0xa6dbc6(0x294)]['startsWith']('.'))[_0xa6dbc6(0x2bf)](async _0x162738=>{const _0x556d2d=_0xa6dbc6,_0x58750a=a0_0x17b783[_0x556d2d(0x21f)](_0xbfd896,_0x162738['name']),_0x1b8b9c=await a0_0x321fe9['stat'](_0x58750a)[_0x556d2d(0x25e)](()=>null);return{'name':_0x162738['name'],'type':_0x162738[_0x556d2d(0x2b8)]()?'directory':'file','path':_0x58750a,'relativePath':a0_0x17b783[_0x556d2d(0x2a3)](process['cwd'](),_0x58750a),'size':_0x1b8b9c?.['size']||0x0,'modified':_0x1b8b9c?.[_0x556d2d(0x296)]||null,'isHidden':_0x162738['name']['startsWith']('.'),'permissions':{'readable':!![],'writable':!![]}};}));_0x48e5d4[_0xa6dbc6(0x255)]((_0x209d8a,_0x541920)=>{const _0x5174cb=_0xa6dbc6;if(_0x209d8a[_0x5174cb(0x1ff)]!==_0x541920[_0x5174cb(0x1ff)])return _0x209d8a['type']===_0x5174cb(0x2a0)?-0x1:0x1;return _0x209d8a[_0x5174cb(0x294)]['localeCompare'](_0x541920[_0x5174cb(0x294)]);});const _0x456954=a0_0x17b783[_0xa6dbc6(0x24f)](_0xbfd896),_0x5227fb=_0x456954!==_0xbfd896;_0x2adf7f[_0xa6dbc6(0x213)]({'success':!![],'currentPath':_0xbfd896,'currentRelativePath':a0_0x17b783['relative'](process['cwd'](),_0xbfd896),'parentPath':_0x5227fb?_0x456954:null,'items':_0x48e5d4,'totalItems':_0x48e5d4[_0xa6dbc6(0x22c)],'directories':_0x48e5d4[_0xa6dbc6(0x22f)](_0x363ec7=>_0x363ec7['type']==='directory')['length'],'files':_0x48e5d4['filter'](_0x1179cc=>_0x1179cc[_0xa6dbc6(0x1ff)]===_0xa6dbc6(0x223))[_0xa6dbc6(0x22c)]});}catch(_0xdc4b8a){_0x2adf7f[_0xa6dbc6(0x29c)](HTTP_STATUS['INTERNAL_SERVER_ERROR'])['json']({'success':![],'error':_0xdc4b8a['message'],'code':_0xdc4b8a[_0xa6dbc6(0x1f4)]});}}),this[_0x3aaad6(0x1e3)]['post']('/api/explorer/mkdir',async(_0x4d9e48,_0x26d2bf)=>{const _0x1727e7=_0x3aaad6;try{const {path:_0x3e2355,name:_0x564712}=_0x4d9e48['body'],_0x202695=a0_0x17b783[_0x1727e7(0x295)](_0x3e2355,_0x564712);await a0_0x321fe9['mkdir'](_0x202695,{'recursive':![]}),_0x26d2bf['json']({'success':!![],'path':_0x202695,'relativePath':a0_0x17b783[_0x1727e7(0x2a3)](process['cwd'](),_0x202695)});}catch(_0x115aab){_0x26d2bf[_0x1727e7(0x29c)](HTTP_STATUS[_0x1727e7(0x27a)])[_0x1727e7(0x213)]({'success':![],'error':_0x115aab[_0x1727e7(0x230)],'code':_0x115aab[_0x1727e7(0x1f4)]});}}),this[_0x3aaad6(0x1e3)][_0x3aaad6(0x233)](_0x3aaad6(0x1fc),async(_0x1bcb0c,_0x1f9a55)=>{const _0x3226a7=_0x3aaad6;try{const {path:_0xc775ee}=_0x1bcb0c['query'],_0x55d534=a0_0x17b783[_0x3226a7(0x295)](_0xc775ee),_0x1fe253=await a0_0x321fe9['stat'](_0x55d534);_0x1f9a55['json']({'success':!![],'path':_0x55d534,'relativePath':a0_0x17b783[_0x3226a7(0x2a3)](process['cwd'](),_0x55d534),'isDirectory':_0x1fe253['isDirectory'](),'isFile':_0x1fe253[_0x3226a7(0x224)](),'size':_0x1fe253[_0x3226a7(0x2d5)],'created':_0x1fe253['birthtime'],'modified':_0x1fe253['mtime'],'accessed':_0x1fe253['atime']});}catch(_0x1a78e3){_0x1f9a55['status'](HTTP_STATUS[_0x3226a7(0x27a)])[_0x3226a7(0x213)]({'success':![],'error':_0x1a78e3[_0x3226a7(0x230)],'code':_0x1a78e3[_0x3226a7(0x1f4)]});}}),this['app']['post']('/api/llm/chat',async(_0x4dfc59,_0x53c221)=>{const _0x29d9ed=_0x3aaad6;try{const {sessionId:_0x3928fb,model:_0x3b53c2,platformProvided:_0x486387}=_0x4dfc59[_0x29d9ed(0x1e7)];if(!_0x3928fb)return _0x53c221[_0x29d9ed(0x29c)](0x190)['json']({'error':'Session\x20ID\x20is\x20required'});let _0x1ebedc=null,_0x2ecf2e=null;if(this[_0x29d9ed(0x2b5)]){const _0x19d600=this['apiKeyManager'][_0x29d9ed(0x2b2)](_0x3928fb,{'platformProvided':_0x486387||![],'vendor':this['_getVendorFromModel'](_0x3b53c2)});_0x1ebedc=_0x19d600[_0x29d9ed(0x28f)],_0x2ecf2e=_0x19d600[_0x29d9ed(0x236)];}!_0x1ebedc&&(_0x1ebedc=this[_0x29d9ed(0x2b7)][_0x29d9ed(0x28f)]||process['env']['LOXIA_API_KEY']);!_0x1ebedc&&_0x4dfc59['body']['apiKey']&&(_0x1ebedc=_0x4dfc59[_0x29d9ed(0x1e7)][_0x29d9ed(0x27f)]);if(!_0x1ebedc)return this['logger'][_0x29d9ed(0x217)]('No\x20API\x20key\x20available\x20for\x20chat\x20request',{'sessionId':_0x3928fb,'model':_0x3b53c2,'platformProvided':_0x486387,'hasSessionManager':!!this[_0x29d9ed(0x2b5)]}),_0x53c221[_0x29d9ed(0x29c)](0x191)[_0x29d9ed(0x213)]({'error':_0x29d9ed(0x2cc)});const _0x23fef0='https://autopilot-api.azurewebsites.net/llm/chat',_0x57ca2f={..._0x4dfc59[_0x29d9ed(0x1e7)],'apiKey':_0x1ebedc,..._0x2ecf2e&&{'vendorApiKey':_0x2ecf2e}},_0xb4d6f4={'method':_0x29d9ed(0x1ef),'headers':{'Content-Type':'application/json','Authorization':_0x29d9ed(0x24d)+_0x1ebedc},'body':JSON[_0x29d9ed(0x2c6)](_0x57ca2f)};this['logger'][_0x29d9ed(0x286)](_0x29d9ed(0x26a),{'url':_0x23fef0,'model':_0x4dfc59[_0x29d9ed(0x1e7)]['model'],'hasApiKey':!!_0x1ebedc});const _0xed75a3=await fetch(_0x23fef0,_0xb4d6f4);if(_0xed75a3['ok']){const _0x1479b0=await _0xed75a3[_0x29d9ed(0x213)]();this['logger'][_0x29d9ed(0x286)]('Successfully received response from Azure backend',{'model':_0x1479b0[_0x29d9ed(0x2a9)],'contentLength':_0x1479b0['content']?.[_0x29d9ed(0x22c)]||0x0}),_0x53c221['json'](_0x1479b0);}else{const _0x44a8c2=await _0xed75a3[_0x29d9ed(0x1fb)]();this[_0x29d9ed(0x21d)]['warn']('Azure\x20backend\x20chat\x20request\x20failed',{'status':_0xed75a3[_0x29d9ed(0x29c)],'statusText':_0xed75a3[_0x29d9ed(0x239)],'error':_0x44a8c2}),_0x53c221['status'](_0xed75a3['status'])[_0x29d9ed(0x213)]({'error':_0x29d9ed(0x29f)+_0xed75a3[_0x29d9ed(0x29c)]+'\x20'+_0xed75a3[_0x29d9ed(0x239)],'details':_0x44a8c2});}}catch(_0x1769df){this[_0x29d9ed(0x21d)]['error']('Failed\x20to\x20proxy\x20chat\x20request\x20to\x20Azure\x20backend',{'error':_0x1769df[_0x29d9ed(0x230)],'stack':_0x1769df['stack']}),_0x53c221[_0x29d9ed(0x29c)](0x1f4)[_0x29d9ed(0x213)]({'error':'Failed\x20to\x20connect\x20to\x20AI\x20service','details':_0x1769df[_0x29d9ed(0x230)]});}}),this['app'][_0x3aaad6(0x233)](_0x3aaad6(0x21e),async(_0x14c32e,_0x193d9a)=>{const _0x18e0cb=_0x3aaad6;try{const _0x5450bc=_0x14c32e['headers'][_0x18e0cb(0x237)];let _0x3a46d6=null;_0x5450bc&&_0x5450bc['startsWith']('Bearer\x20')&&(_0x3a46d6=_0x5450bc['substring'](0x7));!_0x3a46d6&&(_0x3a46d6=this['config']['loxiaApiKey']||process[_0x18e0cb(0x1e9)]['LOXIA_API_KEY']);const _0x50ac37=_0x18e0cb(0x2a1),_0x3aea1d={'method':'GET','headers':{'Content-Type':_0x18e0cb(0x222),..._0x3a46d6&&{'Authorization':_0x18e0cb(0x24d)+_0x3a46d6}}};this['logger']['info']('Fetching models from Loxia Azure backend',{'url':_0x50ac37,'hasApiKey':!!_0x3a46d6});const _0x59e470=await fetch(_0x50ac37,_0x3aea1d);if(_0x59e470['ok']){const _0x5bfe60=await _0x59e470['json']();this['logger']['info']('Successfully fetched models from Azure backend',{'modelCount':_0x5bfe60['models']?.['length']||0x0}),_0x193d9a[_0x18e0cb(0x213)](_0x5bfe60);}else{const _0x24e80f=await _0x59e470['text']();this[_0x18e0cb(0x21d)][_0x18e0cb(0x256)](_0x18e0cb(0x21a),{'status':_0x59e470[_0x18e0cb(0x29c)],'statusText':_0x59e470[_0x18e0cb(0x239)],'error':_0x24e80f}),_0x193d9a[_0x18e0cb(0x29c)](_0x59e470['status'])['json']({'success':![],'error':'Failed\x20to\x20fetch\x20models\x20from\x20backend:\x20'+_0x59e470['status']+'\x20'+_0x59e470['statusText'],'details':_0x24e80f});}}catch(_0x2f5db9){this['logger']['error']('Failed to fetch models from Azure backend - NO FALLBACK',{'error':_0x2f5db9[_0x18e0cb(0x230)],'stack':_0x2f5db9[_0x18e0cb(0x2be)]}),_0x193d9a['status'](0x1f4)['json']({'success':![],'error':'Failed to fetch models from backend','details':_0x2f5db9[_0x18e0cb(0x230)]});}}),this[_0x3aaad6(0x1e3)]['get'](_0x3aaad6(0x204),async(_0x127864,_0x22d24a)=>{const _0x3e502a=_0x3aaad6;try{const _0x35972e=this['toolsRegistry'];if(!_0x35972e)return _0x22d24a['status'](0x1f4)[_0x3e502a(0x213)]({'error':_0x3e502a(0x287)});const _0x211702=_0x35972e[_0x3e502a(0x245)]();this['logger'][_0x3e502a(0x286)](_0x3e502a(0x272),{'toolCount':_0x211702['length'],'tools':_0x211702[_0x3e502a(0x2bf)](_0x38a52b=>({'id':_0x38a52b['id'],'name':_0x38a52b[_0x3e502a(0x294)],'category':_0x38a52b['category']}))}),_0x22d24a[_0x3e502a(0x213)]({'success':!![],'tools':_0x211702,'total':_0x211702['length']});}catch(_0xb49b6e){this[_0x3e502a(0x21d)]['error'](_0x3e502a(0x2d1),{'error':_0xb49b6e[_0x3e502a(0x230)],'stack':_0xb49b6e['stack']}),_0x22d24a[_0x3e502a(0x29c)](0x1f4)['json']({'error':'Failed\x20to\x20retrieve\x20tools\x20information','message':_0xb49b6e[_0x3e502a(0x230)]});}}),this[_0x3aaad6(0x1e3)][_0x3aaad6(0x20c)](_0x3aaad6(0x2a6),async(_0x14d011,_0x574528)=>{const _0x46d6e4=_0x3aaad6;try{const {sessionId:_0x4bca67,loxiaApiKey:_0x160f2c,vendorKeys:_0x4ce16c}=_0x14d011[_0x46d6e4(0x1e7)];if(!_0x4bca67)return _0x574528['status'](0x190)[_0x46d6e4(0x213)]({'success':![],'error':_0x46d6e4(0x263)});if(!this[_0x46d6e4(0x2b5)])return _0x574528['status'](0x1f4)[_0x46d6e4(0x213)]({'success':![],'error':_0x46d6e4(0x27e)});this['apiKeyManager']['setSessionKeys'](_0x4bca67,{'loxiaApiKey':_0x160f2c,'vendorKeys':_0x4ce16c||{}}),this['logger']['info'](_0x46d6e4(0x27b),{'sessionId':_0x4bca67,'hasLoxiaKey':!!_0x160f2c,'vendorKeys':Object[_0x46d6e4(0x23b)](_0x4ce16c||{})});if(this[_0x46d6e4(0x25a)]&&this[_0x46d6e4(0x25a)][_0x46d6e4(0x251)]&&_0x160f2c)try{await this['orchestrator'][_0x46d6e4(0x251)]['refresh']({'sessionId':_0x4bca67,'apiKey':_0x160f2c}),this['logger'][_0x46d6e4(0x286)](_0x46d6e4(0x279),{'sessionId':_0x4bca67});}catch(_0x3e9175){this[_0x46d6e4(0x21d)][_0x46d6e4(0x217)]('Failed\x20to\x20refresh\x20models\x20after\x20API\x20key\x20update',{'error':_0x3e9175[_0x46d6e4(0x230)],'sessionId':_0x4bca67});}_0x574528['json']({'success':!![],'message':'API\x20keys\x20updated\x20successfully','sessionId':_0x4bca67,'hasLoxiaKey':!!_0x160f2c,'vendorKeys':Object['keys'](_0x4ce16c||{})});}catch(_0x2908ab){this['logger'][_0x46d6e4(0x256)]('Failed\x20to\x20set\x20API\x20keys',{'error':_0x2908ab[_0x46d6e4(0x230)],'sessionId':_0x14d011[_0x46d6e4(0x1e7)][_0x46d6e4(0x23f)]}),_0x574528[_0x46d6e4(0x29c)](0x1f4)['json']({'success':![],'error':_0x2908ab[_0x46d6e4(0x230)]});}}),this['app'][_0x3aaad6(0x233)](_0x3aaad6(0x1e6),async(_0x562506,_0x3fd111)=>{const _0x37bea6=_0x3aaad6;try{const {sessionId:_0xe89189}=_0x562506['params'];if(!this[_0x37bea6(0x2b5)])return _0x3fd111['status'](0x1f4)['json']({'success':![],'error':_0x37bea6(0x27e)});const _0x2f9992=this['apiKeyManager']['getSessionKeys'](_0xe89189);_0x3fd111['json']({'success':!![],'sessionId':_0xe89189,'hasLoxiaKey':!!_0x2f9992['loxiaApiKey'],'vendorKeys':Object[_0x37bea6(0x23b)](_0x2f9992[_0x37bea6(0x289)]||{}),'updatedAt':_0x2f9992[_0x37bea6(0x234)]});}catch(_0x2f3fa7){this[_0x37bea6(0x21d)][_0x37bea6(0x256)]('Failed\x20to\x20get\x20API\x20key\x20status',{'error':_0x2f3fa7['message'],'sessionId':_0x562506[_0x37bea6(0x22a)][_0x37bea6(0x23f)]}),_0x3fd111['status'](0x1f4)['json']({'success':![],'error':_0x2f3fa7['message']});}}),this['app']['delete'](_0x3aaad6(0x1e6),async(_0x3ce1c3,_0x44b89f)=>{const _0x96b3ed=_0x3aaad6;try{const {sessionId:_0x32f496}=_0x3ce1c3['params'];if(!this[_0x96b3ed(0x2b5)])return _0x44b89f[_0x96b3ed(0x29c)](0x1f4)['json']({'success':![],'error':'API\x20key\x20manager\x20not\x20available'});const _0x14f964=this['apiKeyManager'][_0x96b3ed(0x2bc)](_0x32f496);_0x44b89f['json']({'success':!![],'removed':_0x14f964,'message':_0x14f964?'API\x20keys\x20removed\x20successfully':_0x96b3ed(0x2ce)});}catch(_0x4d266e){this['logger']['error'](_0x96b3ed(0x21c),{'error':_0x4d266e[_0x96b3ed(0x230)],'sessionId':_0x3ce1c3['params'][_0x96b3ed(0x23f)]}),_0x44b89f['status'](0x1f4)['json']({'success':![],'error':_0x4d266e[_0x96b3ed(0x230)]});}}),this[_0x3aaad6(0x1e3)][_0x3aaad6(0x233)]('/api/keys',async(_0x2b1a99,_0xa5d85e)=>{const _0x573430=_0x3aaad6;try{if(!this['apiKeyManager'])return _0xa5d85e['status'](0x1f4)[_0x573430(0x213)]({'success':![],'error':_0x573430(0x27e)});const _0x46b079=this[_0x573430(0x2b5)]['getActiveSessions']();_0xa5d85e['json']({'success':!![],'sessions':_0x46b079,'total':_0x46b079[_0x573430(0x22c)]});}catch(_0x41fa88){this['logger']['error'](_0x573430(0x262),{'error':_0x41fa88['message']}),_0xa5d85e[_0x573430(0x29c)](0x1f4)['json']({'success':![],'error':_0x41fa88[_0x573430(0x230)]});}}),this['app'][_0x3aaad6(0x20c)](_0x3aaad6(0x252),async(_0x48dc91,_0xc9edaa)=>{const _0x3ce85b=_0x3aaad6;try{const {agentId:_0x204d84}=_0x48dc91[_0x3ce85b(0x22a)],{filePath:_0x4ef23c,mode:_0x2844ff,fileName:_0xa257cc}=_0x48dc91['body'];if(!this['orchestrator']?.['fileAttachmentService'])return _0xc9edaa[_0x3ce85b(0x29c)](0x1f4)['json']({'success':![],'error':'File\x20attachment\x20service\x20not\x20available'});const _0x384da6=await this[_0x3ce85b(0x25a)]['fileAttachmentService'][_0x3ce85b(0x20d)]({'agentId':_0x204d84,'filePath':_0x4ef23c,'mode':_0x2844ff||'content','fileName':_0xa257cc});this[_0x3ce85b(0x21d)]['info']('File\x20attachment\x20uploaded',{'agentId':_0x204d84,'fileId':_0x384da6['fileId'],'fileName':_0x384da6[_0x3ce85b(0x2ab)]}),_0xc9edaa['json']({'success':!![],'attachment':_0x384da6});}catch(_0xde331b){this[_0x3ce85b(0x21d)][_0x3ce85b(0x256)](_0x3ce85b(0x26b),{'agentId':_0x48dc91['params'][_0x3ce85b(0x1e2)],'error':_0xde331b['message']}),_0xc9edaa['status'](0x1f4)[_0x3ce85b(0x213)]({'success':![],'error':_0xde331b[_0x3ce85b(0x230)]});}}),this['app']['get']('/api/agents/:agentId/attachments',async(_0x3a1df0,_0x4c97f0)=>{const _0x44538d=_0x3aaad6;try{const {agentId:_0x464c50}=_0x3a1df0[_0x44538d(0x22a)],{mode:_0x5d22e3,active:_0x353eb8}=_0x3a1df0['query'];if(!this[_0x44538d(0x25a)]?.[_0x44538d(0x211)])return _0x4c97f0[_0x44538d(0x29c)](0x1f4)[_0x44538d(0x213)]({'success':![],'error':'File\x20attachment\x20service\x20not\x20available'});const _0x1d32d6={};if(_0x5d22e3)_0x1d32d6['mode']=_0x5d22e3;if(_0x353eb8!==undefined)_0x1d32d6[_0x44538d(0x283)]=_0x353eb8==='true';const _0x294afd=await this[_0x44538d(0x25a)]['fileAttachmentService'][_0x44538d(0x246)](_0x464c50,_0x1d32d6);_0x4c97f0['json']({'success':!![],'attachments':_0x294afd,'total':_0x294afd[_0x44538d(0x22c)]});}catch(_0x8e7b42){this[_0x44538d(0x21d)][_0x44538d(0x256)]('Failed\x20to\x20get\x20attachments',{'agentId':_0x3a1df0[_0x44538d(0x22a)]['agentId'],'error':_0x8e7b42[_0x44538d(0x230)]}),_0x4c97f0[_0x44538d(0x29c)](0x1f4)[_0x44538d(0x213)]({'success':![],'error':_0x8e7b42[_0x44538d(0x230)]});}}),this['app'][_0x3aaad6(0x233)]('/api/attachments/:fileId',async(_0x12a74c,_0x2eb8de)=>{const _0xcaf01=_0x3aaad6;try{const {fileId:_0x46002e}=_0x12a74c[_0xcaf01(0x22a)];if(!this['orchestrator']?.['fileAttachmentService'])return _0x2eb8de['status'](0x1f4)[_0xcaf01(0x213)]({'success':![],'error':'File\x20attachment\x20service\x20not\x20available'});const _0x18b0ba=await this['orchestrator'][_0xcaf01(0x211)][_0xcaf01(0x1e5)](_0x46002e);if(!_0x18b0ba)return _0x2eb8de['status'](0x194)['json']({'success':![],'error':_0xcaf01(0x2d7)});_0x2eb8de['json']({'success':!![],'attachment':_0x18b0ba});}catch(_0x2dbf1f){this[_0xcaf01(0x21d)][_0xcaf01(0x256)]('Failed\x20to\x20get\x20attachment',{'fileId':_0x12a74c[_0xcaf01(0x22a)]['fileId'],'error':_0x2dbf1f[_0xcaf01(0x230)]}),_0x2eb8de[_0xcaf01(0x29c)](0x1f4)['json']({'success':![],'error':_0x2dbf1f[_0xcaf01(0x230)]});}}),this[_0x3aaad6(0x1e3)][_0x3aaad6(0x22b)]('/api/attachments/:fileId/toggle',async(_0x22e39b,_0x176bc1)=>{const _0x43ed02=_0x3aaad6;try{const {fileId:_0x36c0ed}=_0x22e39b['params'];if(!this['orchestrator']?.['fileAttachmentService'])return _0x176bc1[_0x43ed02(0x29c)](0x1f4)['json']({'success':![],'error':'File\x20attachment\x20service\x20not\x20available'});const _0x21440c=await this['orchestrator'][_0x43ed02(0x211)]['toggleActive'](_0x36c0ed);this['logger']['info']('Attachment\x20active\x20status\x20toggled',{'fileId':_0x36c0ed,'active':_0x21440c['active']}),_0x176bc1[_0x43ed02(0x213)]({'success':!![],'attachment':_0x21440c});}catch(_0x3cc7b4){this['logger'][_0x43ed02(0x256)]('Failed\x20to\x20toggle\x20attachment',{'fileId':_0x22e39b[_0x43ed02(0x22a)][_0x43ed02(0x266)],'error':_0x3cc7b4['message']}),_0x176bc1['status'](0x1f4)[_0x43ed02(0x213)]({'success':![],'error':_0x3cc7b4['message']});}}),this[_0x3aaad6(0x1e3)]['patch'](_0x3aaad6(0x2ad),async(_0x2c50d0,_0x28eeb2)=>{const _0x3f37f1=_0x3aaad6;try{const {fileId:_0x3caff0}=_0x2c50d0[_0x3f37f1(0x22a)],{mode:_0x1a1643,active:_0xdc1a90}=_0x2c50d0['body'];if(!this['orchestrator']?.['fileAttachmentService'])return _0x28eeb2[_0x3f37f1(0x29c)](0x1f4)[_0x3f37f1(0x213)]({'success':![],'error':'File\x20attachment\x20service\x20not\x20available'});const _0x53484a={};if(_0x1a1643!==undefined)_0x53484a['mode']=_0x1a1643;if(_0xdc1a90!==undefined)_0x53484a['active']=_0xdc1a90;const _0x5e8e30=await this['orchestrator'][_0x3f37f1(0x211)]['updateAttachment'](_0x3caff0,_0x53484a);this[_0x3f37f1(0x21d)]['info'](_0x3f37f1(0x258),{'fileId':_0x3caff0,'updates':_0x53484a}),_0x28eeb2['json']({'success':!![],'attachment':_0x5e8e30});}catch(_0x53c8ca){this[_0x3f37f1(0x21d)]['error'](_0x3f37f1(0x253),{'fileId':_0x2c50d0[_0x3f37f1(0x22a)][_0x3f37f1(0x266)],'error':_0x53c8ca['message']}),_0x28eeb2[_0x3f37f1(0x29c)](0x1f4)['json']({'success':![],'error':_0x53c8ca['message']});}}),this['app']['delete']('/api/attachments/:fileId',async(_0x55ca11,_0x4dcd79)=>{const _0x58206a=_0x3aaad6;try{const {fileId:_0xe36c05}=_0x55ca11[_0x58206a(0x22a)],{agentId:_0x187ccf}=_0x55ca11['query'];if(!_0x187ccf)return _0x4dcd79['status'](0x190)['json']({'success':![],'error':'agentId\x20query\x20parameter\x20is\x20required'});if(!this['orchestrator']?.['fileAttachmentService'])return _0x4dcd79['status'](0x1f4)['json']({'success':![],'error':'File\x20attachment\x20service\x20not\x20available'});const _0x5276c0=await this['orchestrator']['fileAttachmentService']['deleteAttachment'](_0xe36c05,_0x187ccf);this['logger']['info']('Attachment\x20deleted',{'fileId':_0xe36c05,'agentId':_0x187ccf,'physicallyDeleted':_0x5276c0[_0x58206a(0x29a)]}),_0x4dcd79[_0x58206a(0x213)]({'success':!![],'message':_0x5276c0['physicallyDeleted']?_0x58206a(0x274):_0x58206a(0x285),'physicallyDeleted':_0x5276c0[_0x58206a(0x29a)]});}catch(_0x277c61){this[_0x58206a(0x21d)]['error'](_0x58206a(0x232),{'fileId':_0x55ca11['params'][_0x58206a(0x266)],'error':_0x277c61['message']}),_0x4dcd79['status'](0x1f4)[_0x58206a(0x213)]({'success':![],'error':_0x277c61[_0x58206a(0x230)]});}}),this['app'][_0x3aaad6(0x20c)]('/api/attachments/:fileId/import',async(_0x19ecf5,_0x52fb8c)=>{const _0x4707ab=_0x3aaad6;try{const {fileId:_0x5556c9}=_0x19ecf5[_0x4707ab(0x22a)],{targetAgentId:_0x3cfb2c}=_0x19ecf5[_0x4707ab(0x1e7)];if(!_0x3cfb2c)return _0x52fb8c[_0x4707ab(0x29c)](0x190)['json']({'success':![],'error':_0x4707ab(0x219)});if(!this['orchestrator']?.['fileAttachmentService'])return _0x52fb8c['status'](0x1f4)[_0x4707ab(0x213)]({'success':![],'error':'File\x20attachment\x20service\x20not\x20available'});const _0x723a6b=await this['orchestrator'][_0x4707ab(0x211)][_0x4707ab(0x1fe)](_0x5556c9,_0x3cfb2c);this['logger']['info']('Attachment imported',{'fileId':_0x5556c9,'targetAgentId':_0x3cfb2c}),_0x52fb8c[_0x4707ab(0x213)]({'success':!![],'attachment':_0x723a6b});}catch(_0x4328ff){this['logger']['error']('Failed to import attachment',{'fileId':_0x19ecf5['params'][_0x4707ab(0x266)],'error':_0x4328ff['message']}),_0x52fb8c[_0x4707ab(0x29c)](0x1f4)['json']({'success':![],'error':_0x4328ff['message']});}}),this[_0x3aaad6(0x1e3)][_0x3aaad6(0x233)](_0x3aaad6(0x205),async(_0x96ee33,_0x43a55d)=>{const _0x1fa479=_0x3aaad6;try{const {fileId:_0x24decd}=_0x96ee33['params'];if(!this[_0x1fa479(0x25a)]?.['fileAttachmentService'])return _0x43a55d['status'](0x1f4)[_0x1fa479(0x213)]({'success':![],'error':_0x1fa479(0x206)});const _0x1d47df=await this['orchestrator'][_0x1fa479(0x211)]['getAttachmentPreview'](_0x24decd);if(!_0x1d47df)return _0x43a55d['status'](0x194)['json']({'success':![],'error':_0x1fa479(0x2d7)});_0x43a55d['json']({'success':!![],'preview':_0x1d47df});}catch(_0xb7308d){this[_0x1fa479(0x21d)]['error']('Failed\x20to\x20get\x20attachment\x20preview',{'fileId':_0x96ee33[_0x1fa479(0x22a)][_0x1fa479(0x266)],'error':_0xb7308d[_0x1fa479(0x230)]}),_0x43a55d['status'](0x1f4)['json']({'success':![],'error':_0xb7308d['message']});}}),this['app']['get'](_0x3aaad6(0x2a8),async(_0x53cfa1,_0x3cdb60)=>{const _0x5aa9be=_0x3aaad6;try{const {sessionId:_0x1df341,filename:_0x1832c2}=_0x53cfa1['params'],_0x24b63f=this['sessions']['get'](_0x1df341);if(!_0x24b63f)return _0x3cdb60[_0x5aa9be(0x29c)](HTTP_STATUS['FORBIDDEN'])[_0x5aa9be(0x213)]({'success':![],'error':'Invalid\x20session'});const _0x3bf986=a0_0x17b783[_0x5aa9be(0x210)](_0x1832c2);if(_0x3bf986!==_0x1832c2||_0x1832c2['includes']('..')||_0x1832c2['includes']('/')||_0x1832c2['includes']('\x5c'))return _0x3cdb60[_0x5aa9be(0x29c)](HTTP_STATUS['BAD_REQUEST'])[_0x5aa9be(0x213)]({'success':![],'error':_0x5aa9be(0x2c5)});let _0x4a6e4d=null;const _0x51a7f0=[a0_0x17b783['join'](_0x24b63f['projectDir']||process['cwd'](),'images',_0x3bf986),a0_0x17b783['join'](_0x5aa9be(0x247),_0x1df341,_0x3bf986),a0_0x17b783['join']('/tmp/loxia-images',_0x3bf986)];if(this['orchestrator']?.[_0x5aa9be(0x260)])try{const _0x2f167a=await this['orchestrator'][_0x5aa9be(0x260)][_0x5aa9be(0x218)]();for(const _0xba20a1 of _0x2f167a){_0xba20a1['directoryAccess']?.[_0x5aa9be(0x288)]&&_0x51a7f0[_0x5aa9be(0x26e)](a0_0x17b783['join'](_0xba20a1[_0x5aa9be(0x2b4)]['workingDirectory'],_0x5aa9be(0x2b3),_0x3bf986));}}catch(_0xe8c87d){this[_0x5aa9be(0x21d)][_0x5aa9be(0x217)]('Failed\x20to\x20get\x20agent\x20working\x20directories\x20for\x20image\x20search',{'error':_0xe8c87d['message']});}for(const _0x9e1752 of _0x51a7f0){try{const _0x2bfb7a=await a0_0x321fe9[_0x5aa9be(0x214)](_0x9e1752);if(_0x2bfb7a[_0x5aa9be(0x224)]()){_0x4a6e4d=_0x9e1752,this[_0x5aa9be(0x21d)]['info']('Image\x20found',{'filename':_0x3bf986,'path':_0x4a6e4d,'sessionId':_0x1df341});break;}}catch(_0x24b947){continue;}}if(!_0x4a6e4d)return this['logger'][_0x5aa9be(0x217)]('Image\x20not\x20found\x20in\x20any\x20search\x20path',{'filename':_0x3bf986,'sessionId':_0x1df341,'searchPaths':_0x51a7f0}),_0x3cdb60[_0x5aa9be(0x29c)](HTTP_STATUS['NOT_FOUND'])['json']({'success':![],'error':'Image\x20not\x20found'});_0x3cdb60[_0x5aa9be(0x284)](_0x4a6e4d,_0x193894=>{const _0x2fae3f=_0x5aa9be;_0x193894?(this['logger'][_0x2fae3f(0x256)]('Failed\x20to\x20send\x20image\x20file',{'error':_0x193894['message'],'imagePath':_0x4a6e4d,'filename':_0x3bf986}),!_0x3cdb60[_0x2fae3f(0x2d4)]&&_0x3cdb60['status'](HTTP_STATUS[_0x2fae3f(0x27a)])[_0x2fae3f(0x213)]({'success':![],'error':'Failed\x20to\x20serve\x20image'})):this['logger'][_0x2fae3f(0x286)]('Image\x20served\x20successfully',{'filename':_0x3bf986,'path':_0x4a6e4d,'sessionId':_0x1df341});});}catch(_0xb97b01){this[_0x5aa9be(0x21d)][_0x5aa9be(0x256)]('Image\x20serving\x20error',{'error':_0xb97b01['message'],'sessionId':_0x53cfa1[_0x5aa9be(0x22a)][_0x5aa9be(0x23f)],'filename':_0x53cfa1[_0x5aa9be(0x22a)][_0x5aa9be(0x1f7)]}),_0x3cdb60['status'](HTTP_STATUS[_0x5aa9be(0x27a)])[_0x5aa9be(0x213)]({'success':![],'error':_0xb97b01['message']});}}),this[_0x3aaad6(0x1e3)][_0x3aaad6(0x20c)]('/api/agents/:agentId/mode',async(_0x233661,_0x4cd22d)=>{const _0xc31d20=_0x3aaad6;try{const {agentId:_0x5360e7}=_0x233661[_0xc31d20(0x22a)],{mode:_0x219aa7,lockMode:lockMode=![],sessionId:_0x688f4b}=_0x233661[_0xc31d20(0x1e7)];if(!Object[_0xc31d20(0x277)](AGENT_MODES)['includes'](_0x219aa7))return _0x4cd22d[_0xc31d20(0x29c)](0x190)[_0xc31d20(0x213)]({'success':![],'error':'Invalid\x20mode.\x20Must\x20be\x20one\x20of:\x20'+Object['values'](AGENT_MODES)['join'](',\x20')});const _0x387e8c=_0x688f4b||_0x233661['sessionID'];!_0x387e8c&&this['logger']['warn']('Agent\x20mode\x20update\x20requested\x20without\x20session\x20ID',{'agentId':_0x5360e7,'hasBodySessionId':!!_0x688f4b,'hasReqSessionId':!!_0x233661[_0xc31d20(0x248)]});const _0xeb460c={'interface':INTERFACE_TYPES['WEB'],'sessionId':_0x387e8c,'action':_0xc31d20(0x28a),'payload':{'agentId':_0x5360e7,'updates':{'mode':_0x219aa7}}},_0x50d15d=await this[_0xc31d20(0x25a)]['processRequest'](_0xeb460c);if(_0x50d15d['success']){const _0x128818=_0x50d15d['data'];this[_0xc31d20(0x21d)][_0xc31d20(0x286)]('Agent\x20mode\x20updated:\x20'+_0x5360e7,{'newMode':_0x219aa7,'lockMode':lockMode,'finalMode':_0x128818?.['mode']}),this['broadcastToSession'](_0xeb460c['sessionId'],{'type':_0xc31d20(0x1f5),'agentId':_0x5360e7,'mode':_0x128818?.[_0xc31d20(0x215)],'modeState':_0x128818?.['modeState']}),_0x4cd22d[_0xc31d20(0x213)]({'success':!![],'agent':_0x128818,'message':'Agent\x20mode\x20switched\x20to\x20'+_0x128818?.[_0xc31d20(0x215)]});}else _0x4cd22d['status'](0x190)[_0xc31d20(0x213)]({'success':![],'error':_0x50d15d['error']||'Failed\x20to\x20update\x20agent\x20mode'});}catch(_0x517197){this['logger'][_0xc31d20(0x256)]('Failed\x20to\x20update\x20agent\x20mode',{'agentId':_0x233661[_0xc31d20(0x22a)]['agentId'],'error':_0x517197[_0xc31d20(0x230)]}),_0x4cd22d[_0xc31d20(0x29c)](0x1f4)[_0xc31d20(0x213)]({'success':![],'error':_0x517197[_0xc31d20(0x230)]});}}),this['app']['post'](_0x3aaad6(0x240),async(_0x2d5fca,_0x3dc623)=>{const _0x1f9be7=_0x3aaad6;try{const {agentId:_0x195991}=_0x2d5fca[_0x1f9be7(0x22a)],_0x360c58=this['orchestrator']['messageProcessor'];if(!_0x360c58)return _0x3dc623[_0x1f9be7(0x29c)](0x1f4)['json']({'success':![],'error':'Message\x20processor\x20not\x20available'});const _0x835097=await _0x360c58[_0x1f9be7(0x1e0)](_0x195991);this[_0x1f9be7(0x21d)][_0x1f9be7(0x286)]('Autonomous\x20execution\x20stop\x20requested:\x20'+_0x195991);const _0x96ec6e=_0x2d5fca['sessionID']||_0x835097[_0x1f9be7(0x261)]?.[_0x1f9be7(0x23f)];_0x96ec6e&&this['broadcastToSession'](_0x96ec6e,{'type':'agent_mode_changed','agentId':_0x195991,'mode':_0x835097[_0x1f9be7(0x261)]?.['mode'],'modeState':_0x835097['agent']?.['modeState']}),_0x3dc623[_0x1f9be7(0x213)](_0x835097);}catch(_0x558408){this[_0x1f9be7(0x21d)]['error'](_0x1f9be7(0x23a),{'agentId':_0x2d5fca['params'][_0x1f9be7(0x1e2)],'error':_0x558408['message']}),_0x3dc623['status'](0x1f4)['json']({'success':![],'error':_0x558408['message']});}}),this['app'][_0x3aaad6(0x233)]('/api/agents/:agentId/mode',async(_0x34e748,_0x2c195d)=>{const _0x3eacb5=_0x3aaad6;try{const {agentId:_0x5c0b27}=_0x34e748['params'],_0x56a04d={'interface':INTERFACE_TYPES['WEB'],'sessionId':_0x34e748[_0x3eacb5(0x248)],'action':'get_agent_status','payload':{'agentId':_0x5c0b27}},_0x1ce8b3=await this['orchestrator']['processRequest'](_0x56a04d);_0x1ce8b3[_0x3eacb5(0x1dc)]&&_0x1ce8b3['agent']?_0x2c195d['json']({'success':!![],'mode':_0x1ce8b3[_0x3eacb5(0x261)][_0x3eacb5(0x215)],'modeState':_0x1ce8b3['agent']['modeState'],'currentTask':_0x1ce8b3[_0x3eacb5(0x261)]['currentTask'],'iterationCount':_0x1ce8b3['agent'][_0x3eacb5(0x1e8)],'taskStartTime':_0x1ce8b3['agent']['taskStartTime'],'stopRequested':_0x1ce8b3[_0x3eacb5(0x261)]['stopRequested']}):_0x2c195d['status'](0x194)['json']({'success':![],'error':_0x3eacb5(0x21b)});}catch(_0x5710f1){this[_0x3eacb5(0x21d)]['error']('Failed\x20to\x20get\x20agent\x20mode\x20status',{'agentId':_0x34e748[_0x3eacb5(0x22a)][_0x3eacb5(0x1e2)],'error':_0x5710f1['message']}),_0x2c195d['status'](0x1f4)[_0x3eacb5(0x213)]({'success':![],'error':_0x5710f1[_0x3eacb5(0x230)]});}}),this[_0x3aaad6(0x1e3)]['get'](_0x3aaad6(0x1f6),async(_0x1ed60b,_0x32e4a4)=>{const _0x523be9=_0x3aaad6;try{const _0x13a266=this['orchestrator']['config'][_0x523be9(0x1f0)]?.[_0x523be9(0x2a0)]||process['cwd'](),_0x47725e=await this[_0x523be9(0x25a)][_0x523be9(0x24b)]['getAllAvailableAgents'](_0x13a266,this[_0x523be9(0x25a)]['agentPool']);_0x32e4a4['json']({'success':!![],'agents':_0x47725e,'total':_0x47725e[_0x523be9(0x22c)],'active':_0x47725e['filter'](_0x11cf11=>_0x11cf11['isLoaded'])[_0x523be9(0x22c)],'archived':_0x47725e['filter'](_0x23aebb=>!_0x23aebb[_0x523be9(0x268)])[_0x523be9(0x22c)]});}catch(_0x53d074){this[_0x523be9(0x21d)][_0x523be9(0x256)]('Failed\x20to\x20get\x20available\x20agents',{'error':_0x53d074[_0x523be9(0x230)],'stack':_0x53d074['stack']}),_0x32e4a4[_0x523be9(0x29c)](0x1f4)[_0x523be9(0x213)]({'success':![],'error':_0x53d074['message']});}}),this['app']['get']('/api/agents/:agentId/metadata',async(_0x2d65a3,_0x55c0f2)=>{const _0x5e4967=_0x3aaad6;try{const {agentId:_0x25505b}=_0x2d65a3[_0x5e4967(0x22a)],_0x1e462c=this['orchestrator']['config']['project']?.['directory']||process['cwd'](),_0x4ddf64=await this['orchestrator'][_0x5e4967(0x24b)][_0x5e4967(0x282)](_0x25505b,_0x1e462c);_0x55c0f2['json']({'success':!![],'metadata':_0x4ddf64});}catch(_0x590cc8){this['logger'][_0x5e4967(0x256)]('Failed\x20to\x20get\x20agent\x20metadata',{'agentId':_0x2d65a3[_0x5e4967(0x22a)][_0x5e4967(0x1e2)],'error':_0x590cc8['message']}),_0x55c0f2['status'](0x194)[_0x5e4967(0x213)]({'success':![],'error':_0x590cc8[_0x5e4967(0x230)]});}}),this[_0x3aaad6(0x1e3)]['post']('/api/agents/import',async(_0x23eb0a,_0x27bbf2)=>{const _0x860163=_0x3aaad6;try{const {agentId:_0x4bd1bb}=_0x23eb0a['body'];if(!_0x4bd1bb)return _0x27bbf2['status'](0x190)['json']({'success':![],'error':'agentId\x20is\x20required\x20in\x20request\x20body'});const _0x159f38=this['orchestrator']['config']['project']?.[_0x860163(0x2a0)]||process[_0x860163(0x1df)](),_0x2e93bd=await this['orchestrator']['stateManager'][_0x860163(0x2c1)](_0x4bd1bb,_0x159f38,this['orchestrator']['agentPool']);this[_0x860163(0x2a7)]&&this['wsManager']['broadcast']({'type':'agent-imported','agent':{'id':_0x2e93bd['id'],'name':_0x2e93bd[_0x860163(0x294)],'status':_0x2e93bd[_0x860163(0x29c)],'capabilities':_0x2e93bd['capabilities'],'model':_0x2e93bd['currentModel']||_0x2e93bd['preferredModel']}}),this['logger']['info']('Agent imported successfully',{'agentId':_0x2e93bd['id'],'name':_0x2e93bd[_0x860163(0x294)]}),_0x27bbf2[_0x860163(0x213)]({'success':!![],'agent':{'id':_0x2e93bd['id'],'name':_0x2e93bd[_0x860163(0x294)],'status':_0x2e93bd['status'],'model':_0x2e93bd[_0x860163(0x241)]||_0x2e93bd['preferredModel'],'capabilities':_0x2e93bd[_0x860163(0x228)],'lastActivity':_0x2e93bd[_0x860163(0x23e)]},'message':_0x860163(0x227)+_0x2e93bd[_0x860163(0x294)]+_0x860163(0x2db)});}catch(_0x58332c){this[_0x860163(0x21d)]['error']('Failed to import agent',{'agentId':_0x23eb0a[_0x860163(0x1e7)][_0x860163(0x1e2)],'error':_0x58332c['message'],'stack':_0x58332c['stack']});const _0x3f1313=_0x58332c['message']['includes'](_0x860163(0x20b))?0x199:_0x58332c[_0x860163(0x230)][_0x860163(0x1db)]('not\x20found')?0x194:_0x58332c[_0x860163(0x230)]['includes']('Invalid')?0x190:0x1f4;_0x27bbf2[_0x860163(0x29c)](_0x3f1313)['json']({'success':![],'error':_0x58332c[_0x860163(0x230)]});}}),this[_0x3aaad6(0x1e3)][_0x3aaad6(0x233)]('*',(_0x38170d,_0x5df04f)=>{const _0x5f0b9e=_0x3aaad6,_0x445925=a0_0x17b783['join'](__dirname,'../../web-ui/build/index.html');_0x5df04f[_0x5f0b9e(0x284)](_0x445925,_0x3632d4=>{const _0xdf42b2=_0x5f0b9e;_0x3632d4&&_0x5df04f[_0xdf42b2(0x29c)](HTTP_STATUS[_0xdf42b2(0x2c7)])[_0xdf42b2(0x2b0)](_0xdf42b2(0x24a));});});}['setupWebSocket'](){const _0x178791=a0_0x433fed;this['logger']['info'](_0x178791(0x235),{'port':this['port'],'host':this['host'],'wsServerExists':!!this[_0x178791(0x28b)],'httpServerExists':!!this[_0x178791(0x22e)]}),this['wss']['on']('error',_0x6bb59=>{const _0x5ebd7c=_0x178791;this['logger']['error'](_0x5ebd7c(0x1f3),{'error':_0x6bb59['message'],'stack':_0x6bb59['stack'],'port':this['port']});}),this['wss']['on']('listening',()=>{const _0x36c29a=_0x178791;this[_0x36c29a(0x21d)][_0x36c29a(0x286)](_0x36c29a(0x1fd),{'port':this[_0x36c29a(0x249)],'host':this['host']});}),this[_0x178791(0x28b)]['on'](_0x178791(0x1f8),(_0x4bee9d,_0x380f38)=>{const _0x4e16a6=_0x178791,_0x1e8087='conn-'+Date['now']()+'-'+Math[_0x4e16a6(0x1ee)]()['toString'](0x24)[_0x4e16a6(0x22d)](0x2,0x9);this['logger'][_0x4e16a6(0x286)]('WebSocket\x20connection\x20established',{'connectionId':_0x1e8087,'ip':_0x380f38[_0x4e16a6(0x244)][_0x4e16a6(0x1de)],'origin':_0x380f38['headers'][_0x4e16a6(0x278)],'host':_0x380f38['headers']['host'],'userAgent':_0x380f38['headers']['user-agent'],'url':_0x380f38['url']});const _0xd2c71f={'id':_0x1e8087,'ws':_0x4bee9d,'sessionId':null,'connectedAt':new Date()['toISOString'](),'lastActivity':new Date()['toISOString']()};this[_0x4e16a6(0x1eb)][_0x4e16a6(0x25d)](_0x1e8087,_0xd2c71f),_0x4bee9d['on'](_0x4e16a6(0x230),async _0xb697fa=>{const _0x24668a=_0x4e16a6;try{const _0x13f950=JSON['parse'](_0xb697fa[_0x24668a(0x299)]());await this['handleWebSocketMessage'](_0x1e8087,_0x13f950);}catch(_0x87655){this['logger'][_0x24668a(0x256)]('WebSocket\x20message\x20error',{'connectionId':_0x1e8087,'error':_0x87655['message']}),_0x4bee9d['send'](JSON['stringify']({'type':_0x24668a(0x256),'error':_0x87655['message']}));}}),_0x4bee9d['on'](_0x4e16a6(0x216),()=>{const _0x487519=_0x4e16a6;this[_0x487519(0x21d)][_0x487519(0x286)](_0x487519(0x2d2),{'connectionId':_0x1e8087}),this[_0x487519(0x1eb)][_0x487519(0x265)](_0x1e8087);}),_0x4bee9d[_0x4e16a6(0x2b0)](JSON['stringify']({'type':_0x4e16a6(0x1e1),'connectionId':_0x1e8087,'timestamp':new Date()[_0x4e16a6(0x27c)]()}));});}async['handleWebSocketMessage'](_0x1d9132,_0x78103){const _0x389bc2=a0_0x433fed,_0xa1ae94=this['connections']['get'](_0x1d9132);if(!_0xa1ae94)return;_0xa1ae94[_0x389bc2(0x23e)]=new Date()[_0x389bc2(0x27c)]();switch(_0x78103['type']){case _0x389bc2(0x231):const _0xea98da=_0x78103['sessionId'];_0xa1ae94[_0x389bc2(0x23f)]=_0xea98da,this[_0x389bc2(0x21d)]['info'](_0x389bc2(0x203),{'connectionId':_0x1d9132,'sessionId':_0xea98da,'totalConnectionsForSession':Array['from'](this[_0x389bc2(0x1eb)][_0x389bc2(0x277)]())[_0x389bc2(0x22f)](_0x2fc1b7=>_0x2fc1b7[_0x389bc2(0x23f)]===_0xea98da)[_0x389bc2(0x22c)]}),_0xa1ae94['ws'][_0x389bc2(0x2b0)](JSON['stringify']({'type':'session_joined','sessionId':_0xea98da}));break;case'ping':_0xa1ae94['ws']['send'](JSON[_0x389bc2(0x2c6)]({'type':_0x389bc2(0x298),'timestamp':new Date()[_0x389bc2(0x27c)]()}));break;case'orchestrator_request':try{const _0x48e646={'interface':INTERFACE_TYPES[_0x389bc2(0x275)],'sessionId':_0xa1ae94['sessionId'],'action':_0x78103['action'],'payload':_0x78103[_0x389bc2(0x2c2)],'projectDir':_0x78103[_0x389bc2(0x20a)]||process[_0x389bc2(0x1df)]()},_0x4b808d=await this[_0x389bc2(0x25a)]['processRequest'](_0x48e646);_0xa1ae94['ws'][_0x389bc2(0x2b0)](JSON[_0x389bc2(0x2c6)]({'type':'orchestrator_response','requestId':_0x78103['requestId'],'response':_0x4b808d}));}catch(_0x1cf640){_0xa1ae94['ws']['send'](JSON[_0x389bc2(0x2c6)]({'type':'error','requestId':_0x78103[_0x389bc2(0x250)],'error':_0x1cf640['message']}));}break;default:this['logger'][_0x389bc2(0x217)](_0x389bc2(0x24c),{'connectionId':_0x1d9132,'type':_0x78103['type']});}}[a0_0x433fed(0x2d3)](_0x29f12f,_0x189eeb){const _0x34ac6f=a0_0x433fed,_0x4bfb4e=Array[_0x34ac6f(0x20e)](this[_0x34ac6f(0x1eb)][_0x34ac6f(0x277)]())[_0x34ac6f(0x22f)](_0x503484=>_0x503484['sessionId']===_0x29f12f);let _0x1197eb=[];_0x4bfb4e['length']===0x0&&(_0x1197eb=Array[_0x34ac6f(0x20e)](this['connections'][_0x34ac6f(0x277)]()),this[_0x34ac6f(0x21d)]?.[_0x34ac6f(0x217)]('🔄\x20No\x20connections\x20for\x20session,\x20trying\x20all\x20connections:',{'targetSessionId':_0x29f12f,'totalConnections':this['connections']['size'],'allSessionIds':Array[_0x34ac6f(0x20e)](this['connections']['values']())['map'](_0x31a405=>_0x31a405['sessionId'])[_0x34ac6f(0x22f)](Boolean)}));const _0x41fb10=_0x4bfb4e['length']>0x0?_0x4bfb4e:_0x1197eb;this['logger']?.['info']('📡\x20WebSocket\x20broadcastToSession\x20called:',{'sessionId':_0x29f12f,'messageType':_0x189eeb[_0x34ac6f(0x1ff)],'agentId':_0x189eeb[_0x34ac6f(0x1e2)],'totalConnections':this['connections'][_0x34ac6f(0x2d5)],'sessionConnections':_0x4bfb4e[_0x34ac6f(0x22c)],'targetConnections':_0x41fb10['length'],'connectionIds':_0x41fb10['map'](_0xec3b1a=>_0xec3b1a['id']),'usingFallback':_0x4bfb4e[_0x34ac6f(0x22c)]===0x0,'messagePreview':_0x189eeb['type']==='autonomous_update'&&_0x189eeb['message']?{'messageId':_0x189eeb['message']['id'],'messageRole':_0x189eeb[_0x34ac6f(0x230)][_0x34ac6f(0x207)],'contentLength':_0x189eeb['message'][_0x34ac6f(0x201)]?.['length'],'hasToolResults':!!_0x189eeb[_0x34ac6f(0x230)]['toolResults']}:undefined});for(const _0x26f899 of _0x41fb10){try{const _0x4cc58d={..._0x189eeb,'timestamp':new Date()['toISOString']()};_0x26f899['ws']['send'](JSON['stringify'](_0x4cc58d)),this[_0x34ac6f(0x21d)]?.['info']('✅\x20WebSocket\x20message\x20sent\x20to\x20connection:',{'connectionId':_0x26f899['id'],'messageType':_0x189eeb[_0x34ac6f(0x1ff)],'agentId':_0x189eeb['agentId']});}catch(_0x46100f){this['logger'][_0x34ac6f(0x217)](_0x34ac6f(0x2cb),{'connectionId':_0x26f899['id'],'sessionId':_0x29f12f,'messageType':_0x189eeb[_0x34ac6f(0x1ff)],'error':_0x46100f[_0x34ac6f(0x230)]});}}}async['startServer'](){return new Promise((_0x2a20c0,_0x2891f3)=>{const _0x250cae=a0_0x5128;this[_0x250cae(0x22e)]['listen'](this[_0x250cae(0x249)],this[_0x250cae(0x270)],_0x2eb6b1=>{const _0xf3a191=_0x250cae;_0x2eb6b1?(this[_0xf3a191(0x21d)][_0xf3a191(0x256)]('Failed\x20to\x20start\x20HTTP\x20server',{'error':_0x2eb6b1['message'],'port':this['port'],'host':this['host']}),_0x2891f3(_0x2eb6b1)):(this['isRunning']=!![],this['logger'][_0xf3a191(0x286)](_0xf3a191(0x2d9),{'port':this['port'],'host':this[_0xf3a191(0x270)],'httpUrl':_0xf3a191(0x25b)+this['host']+':'+this[_0xf3a191(0x249)],'wsUrl':'ws://'+this['host']+':'+this['port'],'wsServerAttached':!!this['wss'],'wsConnections':this[_0xf3a191(0x1eb)][_0xf3a191(0x2d5)]}),setTimeout(async()=>{const _0xb68eac=_0xf3a191;await this[_0xb68eac(0x2aa)]();},0x3e8),_0x2a20c0());}),this['server']['on']('error',_0x4960bf=>{const _0x4fc479=_0x250cae;this[_0x4fc479(0x21d)]['error']('HTTP\x20server\x20error:',{'error':_0x4960bf['message'],'stack':_0x4960bf['stack'],'port':this[_0x4fc479(0x249)]});});});}async[a0_0x433fed(0x2aa)](){const _0x29860b=a0_0x433fed;try{const {default:_0x365aff}=await import('ws'),_0x47f556=new _0x365aff(_0x29860b(0x2ba)+this[_0x29860b(0x249)]);_0x47f556['on'](_0x29860b(0x1fa),()=>{const _0x1030f8=_0x29860b;this['logger']['info'](_0x1030f8(0x2a2),{'port':this['port'],'url':'ws://localhost:'+this[_0x1030f8(0x249)]}),_0x47f556['close']();}),_0x47f556['on'](_0x29860b(0x256),_0x1fee08=>{const _0x32d958=_0x29860b;this['logger'][_0x32d958(0x256)]('❌\x20WebSocket\x20server\x20test:\x20FAILED',{'port':this['port'],'url':_0x32d958(0x2ba)+this[_0x32d958(0x249)],'error':_0x1fee08[_0x32d958(0x230)],'code':_0x1fee08['code']});}),_0x47f556['on']('close',(_0xba4885,_0x557138)=>{const _0x15f5e4=_0x29860b;_0xba4885===0x3e8&&this['logger'][_0x15f5e4(0x286)]('WebSocket\x20test\x20connection\x20closed\x20cleanly');}),setTimeout(()=>{const _0x599de9=_0x29860b;_0x47f556['readyState']===_0x365aff[_0x599de9(0x2ac)]&&(this['logger'][_0x599de9(0x256)](_0x599de9(0x202),{'port':this['port'],'url':_0x599de9(0x2ba)+this[_0x599de9(0x249)],'readyState':_0x47f556[_0x599de9(0x1f1)]}),_0x47f556['terminate']());},0x1388);}catch(_0x3a444a){this[_0x29860b(0x21d)]['error'](_0x29860b(0x28e),{'error':_0x3a444a['message'],'stack':_0x3a444a[_0x29860b(0x2be)]});}}[a0_0x433fed(0x23d)](_0xdfe663){const _0x4f3988=a0_0x433fed;this[_0x4f3988(0x2b5)]=_0xdfe663,this[_0x4f3988(0x21d)]?.['info'](_0x4f3988(0x2d8),{'hasManager':!!_0xdfe663});}[a0_0x433fed(0x2b9)](_0x107de5){const _0x126675=a0_0x433fed;if(!_0x107de5)return null;const _0x293a55=_0x107de5['toLowerCase']();if(_0x293a55['includes'](_0x126675(0x280))||_0x293a55['includes']('claude'))return'anthropic';else{if(_0x293a55['includes']('openai')||_0x293a55['includes']('gpt'))return'openai';else{if(_0x293a55['includes']('deepseek'))return'deepseek';else{if(_0x293a55['includes']('phi'))return _0x126675(0x2bb);}}}return null;}[a0_0x433fed(0x238)](){const _0x3354b9=a0_0x433fed;return{'isRunning':this[_0x3354b9(0x2a5)],'port':this[_0x3354b9(0x249)],'host':this['host'],'connections':this[_0x3354b9(0x1eb)][_0x3354b9(0x2d5)],'sessions':this[_0x3354b9(0x2b1)]['size'],'url':_0x3354b9(0x25b)+this[_0x3354b9(0x270)]+':'+this['port']};}async['shutdown'](){const _0x5627eb=a0_0x433fed;if(!this[_0x5627eb(0x2a5)])return;this[_0x5627eb(0x21d)]['info']('Shutting\x20down\x20web\x20server...');for(const _0x1fa622 of this[_0x5627eb(0x1eb)][_0x5627eb(0x277)]()){_0x1fa622['ws'][_0x5627eb(0x216)]();}return this[_0x5627eb(0x1eb)]['clear'](),this[_0x5627eb(0x28b)][_0x5627eb(0x216)](),new Promise(_0x18862f=>{const _0x420f61=_0x5627eb;this[_0x420f61(0x22e)][_0x420f61(0x216)](()=>{const _0x8298aa=_0x420f61;this['isRunning']=![],this[_0x8298aa(0x21d)]['info']('Web\x20server\x20shutdown\x20complete'),_0x18862f();});});}}export default WebServer;function a0_0x269f(){const _0x44ac91=['yxjNDG','tMv3iefNzw50','ChjVAMvJDerPCG','ywXYzwfKEsbHy3rPDMu','Cg9ZDa','DxbSB2fKrMLSzq','zNjVBq','t1busu9ouW','yMfZzw5HBwu','zMLSzuf0DgfJAg1LBNrtzxj2AwnL','CMvHzezPBgu','ANnVBG','C3rHDa','Bw9Kzq','y2XVC2u','D2fYBG','z2v0qwXSqwDLBNrZ','DgfYz2v0qwDLBNrjzcbPCYbYzxf1AxjLza','qxP1CMuGyMfJA2vUzcbYzxf1zxn0igzHAwXLzcaTie5piezbteXcqunliefwquLmqujmrq','qwDLBNqGBM90igzVDw5K','rMfPBgvKihrVihjLBw92zsbbueKGA2v5CW','Bg9Nz2vY','l2fWAs9SBg0VBw9KzwXZ','AM9PBG','l2fWAs9ZzxnZAw9UCW','v2vIu29JA2v0ignVBM5Ly3rPB24Gyxr0zw1WDa','yxbWBgLJyxrPB24VANnVBG','zMLSzq','AxngAwXL','mZmYnhLuAM16tW','AgvHBhrOEq','qwDLBNqG','y2fWywjPBgL0AwvZ','zMLSztOVlW','CgfYyw1Z','Cgf0y2G','BgvUz3rO','C3vIC3rY','C2vYDMvY','zMLSDgvY','BwvZC2fNzq','AM9PBL9ZzxnZAw9U','rMfPBgvKihrVigrLBgv0zsbHDhrHy2HTzw50','z2v0','DxbKyxrLzef0','u2v0DgLUzYb1CcbxzwjtB2nRzxqGC2vYDMvY','DMvUzg9YqxbPs2v5','yxv0Ag9YAxPHDgLVBG','z2v0u3rHDhvZ','C3rHDhvZvgv4Da','rMfPBgvKihrVihn0B3aGyxv0B25VBw91CYbLEgvJDxrPB24','A2v5CW','DgHLBG','C2v0qxbPs2v5twfUywDLCG','BgfZDefJDgL2Axr5','C2vZC2LVBKLK','l2fWAs9Hz2vUDhmVoMfNzw50swqVC3rVCa','y3vYCMvUDe1VzgvS','C2v0DxbsB3v0zxm','qwnJzxnZlunVBNrYB2WTqwXSB3CTsgvHzgvYCW','C29JA2v0','z2v0qxzHAwXHyMXLvg9VBhngB3jvsq','z2v0qxr0ywnOBwvUDhm','l3rTCc9SB3HPys1PBwfNzxm','C2vZC2LVBKLe','Cg9YDa','v2vIifvjig5VDcbIDwLSDc4GuNvUoIbUCg0GCNvUigj1AwXKoNvP','C3rHDgvnyw5Hz2vY','vw5RBM93BIbxzwjtB2nRzxqGBwvZC2fNzsb0ExbL','qMvHCMvYia','qKfex1jfuvvfu1q','zgLYBMfTzq','CMvXDwvZDeLK','Bw9KzwXZu2vYDMLJzq','l2fWAs9Hz2vUDhmVoMfNzw50swqVyxr0ywnOBwvUDhmVDxbSB2fK','rMfPBgvKihrVihvWzgf0zsbHDhrHy2HTzw50','CgfYC2u','C29YDa','zxjYB3i','ntu1ntC0ENzQuu1R','qxr0ywnOBwvUDcb1CgrHDgvK','Cgf0Aa','B3jJAgvZDhjHDg9Y','Ahr0CdOVlW','mtC0mtC5mdz1C2P6zxu','C2v0','y2f0y2G','l2fWAs9LEhbSB3jLCG','ywDLBNrqB29S','ywDLBNq','rMfPBgvKihrVigDLDcbHy3rPDMuGC2vZC2LVBNm','u2vZC2LVBIbjrcbPCYbYzxf1AxjLza','ugf0AcbPCYbUB3qGysbKAxjLy3rVCNK','zgvSzxrL','zMLSzuLK','CMvHzgrPCG','AxnmB2fKzwq','cVcFM5eGu2H1DhrPBMCGzg93BIb3zwiGC2vYDMvYlI4U','uhjVEhLPBMCGy2HHDcbYzxf1zxn0ihrVieXVEgLHief6DxjLigjHy2TLBMq','rMfPBgvKihrVihvWBg9HzcbMAwXLigf0DgfJAg1LBNq','C2H1DgrVD24','l2fWAs9MAwXLCW','ChvZAa','Bg9N','Ag9ZDa','mti2Affewgjn','u2vYDMLUzYb0B29SCYbPBMzVCM1HDgLVBG','Aw5PDgLHBgL6zq','qxr0ywnOBwvUDcbKzwXLDgvK','v0vc','mtbTyG','DMfSDwvZ','B3jPz2LU','tw9KzwXZihjLzNjLC2HLzcb3AxrOig5LDYbbueKGA2v5','su5urvjoquXFu0vsvKvsx0vsuK9s','qvbjigTLExmGDxbKyxrLzcbMB3iGC2vZC2LVBG','Dg9ju09tDhjPBMC','t3jJAgvZDhjHDg9YiefqssbLCNjVCG','qvbjigTLEsbTyw5Hz2vYig5VDcbHDMfPBgfIBgu','yxbPs2v5','yw50AhjVCgLJ','BxnNlq','z2v0qwDLBNrnzxrHzgf0yq','ywn0AxzL','C2vUzezPBgu','uMvMzxjLBMnLihjLBw92zwq','Aw5MBW','vg9VBhmGCMvNAxn0CNKGBM90igf2ywLSywjSzq','D29YA2LUz0rPCMvJDg9YEq','DMvUzg9Ys2v5CW','DxbKyxrLx2fNzw50','D3nZ','mtqXmejusLr5qq','otm5nZq4nNrksxLUwG','4P2mifDLyLnVy2TLDcbZzxj2zxiGDgvZDdOGrvHdrvbusu9o','Bg94AwfbCgLlzxK','l2fWAs9OzwfSDgG','mc4WlJaUma','CMvX','lI4VlI4VD2vIlxvPl2j1AwXK','BMfTzq','CMvZB2X2zq','BxrPBwu','AgvHzgvYCW','Cg9UzW','Dg9tDhjPBMC','CgH5C2LJywXSEurLBgv0zwq','l2fWAs9MAwXLlwv4CgXVCMvY','C3rHDhvZ','BM93','ntm4ntuWvwzJrM14','qxP1CMuGyMfJA2vUzcbLCNjVCJOG','zgLYzwn0B3j5','Ahr0Chm6lY9HDxrVCgLSB3qTyxbPlMf6DxjLD2vIC2L0zxmUBMv0l2XSBs9TB2rLBhm','4PYfifDLyLnVy2TLDcbZzxj2zxiGDgvZDdOGu1vdq0vtu0zvta','CMvSyxrPDMu','mZy2oteWmgXTrfbcAq','AxnsDw5UAw5N','l2fWAs9RzxLZ','D3nnyw5Hz2vY','l2fWAs9PBwfNzxmVoNnLC3nPB25jzc86zMLSzw5HBwu','Bw9KzwW','DgvZDfDLyLnVy2TLDfnLCNzLCG','zMLSzu5HBwu','q09otKvdveLorW','l2fWAs9HDhrHy2HTzw50CY86zMLSzuLK','l2fWAs9VCMnOzxn0CMf0B3i','Bwv0Ag9K','C2vUza','C2vZC2LVBNm','z2v0s2v5C0zVCLjLCxvLC3q','Aw1Hz2vZ','zgLYzwn0B3j5qwnJzxnZ','yxbPs2v5twfUywDLCG','ww91igfYzsbHigHLBhbMDwWGquKGyxnZAxn0yw50lG','y29UzMLN','AxneAxjLy3rVCNK','x2DLDfzLBMrVCKzYB21nB2rLBa','D3m6lY9SB2nHBgHVC3q6','BwLJCM9ZB2z0','CMvTB3zLu2vZC2LVBKTLExm','zgvIDwC','C3rHy2S','BwfW','mJKXnLLsyNv6tq','Aw1WB3j0qxjJAgL2zwrbz2vUDa','Cgf5Bg9Hza','oeLwuLj1BG','8j+AGcbtDgfYDgLUzYbmB3HPysbxzwiGu2vYDMvYigLUihn0yw5KywXVBMuGBw9Kzs4UlG','sw52ywXPzcbMAwXLBMfTzq','C3rYAw5NAwz5','tK9ux0zpvu5e','C2vUzfn0yxr1CW','qwnJzxnZlunVBNrYB2WTqwXSB3CTt3jPz2LU','qwnJzxnZlunVBNrYB2WTqwXSB3CTtwv0Ag9KCW','4P2miezHAwXLzcb0BYbZzw5KifDLyLnVy2TLDcbTzxnZywDL','tM8GqvbjigTLEsbJB25MAwD1CMvKlIbqBgvHC2uGy29UzMLNDxjLihLVDxiGtg94AweGqvbjigTLEsbPBIbtzxr0Aw5NCY4','CM91DgvY','tM8GqvbjigTLExmGzM91BMqGzM9YihnLC3nPB24','l2fWAs9MAwXLCY91CgXVywq','q1jfqvrfx0fhru5u','rMfPBgvKihrVigDLDcb0B29SCYbPBMzVCM1HDgLVBG','v2vIu29JA2v0ignVBM5Ly3rPB24Gy2XVC2vK','yNjVywrJyxn0vg9tzxnZAw9U','AgvHzgvYC1nLBNq','C2L6zq','CxvLCNK','qxr0ywnOBwvUDcbUB3qGzM91BMq','qvbjigTLEsbTyw5Hz2vYihnLDcbMB3iGD2vIihnLCNzLCG','sfruucbZzxj2zxiGC3rHCNrLzcbZDwnJzxnZzNvSBhK','ywXS','igLTCg9YDgvKihn1y2nLC3nMDwXSEq','DxrMoa','Aw5JBhvKzxm','C3vJy2vZCW','ms4WlJa','CMvTB3rLqwrKCMvZCW','y3DK','C3rVCef1Dg9UB21VDxnfEgvJDxrPB24','y29UBMvJDgvK','ywDLBNrjza','yxbW','ndi1nvrowxLHEq','z2v0qxr0ywnOBwvUDa','l2fWAs9RzxLZlZPZzxnZAw9Uswq','yM9KEq','AxrLCMf0Aw9Uq291BNq','zw52','DxjS','y29UBMvJDgLVBNm','ChjVy2vZC1jLCxvLC3q','zMLSzuv4CgXVCMvYtw9KDwXL','CMfUzg9T','ue9tva','ChjVAMvJDa','CMvHzhLtDgf0zq','tw9JAYbVCMnOzxn0CMf0B3iGywn0Aw9U','v2vIu29JA2v0ihnLCNzLCIbLCNjVCJO','y29Kzq','ywDLBNrFBw9Kzv9JAgfUz2vK','l2fWAs9Hz2vUDhmVyxzHAwXHyMXL','zMLSzw5HBwu','y29UBMvJDgLVBG','DxnL','B3bLBG','Dgv4Da','l2fWAs9LEhbSB3jLCI9PBMzV','v2vIu29JA2v0ihnLCNzLCIbPCYbUB3CGBgLZDgvUAw5N','Aw1WB3j0rNjVBufNzw50','DhLWzq','D3jPDgvgAwXL','y29UDgvUDa','4P2mifDLyLnVy2TLDcbZzxj2zxiGDgvZDdOGveLnru9vva','v2vIu29JA2v0igPVAw5LzcbZzxnZAw9U','l2fWAs90B29SCW','l2fWAs9HDhrHy2HTzw50CY86zMLSzuLKl3bYzxzPzxC','rMLSzsbHDhrHy2HTzw50ihnLCNzPy2uGBM90igf2ywLSywjSzq','CM9Szq'];a0_0x269f=function(){return _0x44ac91;};return a0_0x269f();}function a0_0x5128(_0x45ea26,_0x127999){_0x45ea26=_0x45ea26-0x1da;const _0x269f27=a0_0x269f();let _0x512818=_0x269f27[_0x45ea26];if(a0_0x5128['gcmkcP']===undefined){var _0x4d5e5b=function(_0x1cf87c){const _0x1eeda3='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2ec7b9='',_0x17b783='';for(let _0x321fe9=0x0,_0x1f7e32,_0x2842d3,_0x41cfa1=0x0;_0x2842d3=_0x1cf87c['charAt'](_0x41cfa1++);~_0x2842d3&&(_0x1f7e32=_0x321fe9%0x4?_0x1f7e32*0x40+_0x2842d3:_0x2842d3,_0x321fe9++%0x4)?_0x2ec7b9+=String['fromCharCode'](0xff&_0x1f7e32>>(-0x2*_0x321fe9&0x6)):0x0){_0x2842d3=_0x1eeda3['indexOf'](_0x2842d3);}for(let _0x3cbbd8=0x0,_0x119a95=_0x2ec7b9['length'];_0x3cbbd8<_0x119a95;_0x3cbbd8++){_0x17b783+='%'+('00'+_0x2ec7b9['charCodeAt'](_0x3cbbd8)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x17b783);};a0_0x5128['FCMjZC']=_0x4d5e5b,a0_0x5128['JXvfYQ']={},a0_0x5128['gcmkcP']=!![];}const _0x1e6507=_0x269f27[0x0],_0x4bb792=_0x45ea26+_0x1e6507,_0x6ac154=a0_0x5128['JXvfYQ'][_0x4bb792];return!_0x6ac154?(_0x512818=a0_0x5128['FCMjZC'](_0x512818),a0_0x5128['JXvfYQ'][_0x4bb792]=_0x512818):_0x512818=_0x6ac154,_0x512818;}if(import.meta.url===a0_0x433fed(0x229)+process[a0_0x433fed(0x208)][0x1]){const simpleLogger={'info':(_0x3e5844,_0x29dce4)=>console['log']('[INFO]\x20'+_0x3e5844,_0x29dce4?JSON['stringify'](_0x29dce4,null,0x2):''),'error':(_0x214d5d,_0x5c383e)=>console[a0_0x433fed(0x256)]('[ERROR]\x20'+_0x214d5d,_0x5c383e?JSON[a0_0x433fed(0x2c6)](_0x5c383e,null,0x2):''),'warn':(_0xd33c28,_0x26bb99)=>console[a0_0x433fed(0x217)]('[WARN]\x20'+_0xd33c28,_0x26bb99?JSON[a0_0x433fed(0x2c6)](_0x26bb99,null,0x2):''),'debug':(_0x18d77,_0x1c377f)=>console['log']('[DEBUG]\x20'+_0x18d77,_0x1c377f?JSON[a0_0x433fed(0x2c6)](_0x1c377f,null,0x2):'')},mockOrchestrator={'processAction':async(_0x523424,_0x419418)=>{const _0x5567b2=a0_0x433fed;simpleLogger['info'](_0x5567b2(0x1f2),{'action':_0x523424,'data':_0x419418});switch(_0x523424){case ORCHESTRATOR_ACTIONS['LIST_AGENTS']:return{'success':!![],'data':[]};case ORCHESTRATOR_ACTIONS[_0x5567b2(0x2d0)]:return{'success':!![],'data':{'id':'agent-'+Date['now'](),'name':_0x419418[_0x5567b2(0x294)]||_0x5567b2(0x209),'status':'active','model':_0x419418['model']||'anthropic-sonnet','systemPrompt':_0x419418['systemPrompt']||_0x5567b2(0x2b6)}};case ORCHESTRATOR_ACTIONS['SEND_MESSAGE']:return{'success':!![],'data':{'message':{'id':_0x5567b2(0x281)+Date[_0x5567b2(0x29d)](),'content':'Echo:\x20'+_0x419418['message'],'timestamp':new Date()['toISOString']()}}};default:return{'success':![],'error':'Unknown\x20action:\x20'+_0x523424};}}},server=new WebServer(mockOrchestrator,simpleLogger,{'port':0x1f90,'host':a0_0x433fed(0x291)});console['log'](a0_0x433fed(0x2c4)),server['startServer']()[a0_0x433fed(0x23c)](()=>{const _0x229a4f=a0_0x433fed,_0x32eab8=server[_0x229a4f(0x238)]();console['log']('✅\x20Web\x20Server\x20running\x20at\x20'+_0x32eab8[_0x229a4f(0x1ea)]),console['log']('📱\x20Web\x20UI\x20available\x20at:\x20http://localhost:3001\x20(if\x20running)'),console[_0x229a4f(0x26f)]('🔧\x20API\x20available\x20at:\x20http://localhost:8080/api');})['catch'](_0x24408c=>{const _0x634f84=a0_0x433fed;console['error']('❌\x20Failed\x20to\x20start\x20web\x20server:',_0x24408c[_0x634f84(0x230)]),process['exit'](0x1);}),process['on']('SIGINT',async()=>{const _0x1c018d=a0_0x433fed;console['log'](_0x1c018d(0x269)),await server[_0x1c018d(0x26c)](),process['exit'](0x0);});}