claudia-orchestrator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +109 -0
- package/dist/cli-parser.d.ts +11 -0
- package/dist/cli-parser.d.ts.map +1 -0
- package/dist/cli-parser.js +57 -0
- package/dist/cli-parser.js.map +1 -0
- package/dist/cui-server.d.ts +69 -0
- package/dist/cui-server.d.ts.map +1 -0
- package/dist/cui-server.js +705 -0
- package/dist/cui-server.js.map +1 -0
- package/dist/mcp-server/claudia-tools.d.ts +15 -0
- package/dist/mcp-server/claudia-tools.d.ts.map +1 -0
- package/dist/mcp-server/claudia-tools.js +366 -0
- package/dist/mcp-server/claudia-tools.js.map +1 -0
- package/dist/mcp-server/index.d.ts +3 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +176 -0
- package/dist/mcp-server/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +18 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +136 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/cors-setup.d.ts +7 -0
- package/dist/middleware/cors-setup.d.ts.map +1 -0
- package/dist/middleware/cors-setup.js +8 -0
- package/dist/middleware/cors-setup.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +4 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +27 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/query-parser.d.ts +11 -0
- package/dist/middleware/query-parser.d.ts.map +1 -0
- package/dist/middleware/query-parser.js +68 -0
- package/dist/middleware/query-parser.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +4 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +29 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/process-daemon/index.d.ts +14 -0
- package/dist/process-daemon/index.d.ts.map +1 -0
- package/dist/process-daemon/index.js +51 -0
- package/dist/process-daemon/index.js.map +1 -0
- package/dist/process-daemon/process-daemon.d.ts +78 -0
- package/dist/process-daemon/process-daemon.d.ts.map +1 -0
- package/dist/process-daemon/process-daemon.js +568 -0
- package/dist/process-daemon/process-daemon.js.map +1 -0
- package/dist/process-daemon/process-manager-client.d.ts +108 -0
- package/dist/process-daemon/process-manager-client.d.ts.map +1 -0
- package/dist/process-daemon/process-manager-client.js +314 -0
- package/dist/process-daemon/process-manager-client.js.map +1 -0
- package/dist/process-daemon/process-manager-interface.d.ts +47 -0
- package/dist/process-daemon/process-manager-interface.d.ts.map +1 -0
- package/dist/process-daemon/process-manager-interface.js +8 -0
- package/dist/process-daemon/process-manager-interface.js.map +1 -0
- package/dist/process-daemon/test-daemon.d.ts +12 -0
- package/dist/process-daemon/test-daemon.d.ts.map +1 -0
- package/dist/process-daemon/test-daemon.js +81 -0
- package/dist/process-daemon/test-daemon.js.map +1 -0
- package/dist/process-daemon/types.d.ts +85 -0
- package/dist/process-daemon/types.d.ts.map +1 -0
- package/dist/process-daemon/types.js +8 -0
- package/dist/process-daemon/types.js.map +1 -0
- package/dist/routes/claudia.routes.d.ts +10 -0
- package/dist/routes/claudia.routes.d.ts.map +1 -0
- package/dist/routes/claudia.routes.js +123 -0
- package/dist/routes/claudia.routes.js.map +1 -0
- package/dist/routes/config.routes.d.ts +4 -0
- package/dist/routes/config.routes.d.ts.map +1 -0
- package/dist/routes/config.routes.js +27 -0
- package/dist/routes/config.routes.js.map +1 -0
- package/dist/routes/conversation.routes.d.ts +8 -0
- package/dist/routes/conversation.routes.d.ts.map +1 -0
- package/dist/routes/conversation.routes.js +870 -0
- package/dist/routes/conversation.routes.js.map +1 -0
- package/dist/routes/filesystem.routes.d.ts +4 -0
- package/dist/routes/filesystem.routes.d.ts.map +1 -0
- package/dist/routes/filesystem.routes.js +86 -0
- package/dist/routes/filesystem.routes.js.map +1 -0
- package/dist/routes/gemini.routes.d.ts +4 -0
- package/dist/routes/gemini.routes.d.ts.map +1 -0
- package/dist/routes/gemini.routes.js +93 -0
- package/dist/routes/gemini.routes.js.map +1 -0
- package/dist/routes/insights.routes.d.ts +17 -0
- package/dist/routes/insights.routes.d.ts.map +1 -0
- package/dist/routes/insights.routes.js +417 -0
- package/dist/routes/insights.routes.js.map +1 -0
- package/dist/routes/license.routes.d.ts +3 -0
- package/dist/routes/license.routes.d.ts.map +1 -0
- package/dist/routes/license.routes.js +111 -0
- package/dist/routes/license.routes.js.map +1 -0
- package/dist/routes/log.routes.d.ts +3 -0
- package/dist/routes/log.routes.d.ts.map +1 -0
- package/dist/routes/log.routes.js +65 -0
- package/dist/routes/log.routes.js.map +1 -0
- package/dist/routes/notifications.routes.d.ts +4 -0
- package/dist/routes/notifications.routes.d.ts.map +1 -0
- package/dist/routes/notifications.routes.js +71 -0
- package/dist/routes/notifications.routes.js.map +1 -0
- package/dist/routes/permission.routes.d.ts +4 -0
- package/dist/routes/permission.routes.d.ts.map +1 -0
- package/dist/routes/permission.routes.js +116 -0
- package/dist/routes/permission.routes.js.map +1 -0
- package/dist/routes/question.routes.d.ts +8 -0
- package/dist/routes/question.routes.d.ts.map +1 -0
- package/dist/routes/question.routes.js +82 -0
- package/dist/routes/question.routes.js.map +1 -0
- package/dist/routes/streaming.routes.d.ts +4 -0
- package/dist/routes/streaming.routes.d.ts.map +1 -0
- package/dist/routes/streaming.routes.js +28 -0
- package/dist/routes/streaming.routes.js.map +1 -0
- package/dist/routes/system.routes.d.ts +5 -0
- package/dist/routes/system.routes.d.ts.map +1 -0
- package/dist/routes/system.routes.js +103 -0
- package/dist/routes/system.routes.js.map +1 -0
- package/dist/routes/working-directories.routes.d.ts +4 -0
- package/dist/routes/working-directories.routes.d.ts.map +1 -0
- package/dist/routes/working-directories.routes.js +25 -0
- package/dist/routes/working-directories.routes.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +34 -0
- package/dist/server.js.map +1 -0
- package/dist/services/ToolMetricsService.d.ts +53 -0
- package/dist/services/ToolMetricsService.d.ts.map +1 -0
- package/dist/services/ToolMetricsService.js +230 -0
- package/dist/services/ToolMetricsService.js.map +1 -0
- package/dist/services/anthropic-service.d.ts +186 -0
- package/dist/services/anthropic-service.d.ts.map +1 -0
- package/dist/services/anthropic-service.js +1132 -0
- package/dist/services/anthropic-service.js.map +1 -0
- package/dist/services/claude-history-reader.d.ts +126 -0
- package/dist/services/claude-history-reader.d.ts.map +1 -0
- package/dist/services/claude-history-reader.js +717 -0
- package/dist/services/claude-history-reader.js.map +1 -0
- package/dist/services/claude-process-manager.d.ts +108 -0
- package/dist/services/claude-process-manager.d.ts.map +1 -0
- package/dist/services/claude-process-manager.js +909 -0
- package/dist/services/claude-process-manager.js.map +1 -0
- package/dist/services/claude-router-service.d.ts +19 -0
- package/dist/services/claude-router-service.d.ts.map +1 -0
- package/dist/services/claude-router-service.js +160 -0
- package/dist/services/claude-router-service.js.map +1 -0
- package/dist/services/claudia-service.d.ts +77 -0
- package/dist/services/claudia-service.d.ts.map +1 -0
- package/dist/services/claudia-service.js +194 -0
- package/dist/services/claudia-service.js.map +1 -0
- package/dist/services/commands-service.d.ts +18 -0
- package/dist/services/commands-service.d.ts.map +1 -0
- package/dist/services/commands-service.js +76 -0
- package/dist/services/commands-service.js.map +1 -0
- package/dist/services/config-service.d.ts +68 -0
- package/dist/services/config-service.d.ts.map +1 -0
- package/dist/services/config-service.js +429 -0
- package/dist/services/config-service.js.map +1 -0
- package/dist/services/conversation-cache.d.ts +86 -0
- package/dist/services/conversation-cache.d.ts.map +1 -0
- package/dist/services/conversation-cache.js +235 -0
- package/dist/services/conversation-cache.js.map +1 -0
- package/dist/services/conversation-status-manager.d.ts +98 -0
- package/dist/services/conversation-status-manager.d.ts.map +1 -0
- package/dist/services/conversation-status-manager.js +295 -0
- package/dist/services/conversation-status-manager.js.map +1 -0
- package/dist/services/cost-tracker.d.ts +87 -0
- package/dist/services/cost-tracker.d.ts.map +1 -0
- package/dist/services/cost-tracker.js +335 -0
- package/dist/services/cost-tracker.js.map +1 -0
- package/dist/services/file-system-service.d.ts +61 -0
- package/dist/services/file-system-service.d.ts.map +1 -0
- package/dist/services/file-system-service.js +348 -0
- package/dist/services/file-system-service.js.map +1 -0
- package/dist/services/gemini-service.d.ts +72 -0
- package/dist/services/gemini-service.d.ts.map +1 -0
- package/dist/services/gemini-service.js +431 -0
- package/dist/services/gemini-service.js.map +1 -0
- package/dist/services/insight-queue.d.ts +99 -0
- package/dist/services/insight-queue.d.ts.map +1 -0
- package/dist/services/insight-queue.js +277 -0
- package/dist/services/insight-queue.js.map +1 -0
- package/dist/services/insights-service.d.ts +102 -0
- package/dist/services/insights-service.d.ts.map +1 -0
- package/dist/services/insights-service.js +1152 -0
- package/dist/services/insights-service.js.map +1 -0
- package/dist/services/json-lines-parser.d.ts +19 -0
- package/dist/services/json-lines-parser.d.ts.map +1 -0
- package/dist/services/json-lines-parser.js +56 -0
- package/dist/services/json-lines-parser.js.map +1 -0
- package/dist/services/license-service.d.ts +69 -0
- package/dist/services/license-service.d.ts.map +1 -0
- package/dist/services/license-service.js +330 -0
- package/dist/services/license-service.js.map +1 -0
- package/dist/services/log-formatter.d.ts +5 -0
- package/dist/services/log-formatter.d.ts.map +1 -0
- package/dist/services/log-formatter.js +77 -0
- package/dist/services/log-formatter.js.map +1 -0
- package/dist/services/log-stream-buffer.d.ts +11 -0
- package/dist/services/log-stream-buffer.d.ts.map +1 -0
- package/dist/services/log-stream-buffer.js +36 -0
- package/dist/services/log-stream-buffer.js.map +1 -0
- package/dist/services/logger.d.ts +71 -0
- package/dist/services/logger.d.ts.map +1 -0
- package/dist/services/logger.js +215 -0
- package/dist/services/logger.js.map +1 -0
- package/dist/services/mcp-config-generator.d.ts +32 -0
- package/dist/services/mcp-config-generator.d.ts.map +1 -0
- package/dist/services/mcp-config-generator.js +126 -0
- package/dist/services/mcp-config-generator.js.map +1 -0
- package/dist/services/message-filter.d.ts +22 -0
- package/dist/services/message-filter.d.ts.map +1 -0
- package/dist/services/message-filter.js +57 -0
- package/dist/services/message-filter.js.map +1 -0
- package/dist/services/notification-service.d.ts +45 -0
- package/dist/services/notification-service.d.ts.map +1 -0
- package/dist/services/notification-service.js +184 -0
- package/dist/services/notification-service.js.map +1 -0
- package/dist/services/permission-tracker.d.ts +67 -0
- package/dist/services/permission-tracker.d.ts.map +1 -0
- package/dist/services/permission-tracker.js +161 -0
- package/dist/services/permission-tracker.js.map +1 -0
- package/dist/services/process-manager-factory.d.ts +81 -0
- package/dist/services/process-manager-factory.d.ts.map +1 -0
- package/dist/services/process-manager-factory.js +211 -0
- package/dist/services/process-manager-factory.js.map +1 -0
- package/dist/services/question-tracker.d.ts +47 -0
- package/dist/services/question-tracker.d.ts.map +1 -0
- package/dist/services/question-tracker.js +105 -0
- package/dist/services/question-tracker.js.map +1 -0
- package/dist/services/session-activity-watcher.d.ts +33 -0
- package/dist/services/session-activity-watcher.d.ts.map +1 -0
- package/dist/services/session-activity-watcher.js +194 -0
- package/dist/services/session-activity-watcher.js.map +1 -0
- package/dist/services/session-info-service.d.ts +228 -0
- package/dist/services/session-info-service.d.ts.map +1 -0
- package/dist/services/session-info-service.js +920 -0
- package/dist/services/session-info-service.js.map +1 -0
- package/dist/services/session-insights-service.d.ts +119 -0
- package/dist/services/session-insights-service.d.ts.map +1 -0
- package/dist/services/session-insights-service.js +889 -0
- package/dist/services/session-insights-service.js.map +1 -0
- package/dist/services/stream-manager.d.ts +62 -0
- package/dist/services/stream-manager.d.ts.map +1 -0
- package/dist/services/stream-manager.js +239 -0
- package/dist/services/stream-manager.js.map +1 -0
- package/dist/services/web-push-service.d.ts +48 -0
- package/dist/services/web-push-service.d.ts.map +1 -0
- package/dist/services/web-push-service.js +186 -0
- package/dist/services/web-push-service.js.map +1 -0
- package/dist/services/working-directories-service.d.ts +19 -0
- package/dist/services/working-directories-service.d.ts.map +1 -0
- package/dist/services/working-directories-service.js +103 -0
- package/dist/services/working-directories-service.js.map +1 -0
- package/dist/types/config.d.ts +111 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +14 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/express.d.ts +5 -0
- package/dist/types/express.d.ts.map +1 -0
- package/dist/types/express.js +2 -0
- package/dist/types/express.js.map +1 -0
- package/dist/types/index.d.ts +325 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/insights.d.ts +99 -0
- package/dist/types/insights.d.ts.map +1 -0
- package/dist/types/insights.js +7 -0
- package/dist/types/insights.js.map +1 -0
- package/dist/types/license.d.ts +70 -0
- package/dist/types/license.d.ts.map +1 -0
- package/dist/types/license.js +5 -0
- package/dist/types/license.js.map +1 -0
- package/dist/types/router-config.d.ts +13 -0
- package/dist/types/router-config.d.ts.map +1 -0
- package/dist/types/router-config.js +2 -0
- package/dist/types/router-config.js.map +1 -0
- package/dist/utils/constants.d.ts +26 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +28 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/machine-id.d.ts +7 -0
- package/dist/utils/machine-id.d.ts.map +1 -0
- package/dist/utils/machine-id.js +76 -0
- package/dist/utils/machine-id.js.map +1 -0
- package/dist/utils/server-startup.d.ts +13 -0
- package/dist/utils/server-startup.d.ts.map +1 -0
- package/dist/utils/server-startup.js +20 -0
- package/dist/utils/server-startup.js.map +1 -0
- package/dist/web/assets/main-DAc2rjJ2.css +1 -0
- package/dist/web/assets/main-DvlZ02mT.js +137 -0
- package/dist/web/favicon.png +0 -0
- package/dist/web/favicon.svg +22 -0
- package/dist/web/icon-192x192.png +0 -0
- package/dist/web/icon-512x512.png +0 -0
- package/dist/web/index.html +36 -0
- package/dist/web/manifest.json +61 -0
- package/package.json +174 -0
- package/scripts/postinstall.js +30 -0
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname } from 'path';
|
|
5
|
+
import { displayServerStartup } from './utils/server-startup.js';
|
|
6
|
+
// Get the directory of this module for serving static files
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
import { createProcessManager } from './services/process-manager-factory.js';
|
|
10
|
+
import { StreamManager } from './services/stream-manager.js';
|
|
11
|
+
import { ClaudeHistoryReader } from './services/claude-history-reader.js';
|
|
12
|
+
import { PermissionTracker } from './services/permission-tracker.js';
|
|
13
|
+
import { QuestionTracker } from './services/question-tracker.js';
|
|
14
|
+
import { MCPConfigGenerator } from './services/mcp-config-generator.js';
|
|
15
|
+
import { FileSystemService } from './services/file-system-service.js';
|
|
16
|
+
import { ConfigService } from './services/config-service.js';
|
|
17
|
+
import { SessionInfoService } from './services/session-info-service.js';
|
|
18
|
+
import { ConversationStatusManager } from './services/conversation-status-manager.js';
|
|
19
|
+
import { WorkingDirectoriesService } from './services/working-directories-service.js';
|
|
20
|
+
import { ToolMetricsService } from './services/ToolMetricsService.js';
|
|
21
|
+
import { NotificationService } from './services/notification-service.js';
|
|
22
|
+
import { WebPushService } from './services/web-push-service.js';
|
|
23
|
+
import { geminiService } from './services/gemini-service.js';
|
|
24
|
+
import { anthropicService } from './services/anthropic-service.js';
|
|
25
|
+
import { SessionInsightsService } from './services/session-insights-service.js';
|
|
26
|
+
import { ClaudeRouterService } from './services/claude-router-service.js';
|
|
27
|
+
import { CUIError } from './types/index.js';
|
|
28
|
+
import { createLogger } from './services/logger.js';
|
|
29
|
+
import { createConversationRoutes } from './routes/conversation.routes.js';
|
|
30
|
+
import { createSystemRoutes } from './routes/system.routes.js';
|
|
31
|
+
import { createPermissionRoutes } from './routes/permission.routes.js';
|
|
32
|
+
import { createQuestionRoutes } from './routes/question.routes.js';
|
|
33
|
+
import { createFileSystemRoutes } from './routes/filesystem.routes.js';
|
|
34
|
+
import { createLogRoutes } from './routes/log.routes.js';
|
|
35
|
+
import { createStreamingRoutes } from './routes/streaming.routes.js';
|
|
36
|
+
import { createWorkingDirectoriesRoutes } from './routes/working-directories.routes.js';
|
|
37
|
+
import { createConfigRoutes } from './routes/config.routes.js';
|
|
38
|
+
import { createGeminiRoutes } from './routes/gemini.routes.js';
|
|
39
|
+
import { createNotificationsRoutes } from './routes/notifications.routes.js';
|
|
40
|
+
import { createInsightsRoutes } from './routes/insights.routes.js';
|
|
41
|
+
import { createClaudiaRoutes } from './routes/claudia.routes.js';
|
|
42
|
+
import { ClaudiaService } from './services/claudia-service.js';
|
|
43
|
+
import { createLicenseRoutes } from './routes/license.routes.js';
|
|
44
|
+
import { errorHandler } from './middleware/error-handler.js';
|
|
45
|
+
import { requestLogger } from './middleware/request-logger.js';
|
|
46
|
+
import { createCorsMiddleware } from './middleware/cors-setup.js';
|
|
47
|
+
import { queryParser } from './middleware/query-parser.js';
|
|
48
|
+
import { authMiddleware, createAuthMiddleware } from './middleware/auth.js';
|
|
49
|
+
// ViteExpress will be imported dynamically in initialize() if needed
|
|
50
|
+
let ViteExpress;
|
|
51
|
+
/**
|
|
52
|
+
* Main CUI server class
|
|
53
|
+
*/
|
|
54
|
+
export class CUIServer {
|
|
55
|
+
app;
|
|
56
|
+
server;
|
|
57
|
+
processManager; // Initialized in initialize()
|
|
58
|
+
streamManager;
|
|
59
|
+
historyReader;
|
|
60
|
+
statusTracker;
|
|
61
|
+
permissionTracker;
|
|
62
|
+
questionTracker;
|
|
63
|
+
mcpConfigGenerator;
|
|
64
|
+
fileSystemService;
|
|
65
|
+
configService;
|
|
66
|
+
sessionInfoService;
|
|
67
|
+
conversationStatusManager;
|
|
68
|
+
workingDirectoriesService;
|
|
69
|
+
toolMetricsService;
|
|
70
|
+
notificationService;
|
|
71
|
+
webPushService;
|
|
72
|
+
routerService;
|
|
73
|
+
logger;
|
|
74
|
+
port;
|
|
75
|
+
host;
|
|
76
|
+
configOverrides;
|
|
77
|
+
constructor(configOverrides) {
|
|
78
|
+
this.app = express();
|
|
79
|
+
this.configOverrides = configOverrides;
|
|
80
|
+
this.logger = createLogger('CUIServer');
|
|
81
|
+
// TEST: Add debug log right at the start
|
|
82
|
+
this.logger.debug('🔍 TEST: CUIServer constructor started - this should be visible if debug logging works');
|
|
83
|
+
// Initialize config service first
|
|
84
|
+
this.configService = ConfigService.getInstance();
|
|
85
|
+
// Will be set after config is loaded
|
|
86
|
+
this.port = 0;
|
|
87
|
+
this.host = '';
|
|
88
|
+
this.logger.debug('Initializing CUIServer', {
|
|
89
|
+
nodeEnv: process.env.NODE_ENV,
|
|
90
|
+
configOverrides
|
|
91
|
+
});
|
|
92
|
+
// Initialize services
|
|
93
|
+
this.logger.debug('Initializing services');
|
|
94
|
+
// Use singleton to ensure event handlers get the same initialized instance
|
|
95
|
+
this.sessionInfoService = SessionInfoService.getInstance();
|
|
96
|
+
this.historyReader = new ClaudeHistoryReader(this.sessionInfoService);
|
|
97
|
+
// Create a single instance of ConversationStatusManager for both statusTracker and conversationStatusManager
|
|
98
|
+
this.conversationStatusManager = new ConversationStatusManager();
|
|
99
|
+
this.statusTracker = this.conversationStatusManager; // Use the same instance for backward compatibility
|
|
100
|
+
this.toolMetricsService = new ToolMetricsService();
|
|
101
|
+
this.fileSystemService = new FileSystemService();
|
|
102
|
+
// processManager is initialized in initialize() to support daemon mode
|
|
103
|
+
this.streamManager = new StreamManager();
|
|
104
|
+
this.permissionTracker = new PermissionTracker();
|
|
105
|
+
this.questionTracker = new QuestionTracker();
|
|
106
|
+
this.mcpConfigGenerator = new MCPConfigGenerator(this.fileSystemService);
|
|
107
|
+
this.workingDirectoriesService = new WorkingDirectoriesService(this.historyReader, this.logger);
|
|
108
|
+
this.notificationService = new NotificationService();
|
|
109
|
+
this.webPushService = WebPushService.getInstance();
|
|
110
|
+
// Wire up services that don't depend on processManager
|
|
111
|
+
this.permissionTracker.setNotificationService(this.notificationService);
|
|
112
|
+
this.permissionTracker.setConversationStatusManager(this.conversationStatusManager);
|
|
113
|
+
this.permissionTracker.setHistoryReader(this.historyReader);
|
|
114
|
+
// Set up background insights computation when sessions end
|
|
115
|
+
const insightsService = new SessionInsightsService(this.historyReader, this.sessionInfoService);
|
|
116
|
+
this.conversationStatusManager.on('session-ended', ({ claudeSessionId }) => {
|
|
117
|
+
// Compute insights in background (fire and forget)
|
|
118
|
+
insightsService.computeInsights(claudeSessionId)
|
|
119
|
+
.then(insights => insightsService.cacheInsights(claudeSessionId, insights))
|
|
120
|
+
.catch(err => this.logger.debug('Background insights computation failed', { claudeSessionId, error: err }));
|
|
121
|
+
});
|
|
122
|
+
this.logger.debug('Services initialized (processManager deferred to initialize())');
|
|
123
|
+
this.setupMiddleware();
|
|
124
|
+
// Routes and processManager integration are set up in initialize()
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get the Express app instance
|
|
128
|
+
*/
|
|
129
|
+
getApp() {
|
|
130
|
+
return this.app;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the configured port
|
|
134
|
+
*/
|
|
135
|
+
getPort() {
|
|
136
|
+
return this.port;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the configured host
|
|
140
|
+
*/
|
|
141
|
+
getHost() {
|
|
142
|
+
return this.host;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Initialize services without starting the HTTP server
|
|
146
|
+
*/
|
|
147
|
+
async initialize() {
|
|
148
|
+
this.logger.debug('Initialize method called');
|
|
149
|
+
try {
|
|
150
|
+
// Initialize configuration first
|
|
151
|
+
this.logger.debug('Initializing configuration');
|
|
152
|
+
await this.configService.initialize();
|
|
153
|
+
const config = this.configService.getConfig();
|
|
154
|
+
// Initialize session info service
|
|
155
|
+
this.logger.debug('Initializing session info service');
|
|
156
|
+
await this.sessionInfoService.initialize();
|
|
157
|
+
this.logger.debug('Session info service initialized successfully');
|
|
158
|
+
// Initialize process manager
|
|
159
|
+
this.logger.debug('Initializing process manager');
|
|
160
|
+
this.processManager = await createProcessManager({
|
|
161
|
+
historyReader: this.historyReader,
|
|
162
|
+
statusTracker: this.statusTracker,
|
|
163
|
+
toolMetricsService: this.toolMetricsService,
|
|
164
|
+
sessionInfoService: this.sessionInfoService,
|
|
165
|
+
fileSystemService: this.fileSystemService
|
|
166
|
+
});
|
|
167
|
+
this.processManager.setNotificationService(this.notificationService);
|
|
168
|
+
this.processManager.setConversationStatusManager(this.conversationStatusManager);
|
|
169
|
+
// Set up event listeners now that processManager exists
|
|
170
|
+
this.setupProcessManagerIntegration();
|
|
171
|
+
this.setupPermissionTrackerIntegration();
|
|
172
|
+
this.setupQuestionTrackerIntegration();
|
|
173
|
+
this.logger.debug('Process manager initialized successfully');
|
|
174
|
+
this.logger.debug('Initializing Gemini service');
|
|
175
|
+
await geminiService.initialize();
|
|
176
|
+
this.logger.debug('Gemini service initialized successfully');
|
|
177
|
+
this.logger.debug('Initializing Anthropic service');
|
|
178
|
+
await anthropicService.initialize();
|
|
179
|
+
this.logger.debug('Anthropic service initialized successfully');
|
|
180
|
+
// Initialize cost tracker for LLM usage monitoring
|
|
181
|
+
this.logger.debug('Initializing cost tracker');
|
|
182
|
+
const { getCostTracker } = await import('./services/cost-tracker.js');
|
|
183
|
+
await getCostTracker().initialize();
|
|
184
|
+
this.logger.debug('Cost tracker initialized successfully');
|
|
185
|
+
// Initialize insights service (unified event-driven system)
|
|
186
|
+
this.logger.debug('Initializing insights service');
|
|
187
|
+
const { initializeInsightsService } = await import('./services/insights-service.js');
|
|
188
|
+
initializeInsightsService();
|
|
189
|
+
this.logger.debug('Insights service initialized successfully');
|
|
190
|
+
// Initialize router service if configured
|
|
191
|
+
await this.initializeOrReloadRouter(config);
|
|
192
|
+
// Apply overrides if provided (for tests and CLI options)
|
|
193
|
+
this.port = this.configOverrides?.port ?? config.server.port;
|
|
194
|
+
this.host = this.configOverrides?.host ?? config.server.host;
|
|
195
|
+
this.logger.info('Configuration loaded', {
|
|
196
|
+
machineId: config.machine_id,
|
|
197
|
+
port: this.port,
|
|
198
|
+
host: this.host,
|
|
199
|
+
overrides: this.configOverrides ? Object.keys(this.configOverrides) : []
|
|
200
|
+
});
|
|
201
|
+
// Set up routes after services are initialized
|
|
202
|
+
// This allows tests to override services before routes are created
|
|
203
|
+
this.logger.debug('Setting up routes');
|
|
204
|
+
this.setupRoutes();
|
|
205
|
+
// Generate MCP config before starting server
|
|
206
|
+
try {
|
|
207
|
+
const mcpConfigPath = await this.mcpConfigGenerator.generateConfig(this.port);
|
|
208
|
+
this.processManager.setMCPConfigPath(mcpConfigPath);
|
|
209
|
+
this.logger.debug('MCP config generated and set', { path: mcpConfigPath });
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
const isNonProduction = process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development';
|
|
213
|
+
if (isNonProduction) {
|
|
214
|
+
this.logger.warn('MCP config generation failed in non-production environment, proceeding without MCP', {
|
|
215
|
+
error: error instanceof Error ? error.message : String(error),
|
|
216
|
+
nodeEnv: process.env.NODE_ENV
|
|
217
|
+
});
|
|
218
|
+
// Don't set MCP config path - conversations will run without MCP
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
this.logger.error('MCP config generation failed in production environment', {
|
|
222
|
+
error: error instanceof Error ? error.message : String(error)
|
|
223
|
+
});
|
|
224
|
+
throw new CUIError('MCP_CONFIG_REQUIRED', `MCP server files are required in production but not found: ${error instanceof Error ? error.message : String(error)}`, 500);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Subscribe to configuration changes to hot-reload router when needed
|
|
228
|
+
this.configService.onChange(async (newConfig) => {
|
|
229
|
+
try {
|
|
230
|
+
await this.initializeOrReloadRouter(newConfig);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
this.logger.error('Failed to reload router after config change', error);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
this.logger.error('Failed to initialize server:', error, {
|
|
239
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
240
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
241
|
+
});
|
|
242
|
+
if (error instanceof CUIError) {
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
throw new CUIError('SERVER_INIT_FAILED', `Server initialization failed: ${error}`, 500);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Start the server
|
|
252
|
+
*/
|
|
253
|
+
async start() {
|
|
254
|
+
this.logger.debug('Start method called');
|
|
255
|
+
try {
|
|
256
|
+
// Initialize all services
|
|
257
|
+
await this.initialize();
|
|
258
|
+
// Start Express server
|
|
259
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
260
|
+
this.logger.debug('Creating HTTP server listener', {
|
|
261
|
+
useViteExpress: isDev,
|
|
262
|
+
environment: process.env.NODE_ENV
|
|
263
|
+
});
|
|
264
|
+
// Import ViteExpress dynamically if in development mode
|
|
265
|
+
if (isDev && !ViteExpress) {
|
|
266
|
+
const viteExpressModule = await import('vite-express');
|
|
267
|
+
ViteExpress = viteExpressModule.default;
|
|
268
|
+
}
|
|
269
|
+
await new Promise((resolve, reject) => {
|
|
270
|
+
// Use ViteExpress only in development
|
|
271
|
+
if (isDev && ViteExpress) {
|
|
272
|
+
try {
|
|
273
|
+
this.server = this.app.listen(this.port, this.host, () => {
|
|
274
|
+
this.logger.debug('Server successfully bound to port (dev mode)', {
|
|
275
|
+
port: this.port,
|
|
276
|
+
host: this.host,
|
|
277
|
+
address: this.server?.address()
|
|
278
|
+
});
|
|
279
|
+
// Display startup info now that server is actually listening
|
|
280
|
+
const config = this.configService.getConfig();
|
|
281
|
+
displayServerStartup({
|
|
282
|
+
host: this.host,
|
|
283
|
+
port: this.port,
|
|
284
|
+
authToken: this.configOverrides?.token ?? config.authToken,
|
|
285
|
+
skipAuthToken: this.configOverrides?.skipAuthToken,
|
|
286
|
+
logger: this.logger
|
|
287
|
+
});
|
|
288
|
+
// Configure and bind ViteExpress AFTER server is listening
|
|
289
|
+
ViteExpress.config({
|
|
290
|
+
mode: 'development',
|
|
291
|
+
viteConfigFile: 'vite.config.mts'
|
|
292
|
+
});
|
|
293
|
+
ViteExpress.bind(this.app, this.server);
|
|
294
|
+
this.logger.info(`CUI development server running on http://${this.host}:${this.port}`);
|
|
295
|
+
resolve();
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
this.logger.error('Failed to start ViteExpress server', error);
|
|
300
|
+
reject(error);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// Production/test mode - regular Express server
|
|
305
|
+
this.server = this.app.listen(this.port, this.host, () => {
|
|
306
|
+
this.logger.debug('Server successfully bound to port', {
|
|
307
|
+
port: this.port,
|
|
308
|
+
host: this.host,
|
|
309
|
+
address: this.server?.address(),
|
|
310
|
+
mode: process.env.NODE_ENV || 'production'
|
|
311
|
+
});
|
|
312
|
+
// Display startup info now that server is actually listening
|
|
313
|
+
const config = this.configService.getConfig();
|
|
314
|
+
displayServerStartup({
|
|
315
|
+
host: this.host,
|
|
316
|
+
port: this.port,
|
|
317
|
+
authToken: this.configOverrides?.token ?? config.authToken,
|
|
318
|
+
skipAuthToken: this.configOverrides?.skipAuthToken,
|
|
319
|
+
logger: this.logger
|
|
320
|
+
});
|
|
321
|
+
resolve();
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
if (this.server) {
|
|
325
|
+
this.server.on('error', (error) => {
|
|
326
|
+
this.logger.error('Failed to start HTTP server:', error, {
|
|
327
|
+
errorCode: error.code,
|
|
328
|
+
errorSyscall: error.syscall,
|
|
329
|
+
port: this.port,
|
|
330
|
+
host: this.host
|
|
331
|
+
});
|
|
332
|
+
reject(new CUIError('HTTP_SERVER_START_FAILED', `Failed to start HTTP server: ${error.message}`, 500));
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
this.logger.error('Failed to start server:', error, {
|
|
339
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
340
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
341
|
+
});
|
|
342
|
+
// Attempt cleanup on startup failure
|
|
343
|
+
await this.cleanup();
|
|
344
|
+
if (error instanceof CUIError) {
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
throw new CUIError('SERVER_START_FAILED', `Server startup failed: ${error}`, 500);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Stop the server gracefully
|
|
354
|
+
*/
|
|
355
|
+
async stop() {
|
|
356
|
+
this.logger.debug('Stop method called', {
|
|
357
|
+
hasServer: !!this.server,
|
|
358
|
+
activeSessions: this.processManager.getActiveSessions().length,
|
|
359
|
+
connectedClients: this.streamManager.getTotalClientCount()
|
|
360
|
+
});
|
|
361
|
+
if (this.routerService) {
|
|
362
|
+
await this.routerService.stop();
|
|
363
|
+
}
|
|
364
|
+
// Stop all active Claude processes (only in direct mode - daemon keeps them alive)
|
|
365
|
+
if (this.processManager.mode === 'direct') {
|
|
366
|
+
const activeSessions = this.processManager.getActiveSessions();
|
|
367
|
+
if (activeSessions.length > 0) {
|
|
368
|
+
this.logger.info(`Stopping ${activeSessions.length} active sessions...`);
|
|
369
|
+
this.logger.debug('Active sessions to stop', { sessionIds: activeSessions });
|
|
370
|
+
const stopResults = await Promise.allSettled(activeSessions.map(streamingId => this.processManager.stopConversation(streamingId)
|
|
371
|
+
.catch(error => this.logger.error(`Error stopping session ${streamingId}:`, error))));
|
|
372
|
+
this.logger.debug('Session stop results', {
|
|
373
|
+
total: stopResults.length,
|
|
374
|
+
fulfilled: stopResults.filter(r => r.status === 'fulfilled').length,
|
|
375
|
+
rejected: stopResults.filter(r => r.status === 'rejected').length
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
this.logger.info('Daemon mode: keeping Claude processes alive through restart');
|
|
381
|
+
}
|
|
382
|
+
// Disconnect from daemon to stop reconnection loop
|
|
383
|
+
this.processManager.disconnect();
|
|
384
|
+
// Disconnect all streaming clients (sends close events and ends responses)
|
|
385
|
+
this.logger.debug('Disconnecting all streaming clients');
|
|
386
|
+
this.streamManager.disconnectAll();
|
|
387
|
+
// Clean up MCP config
|
|
388
|
+
this.logger.debug('Cleaning up MCP config');
|
|
389
|
+
this.mcpConfigGenerator.cleanup();
|
|
390
|
+
// Close HTTP server to release port
|
|
391
|
+
if (this.server) {
|
|
392
|
+
this.logger.debug('Closing HTTP server');
|
|
393
|
+
// Force close all connections after ending SSE streams
|
|
394
|
+
// This ensures we don't wait for clients that haven't acknowledged the close
|
|
395
|
+
if (typeof this.server.closeAllConnections === 'function') {
|
|
396
|
+
this.server.closeAllConnections();
|
|
397
|
+
}
|
|
398
|
+
// Close with timeout - don't block shutdown forever
|
|
399
|
+
await new Promise((resolve) => {
|
|
400
|
+
const timeout = setTimeout(() => {
|
|
401
|
+
this.logger.warn('HTTP server close timed out after 2s, forcing shutdown');
|
|
402
|
+
resolve();
|
|
403
|
+
}, 2000);
|
|
404
|
+
this.server.close(() => {
|
|
405
|
+
clearTimeout(timeout);
|
|
406
|
+
this.logger.info('HTTP server closed successfully');
|
|
407
|
+
resolve();
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Cleanup resources during failed startup
|
|
414
|
+
*/
|
|
415
|
+
async cleanup() {
|
|
416
|
+
this.logger.info('Performing cleanup after startup failure...');
|
|
417
|
+
this.logger.debug('Cleanup initiated', {
|
|
418
|
+
hasServer: !!this.server,
|
|
419
|
+
hasActiveStreams: this.streamManager.getTotalClientCount() > 0
|
|
420
|
+
});
|
|
421
|
+
try {
|
|
422
|
+
// Close HTTP server if it was started
|
|
423
|
+
if (this.server) {
|
|
424
|
+
await new Promise((resolve) => {
|
|
425
|
+
this.server.close(() => {
|
|
426
|
+
this.logger.info('HTTP server closed during cleanup');
|
|
427
|
+
resolve();
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
// Disconnect streaming clients
|
|
432
|
+
this.streamManager.disconnectAll();
|
|
433
|
+
this.logger.info('Cleanup completed');
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
this.logger.error('Error during cleanup:', error, {
|
|
437
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
setupMiddleware() {
|
|
442
|
+
this.app.use(createCorsMiddleware());
|
|
443
|
+
this.app.use(express.json({ limit: '10mb' }));
|
|
444
|
+
// Static file serving
|
|
445
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
446
|
+
if (!isDev) {
|
|
447
|
+
// In production/test, serve built static files
|
|
448
|
+
// In production, __dirname will be /path/to/node_modules/cui-server/dist
|
|
449
|
+
// We need to serve from dist/web
|
|
450
|
+
const staticPath = path.join(__dirname, 'web');
|
|
451
|
+
this.logger.debug('Serving static files from', { path: staticPath });
|
|
452
|
+
this.app.use(express.static(staticPath));
|
|
453
|
+
}
|
|
454
|
+
// In development, ViteExpress handles static file serving
|
|
455
|
+
// Request logging
|
|
456
|
+
this.app.use(requestLogger);
|
|
457
|
+
// Query parameter parsing - convert strings to proper types
|
|
458
|
+
this.app.use(queryParser);
|
|
459
|
+
}
|
|
460
|
+
setupRoutes() {
|
|
461
|
+
// System routes (includes health check) - before auth
|
|
462
|
+
this.app.use('/api/system', createSystemRoutes(this.processManager, this.historyReader));
|
|
463
|
+
this.app.use('/', createSystemRoutes(this.processManager, this.historyReader)); // For /health at root
|
|
464
|
+
// Permission routes - before auth (needed for MCP server communication)
|
|
465
|
+
this.app.use('/api/permissions', createPermissionRoutes(this.permissionTracker));
|
|
466
|
+
// Notifications routes - before auth (needed for service worker subscription on first load)
|
|
467
|
+
this.app.use('/api/notifications', createNotificationsRoutes(this.webPushService));
|
|
468
|
+
// Apply auth middleware to all other API routes unless skipAuthToken is set
|
|
469
|
+
if (!this.configOverrides?.skipAuthToken) {
|
|
470
|
+
if (this.configOverrides?.token) {
|
|
471
|
+
// Use custom auth middleware with token override
|
|
472
|
+
this.app.use('/api', createAuthMiddleware(this.configOverrides.token));
|
|
473
|
+
this.logger.info('Using custom authentication token from CLI');
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
// Use default auth middleware
|
|
477
|
+
this.app.use('/api', authMiddleware);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
this.logger.warn('Authentication middleware is disabled - API endpoints are not protected!');
|
|
482
|
+
}
|
|
483
|
+
// API routes
|
|
484
|
+
// IMPORTANT: Insights routes must be registered BEFORE conversation routes
|
|
485
|
+
// because conversation routes have a /:sessionId catch-all pattern that would
|
|
486
|
+
// otherwise match paths like /activity-check
|
|
487
|
+
this.app.use('/api/conversations', createInsightsRoutes(this.historyReader, this.sessionInfoService));
|
|
488
|
+
this.app.use('/api/conversations', createConversationRoutes(this.processManager, this.historyReader, this.statusTracker, this.sessionInfoService, this.conversationStatusManager, this.toolMetricsService));
|
|
489
|
+
this.app.use('/api/filesystem', createFileSystemRoutes(this.fileSystemService));
|
|
490
|
+
this.app.use('/api/logs', createLogRoutes());
|
|
491
|
+
this.app.use('/api/stream', createStreamingRoutes(this.streamManager));
|
|
492
|
+
this.app.use('/api/working-directories', createWorkingDirectoriesRoutes(this.workingDirectoriesService));
|
|
493
|
+
this.app.use('/api/config', createConfigRoutes(this.configService));
|
|
494
|
+
this.app.use('/api/gemini', createGeminiRoutes(geminiService));
|
|
495
|
+
this.app.use('/api/questions', createQuestionRoutes(this.questionTracker, this.processManager));
|
|
496
|
+
// Claudia orchestrator routes
|
|
497
|
+
const claudiaService = ClaudiaService.getInstance({
|
|
498
|
+
processManager: this.processManager,
|
|
499
|
+
historyReader: this.historyReader,
|
|
500
|
+
statusManager: this.conversationStatusManager
|
|
501
|
+
});
|
|
502
|
+
this.app.use('/api/claudia', createClaudiaRoutes(claudiaService));
|
|
503
|
+
// License routes
|
|
504
|
+
this.app.use('/api/license', createLicenseRoutes());
|
|
505
|
+
// React Router catch-all - must be after all API routes
|
|
506
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
507
|
+
if (!isDev) {
|
|
508
|
+
// In production/test, serve index.html for all non-API routes
|
|
509
|
+
this.app.get('*', (req, res) => {
|
|
510
|
+
res.sendFile(path.join(__dirname, 'web', 'index.html'));
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
// In development, ViteExpress handles React routing
|
|
514
|
+
// Error handling - MUST be last
|
|
515
|
+
this.app.use(errorHandler);
|
|
516
|
+
}
|
|
517
|
+
setupProcessManagerIntegration() {
|
|
518
|
+
this.logger.debug('Setting up ProcessManager integration with StreamManager');
|
|
519
|
+
// Set up tool metrics service to listen to claude messages
|
|
520
|
+
this.toolMetricsService.listenToClaudeMessages(this.processManager);
|
|
521
|
+
// Forward Claude messages to stream
|
|
522
|
+
this.processManager.on('claude-message', ({ streamingId, message }) => {
|
|
523
|
+
this.logger.debug('Received claude-message event', {
|
|
524
|
+
streamingId,
|
|
525
|
+
messageType: message?.type,
|
|
526
|
+
messageSubtype: message?.subtype,
|
|
527
|
+
hasContent: !!message?.content,
|
|
528
|
+
contentLength: message?.content?.length || 0,
|
|
529
|
+
messageKeys: message ? Object.keys(message) : []
|
|
530
|
+
});
|
|
531
|
+
// Skip broadcasting system init messages as they're now included in API response
|
|
532
|
+
if (message && message.type === 'system' && message.subtype === 'init') {
|
|
533
|
+
this.logger.debug('Skipping broadcast of system init message (included in API response)', {
|
|
534
|
+
streamingId,
|
|
535
|
+
sessionId: message.session_id
|
|
536
|
+
});
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
// Detect AskUserQuestion tool_use and register with QuestionTracker
|
|
540
|
+
if (message && message.type === 'assistant' && message.message?.content) {
|
|
541
|
+
const content = message.message.content;
|
|
542
|
+
if (Array.isArray(content)) {
|
|
543
|
+
for (const block of content) {
|
|
544
|
+
if (block.type === 'tool_use' && block.name === 'AskUserQuestion') {
|
|
545
|
+
const input = block.input;
|
|
546
|
+
if (input.questions) {
|
|
547
|
+
this.questionTracker.addQuestionRequest(block.id, input.questions, streamingId);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
// Stream other Claude messages as normal
|
|
554
|
+
this.logger.debug('Broadcasting message to StreamManager', {
|
|
555
|
+
streamingId,
|
|
556
|
+
messageType: message?.type,
|
|
557
|
+
messageSubtype: message?.subtype
|
|
558
|
+
});
|
|
559
|
+
this.streamManager.broadcast(streamingId, message);
|
|
560
|
+
});
|
|
561
|
+
// Handle process closure
|
|
562
|
+
this.processManager.on('process-closed', ({ streamingId, code }) => {
|
|
563
|
+
this.logger.debug('Received process-closed event, closing StreamManager session', {
|
|
564
|
+
streamingId,
|
|
565
|
+
exitCode: code,
|
|
566
|
+
clientCount: this.streamManager.getClientCount(streamingId),
|
|
567
|
+
wasSuccessful: code === 0
|
|
568
|
+
});
|
|
569
|
+
// Unregister session from status tracker
|
|
570
|
+
this.logger.debug('Unregistering session from status tracker', { streamingId });
|
|
571
|
+
this.statusTracker.unregisterActiveSession(streamingId);
|
|
572
|
+
// Clean up conversation context (handled automatically in unregisterActiveSession)
|
|
573
|
+
// Clean up permissions for this streaming session
|
|
574
|
+
const removedCount = this.permissionTracker.removePermissionsByStreamingId(streamingId);
|
|
575
|
+
if (removedCount > 0) {
|
|
576
|
+
this.logger.debug('Cleaned up permissions for closed session', {
|
|
577
|
+
streamingId,
|
|
578
|
+
removedPermissions: removedCount
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
// Clean up questions for this streaming session
|
|
582
|
+
const removedQuestions = this.questionTracker.removeQuestionsByStreamingId(streamingId);
|
|
583
|
+
if (removedQuestions > 0) {
|
|
584
|
+
this.logger.debug('Cleaned up questions for closed session', {
|
|
585
|
+
streamingId,
|
|
586
|
+
removedQuestions
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
if (code === 0) {
|
|
590
|
+
// Session completion notification removed
|
|
591
|
+
}
|
|
592
|
+
this.streamManager.closeSession(streamingId);
|
|
593
|
+
});
|
|
594
|
+
// Handle process errors
|
|
595
|
+
this.processManager.on('process-error', ({ streamingId, error }) => {
|
|
596
|
+
this.logger.debug('Received process-error event, forwarding to StreamManager', {
|
|
597
|
+
streamingId,
|
|
598
|
+
error,
|
|
599
|
+
errorLength: error?.toString().length || 0,
|
|
600
|
+
clientCount: this.streamManager.getClientCount(streamingId)
|
|
601
|
+
});
|
|
602
|
+
// Unregister session from status tracker on error
|
|
603
|
+
this.logger.debug('Unregistering session from status tracker due to error', { streamingId });
|
|
604
|
+
this.statusTracker.unregisterActiveSession(streamingId);
|
|
605
|
+
// Clean up conversation context on error (handled automatically in unregisterActiveSession)
|
|
606
|
+
const errorEvent = {
|
|
607
|
+
type: 'error',
|
|
608
|
+
error: error.toString(),
|
|
609
|
+
streamingId: streamingId,
|
|
610
|
+
timestamp: new Date().toISOString()
|
|
611
|
+
};
|
|
612
|
+
this.logger.debug('Broadcasting error event to clients', {
|
|
613
|
+
streamingId,
|
|
614
|
+
errorEventKeys: Object.keys(errorEvent)
|
|
615
|
+
});
|
|
616
|
+
this.streamManager.broadcast(streamingId, errorEvent);
|
|
617
|
+
});
|
|
618
|
+
this.logger.debug('ProcessManager integration setup complete', {
|
|
619
|
+
totalEventListeners: this.processManager.listenerCount('claude-message') +
|
|
620
|
+
this.processManager.listenerCount('process-closed') +
|
|
621
|
+
this.processManager.listenerCount('process-error')
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
setupPermissionTrackerIntegration() {
|
|
625
|
+
this.logger.debug('Setting up PermissionTracker integration');
|
|
626
|
+
// Forward permission events to stream
|
|
627
|
+
this.permissionTracker.on('permission_request', (request) => {
|
|
628
|
+
this.logger.debug('Permission request event received', {
|
|
629
|
+
id: request.id,
|
|
630
|
+
toolName: request.toolName,
|
|
631
|
+
streamingId: request.streamingId
|
|
632
|
+
});
|
|
633
|
+
// Broadcast to the appropriate streaming session
|
|
634
|
+
if (request.streamingId && request.streamingId !== 'unknown') {
|
|
635
|
+
const event = {
|
|
636
|
+
type: 'permission_request',
|
|
637
|
+
data: request,
|
|
638
|
+
streamingId: request.streamingId,
|
|
639
|
+
timestamp: new Date().toISOString()
|
|
640
|
+
};
|
|
641
|
+
this.streamManager.broadcast(request.streamingId, event);
|
|
642
|
+
// Permission request notification removed
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
this.logger.debug('PermissionTracker integration setup complete');
|
|
646
|
+
}
|
|
647
|
+
setupQuestionTrackerIntegration() {
|
|
648
|
+
this.logger.debug('Setting up QuestionTracker integration');
|
|
649
|
+
// Forward question events to stream
|
|
650
|
+
this.questionTracker.on('question_request', (request) => {
|
|
651
|
+
this.logger.debug('Question request event received', {
|
|
652
|
+
id: request.id,
|
|
653
|
+
toolUseId: request.toolUseId,
|
|
654
|
+
streamingId: request.streamingId,
|
|
655
|
+
questionCount: request.questions.length
|
|
656
|
+
});
|
|
657
|
+
// Broadcast to the appropriate streaming session
|
|
658
|
+
if (request.streamingId && request.streamingId !== 'unknown') {
|
|
659
|
+
const event = {
|
|
660
|
+
type: 'question_request',
|
|
661
|
+
data: request,
|
|
662
|
+
streamingId: request.streamingId,
|
|
663
|
+
timestamp: new Date().toISOString()
|
|
664
|
+
};
|
|
665
|
+
this.streamManager.broadcast(request.streamingId, event);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
this.logger.debug('QuestionTracker integration setup complete');
|
|
669
|
+
}
|
|
670
|
+
async initializeOrReloadRouter(config) {
|
|
671
|
+
// If router is disabled, ensure it is stopped
|
|
672
|
+
if (!config.router?.enabled) {
|
|
673
|
+
if (this.routerService) {
|
|
674
|
+
this.logger.info('Router disabled in configuration, stopping router service');
|
|
675
|
+
await this.routerService.stop();
|
|
676
|
+
this.routerService = undefined;
|
|
677
|
+
this.processManager.setRouterService(undefined);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
this.logger.info('Router service is disabled');
|
|
681
|
+
}
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
// If router is enabled
|
|
685
|
+
try {
|
|
686
|
+
// If there is an existing router, stop it first
|
|
687
|
+
if (this.routerService) {
|
|
688
|
+
this.logger.info('Reloading router service due to configuration change...');
|
|
689
|
+
await this.routerService.stop();
|
|
690
|
+
this.routerService = undefined;
|
|
691
|
+
}
|
|
692
|
+
else {
|
|
693
|
+
this.logger.debug('Router service is enabled, attempting to initialize...');
|
|
694
|
+
}
|
|
695
|
+
this.routerService = new ClaudeRouterService(config.router);
|
|
696
|
+
await this.routerService.initialize();
|
|
697
|
+
this.processManager.setRouterService(this.routerService);
|
|
698
|
+
this.logger.info('Router service initialized');
|
|
699
|
+
}
|
|
700
|
+
catch (error) {
|
|
701
|
+
this.logger.error('Router initialization failed, continuing without router', error);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
//# sourceMappingURL=cui-server.js.map
|