@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.
- package/README.md +44 -54
- package/bin/cli.js +1 -115
- package/bin/loxia-terminal-v2.js +3 -0
- package/bin/loxia-terminal.js +3 -0
- package/bin/start-with-terminal.js +3 -0
- package/package.json +14 -15
- package/scripts/install-scanners.js +1 -235
- package/src/analyzers/CSSAnalyzer.js +1 -297
- package/src/analyzers/ConfigValidator.js +1 -690
- package/src/analyzers/ESLintAnalyzer.js +1 -320
- package/src/analyzers/JavaScriptAnalyzer.js +1 -261
- package/src/analyzers/PrettierFormatter.js +1 -247
- package/src/analyzers/PythonAnalyzer.js +1 -266
- package/src/analyzers/SecurityAnalyzer.js +1 -729
- package/src/analyzers/TypeScriptAnalyzer.js +1 -247
- package/src/analyzers/codeCloneDetector/analyzer.js +1 -344
- package/src/analyzers/codeCloneDetector/detector.js +1 -203
- package/src/analyzers/codeCloneDetector/index.js +1 -160
- package/src/analyzers/codeCloneDetector/parser.js +1 -199
- package/src/analyzers/codeCloneDetector/reporter.js +1 -148
- package/src/analyzers/codeCloneDetector/scanner.js +1 -59
- package/src/core/agentPool.js +1 -1474
- package/src/core/agentScheduler.js +1 -2147
- package/src/core/contextManager.js +1 -709
- package/src/core/messageProcessor.js +1 -732
- package/src/core/orchestrator.js +1 -548
- package/src/core/stateManager.js +1 -877
- package/src/index.js +1 -631
- package/src/interfaces/cli.js +1 -549
- package/src/interfaces/terminal/__tests__/smoke/advancedFeatures.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/agentControl.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/agents.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/components.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/connection.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/imports.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/messages.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/tools.test.js +1 -0
- package/src/interfaces/terminal/api/apiClient.js +1 -0
- package/src/interfaces/terminal/api/messageRouter.js +1 -0
- package/src/interfaces/terminal/api/session.js +1 -0
- package/src/interfaces/terminal/api/websocket.js +1 -0
- package/src/interfaces/terminal/components/AgentCreator.js +1 -0
- package/src/interfaces/terminal/components/AgentEditor.js +1 -0
- package/src/interfaces/terminal/components/AgentSwitcher.js +1 -0
- package/src/interfaces/terminal/components/ErrorBoundary.js +1 -0
- package/src/interfaces/terminal/components/ErrorPanel.js +1 -0
- package/src/interfaces/terminal/components/Header.js +1 -0
- package/src/interfaces/terminal/components/HelpPanel.js +1 -0
- package/src/interfaces/terminal/components/InputBox.js +1 -0
- package/src/interfaces/terminal/components/Layout.js +1 -0
- package/src/interfaces/terminal/components/LoadingSpinner.js +1 -0
- package/src/interfaces/terminal/components/MessageList.js +1 -0
- package/src/interfaces/terminal/components/MultilineTextInput.js +1 -0
- package/src/interfaces/terminal/components/SearchPanel.js +1 -0
- package/src/interfaces/terminal/components/SettingsPanel.js +1 -0
- package/src/interfaces/terminal/components/StatusBar.js +1 -0
- package/src/interfaces/terminal/components/TextInput.js +1 -0
- package/src/interfaces/terminal/config/agentEditorConstants.js +1 -0
- package/src/interfaces/terminal/config/constants.js +1 -0
- package/src/interfaces/terminal/index.js +1 -0
- package/src/interfaces/terminal/state/useAgentControl.js +1 -0
- package/src/interfaces/terminal/state/useAgents.js +1 -0
- package/src/interfaces/terminal/state/useConnection.js +1 -0
- package/src/interfaces/terminal/state/useMessages.js +1 -0
- package/src/interfaces/terminal/state/useTools.js +1 -0
- package/src/interfaces/terminal/utils/debugLogger.js +1 -0
- package/src/interfaces/terminal/utils/settingsStorage.js +1 -0
- package/src/interfaces/terminal/utils/theme.js +1 -0
- package/src/interfaces/webServer.js +1 -2162
- package/src/modules/fileExplorer/controller.js +1 -280
- package/src/modules/fileExplorer/index.js +1 -37
- package/src/modules/fileExplorer/middleware.js +1 -92
- package/src/modules/fileExplorer/routes.js +1 -125
- package/src/modules/fileExplorer/types.js +1 -44
- package/src/services/aiService.js +1 -1232
- package/src/services/apiKeyManager.js +1 -164
- package/src/services/benchmarkService.js +1 -366
- package/src/services/budgetService.js +1 -539
- package/src/services/contextInjectionService.js +1 -247
- package/src/services/conversationCompactionService.js +1 -637
- package/src/services/errorHandler.js +1 -810
- package/src/services/fileAttachmentService.js +1 -544
- package/src/services/modelRouterService.js +1 -366
- package/src/services/modelsService.js +1 -322
- package/src/services/qualityInspector.js +1 -796
- package/src/services/tokenCountingService.js +1 -536
- package/src/tools/agentCommunicationTool.js +1 -1344
- package/src/tools/agentDelayTool.js +1 -485
- package/src/tools/asyncToolManager.js +1 -604
- package/src/tools/baseTool.js +1 -800
- package/src/tools/browserTool.js +1 -920
- package/src/tools/cloneDetectionTool.js +1 -621
- package/src/tools/dependencyResolverTool.js +1 -1215
- package/src/tools/fileContentReplaceTool.js +1 -875
- package/src/tools/fileSystemTool.js +1 -1107
- package/src/tools/fileTreeTool.js +1 -853
- package/src/tools/imageTool.js +1 -901
- package/src/tools/importAnalyzerTool.js +1 -1060
- package/src/tools/jobDoneTool.js +1 -248
- package/src/tools/seekTool.js +1 -956
- package/src/tools/staticAnalysisTool.js +1 -1778
- package/src/tools/taskManagerTool.js +1 -2873
- package/src/tools/terminalTool.js +1 -2304
- package/src/tools/webTool.js +1 -1430
- package/src/types/agent.js +1 -519
- package/src/types/contextReference.js +1 -972
- package/src/types/conversation.js +1 -730
- package/src/types/toolCommand.js +1 -747
- package/src/utilities/attachmentValidator.js +1 -292
- package/src/utilities/configManager.js +1 -582
- package/src/utilities/constants.js +1 -722
- package/src/utilities/directoryAccessManager.js +1 -535
- package/src/utilities/fileProcessor.js +1 -307
- package/src/utilities/logger.js +1 -436
- package/src/utilities/tagParser.js +1 -1246
- package/src/utilities/toolConstants.js +1 -317
- package/web-ui/build/index.html +2 -2
- package/web-ui/build/static/{index-Dy2bYbOa.css → index-CClD1090.css} +1 -1
- 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);});}
|