codex-linux 1.0.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/.claude/settings.local.json +10 -0
- package/.eslintrc.json +27 -0
- package/.github/workflows/ci.yml +156 -0
- package/.huskyrc +7 -0
- package/.lintstagedrc +13 -0
- package/.prettierrc +12 -0
- package/CLAUDE.md +163 -0
- package/DESIGN_SUPERIOR.md +73 -0
- package/Dockerfile +64 -0
- package/INSTALLATION.md +152 -0
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/assets/skills/code-review/instructions.md +102 -0
- package/assets/skills/code-review/skill.yaml +15 -0
- package/assets/skills/refactoring/instructions.md +149 -0
- package/assets/skills/refactoring/skill.yaml +15 -0
- package/assets/skills/testing/skill.yaml +15 -0
- package/commitlint.config.js +23 -0
- package/dist/main/DatabaseManager.js +763 -0
- package/dist/main/DatabaseManager.js.map +1 -0
- package/dist/main/SettingsManager.js +61 -0
- package/dist/main/SettingsManager.js.map +1 -0
- package/dist/main/agents/AgentOrchestrator.js +787 -0
- package/dist/main/agents/AgentOrchestrator.js.map +1 -0
- package/dist/main/agents/AgentSDK.js +219 -0
- package/dist/main/agents/AgentSDK.js.map +1 -0
- package/dist/main/agents/AgentTools.js +348 -0
- package/dist/main/agents/AgentTools.js.map +1 -0
- package/dist/main/agents/CodeIndex.js +233 -0
- package/dist/main/agents/CodeIndex.js.map +1 -0
- package/dist/main/agents/EmbeddingService.js +80 -0
- package/dist/main/agents/EmbeddingService.js.map +1 -0
- package/dist/main/agents/NativeToolCalling.js +206 -0
- package/dist/main/agents/NativeToolCalling.js.map +1 -0
- package/dist/main/api/APIServer.js +278 -0
- package/dist/main/api/APIServer.js.map +1 -0
- package/dist/main/api/RateLimiter.js +138 -0
- package/dist/main/api/RateLimiter.js.map +1 -0
- package/dist/main/api/WebSocketManager.js +300 -0
- package/dist/main/api/WebSocketManager.js.map +1 -0
- package/dist/main/assistant/ContextOptimizer.js +192 -0
- package/dist/main/assistant/ContextOptimizer.js.map +1 -0
- package/dist/main/assistant/PredictedOutputManager.js +172 -0
- package/dist/main/assistant/PredictedOutputManager.js.map +1 -0
- package/dist/main/assistant/PromptCacheManager.js +193 -0
- package/dist/main/assistant/PromptCacheManager.js.map +1 -0
- package/dist/main/assistant/PromptOptimizer.js +626 -0
- package/dist/main/assistant/PromptOptimizer.js.map +1 -0
- package/dist/main/assistant/SmartCodeAssistant.js +224 -0
- package/dist/main/assistant/SmartCodeAssistant.js.map +1 -0
- package/dist/main/auth/SessionManager.js +300 -0
- package/dist/main/auth/SessionManager.js.map +1 -0
- package/dist/main/automations/AdvancedWebhookSystem.js +212 -0
- package/dist/main/automations/AdvancedWebhookSystem.js.map +1 -0
- package/dist/main/automations/AutomationScheduler.js +269 -0
- package/dist/main/automations/AutomationScheduler.js.map +1 -0
- package/dist/main/automations/BatchProcessingSystem.js +159 -0
- package/dist/main/automations/BatchProcessingSystem.js.map +1 -0
- package/dist/main/automations/BrowserAutomationManager.js +195 -0
- package/dist/main/automations/BrowserAutomationManager.js.map +1 -0
- package/dist/main/automations/GitHubActionsManager.js +129 -0
- package/dist/main/automations/GitHubActionsManager.js.map +1 -0
- package/dist/main/automations/GitLabCIManager.js +122 -0
- package/dist/main/automations/GitLabCIManager.js.map +1 -0
- package/dist/main/automations/PriorityQueueManager.js +240 -0
- package/dist/main/automations/PriorityQueueManager.js.map +1 -0
- package/dist/main/background/BackgroundModeManager.js +117 -0
- package/dist/main/background/BackgroundModeManager.js.map +1 -0
- package/dist/main/backup/BackupManager.js +254 -0
- package/dist/main/backup/BackupManager.js.map +1 -0
- package/dist/main/backup/MigrationManager.js +114 -0
- package/dist/main/backup/MigrationManager.js.map +1 -0
- package/dist/main/commands/SlashCommandManager.js +399 -0
- package/dist/main/commands/SlashCommandManager.js.map +1 -0
- package/dist/main/config/ClaudeMdParser.js +519 -0
- package/dist/main/config/ClaudeMdParser.js.map +1 -0
- package/dist/main/config/CustomizationManager.js +381 -0
- package/dist/main/config/CustomizationManager.js.map +1 -0
- package/dist/main/config/LaunchConfigManager.js +211 -0
- package/dist/main/config/LaunchConfigManager.js.map +1 -0
- package/dist/main/config/SettingsManager.js +166 -0
- package/dist/main/config/SettingsManager.js.map +1 -0
- package/dist/main/connectors/ConnectorManager.js +151 -0
- package/dist/main/connectors/ConnectorManager.js.map +1 -0
- package/dist/main/connectors/DatabaseConnector.js +222 -0
- package/dist/main/connectors/DatabaseConnector.js.map +1 -0
- package/dist/main/cowork/CoworkManager.js +324 -0
- package/dist/main/cowork/CoworkManager.js.map +1 -0
- package/dist/main/evals/AgentEvalFramework.js +538 -0
- package/dist/main/evals/AgentEvalFramework.js.map +1 -0
- package/dist/main/evals/GraderManager.js +285 -0
- package/dist/main/evals/GraderManager.js.map +1 -0
- package/dist/main/git/GitWorktreeManager.js +214 -0
- package/dist/main/git/GitWorktreeManager.js.map +1 -0
- package/dist/main/github/GitHubPRMonitor.js +244 -0
- package/dist/main/github/GitHubPRMonitor.js.map +1 -0
- package/dist/main/ide/ContinueInManager.js +181 -0
- package/dist/main/ide/ContinueInManager.js.map +1 -0
- package/dist/main/ide/IDEIntegration.js +277 -0
- package/dist/main/ide/IDEIntegration.js.map +1 -0
- package/dist/main/integrations/LinearManager.js +252 -0
- package/dist/main/integrations/LinearManager.js.map +1 -0
- package/dist/main/integrations/SlackBotManager.js +247 -0
- package/dist/main/integrations/SlackBotManager.js.map +1 -0
- package/dist/main/lsp/LSPManager.js +394 -0
- package/dist/main/lsp/LSPManager.js.map +1 -0
- package/dist/main/main.js +1087 -0
- package/dist/main/main.js.map +1 -0
- package/dist/main/mcp/MCPConfigurationManager.js +281 -0
- package/dist/main/mcp/MCPConfigurationManager.js.map +1 -0
- package/dist/main/mcp/MCPManager.js +710 -0
- package/dist/main/mcp/MCPManager.js.map +1 -0
- package/dist/main/mcp/MCPRegistry.js +272 -0
- package/dist/main/mcp/MCPRegistry.js.map +1 -0
- package/dist/main/monitoring/ErrorRecoveryManager.js +268 -0
- package/dist/main/monitoring/ErrorRecoveryManager.js.map +1 -0
- package/dist/main/monitoring/ErrorTracker.js +57 -0
- package/dist/main/monitoring/ErrorTracker.js.map +1 -0
- package/dist/main/monitoring/MetricsCollector.js +155 -0
- package/dist/main/monitoring/MetricsCollector.js.map +1 -0
- package/dist/main/monitoring/TraceGradingSystem.js +148 -0
- package/dist/main/monitoring/TraceGradingSystem.js.map +1 -0
- package/dist/main/notifications/NotificationManager.js +67 -0
- package/dist/main/notifications/NotificationManager.js.map +1 -0
- package/dist/main/pair/AIPairProgramming.js +200 -0
- package/dist/main/pair/AIPairProgramming.js.map +1 -0
- package/dist/main/plugins/PluginManager.js +222 -0
- package/dist/main/plugins/PluginManager.js.map +1 -0
- package/dist/main/plugins/PluginMarketplace.js +237 -0
- package/dist/main/plugins/PluginMarketplace.js.map +1 -0
- package/dist/main/preload.js +189 -0
- package/dist/main/preload.js.map +1 -0
- package/dist/main/preview/PreviewSessionManager.js +170 -0
- package/dist/main/preview/PreviewSessionManager.js.map +1 -0
- package/dist/main/providers/AIProviderManager.js +327 -0
- package/dist/main/providers/AIProviderManager.js.map +1 -0
- package/dist/main/providers/FineTuningManager.js +276 -0
- package/dist/main/providers/FineTuningManager.js.map +1 -0
- package/dist/main/providers/FreeModelsProvider.js +1104 -0
- package/dist/main/providers/FreeModelsProvider.js.map +1 -0
- package/dist/main/realtime/RealtimeManager.js +116 -0
- package/dist/main/realtime/RealtimeManager.js.map +1 -0
- package/dist/main/remote/CloudEnvironmentManager.js +232 -0
- package/dist/main/remote/CloudEnvironmentManager.js.map +1 -0
- package/dist/main/remote/RemoteSessionManager.js +255 -0
- package/dist/main/remote/RemoteSessionManager.js.map +1 -0
- package/dist/main/search/DeepResearchManager.js +335 -0
- package/dist/main/search/DeepResearchManager.js.map +1 -0
- package/dist/main/search/WebSearchIntegration.js +147 -0
- package/dist/main/search/WebSearchIntegration.js.map +1 -0
- package/dist/main/security/AdminConsoleManager.js +223 -0
- package/dist/main/security/AdminConsoleManager.js.map +1 -0
- package/dist/main/security/AuditLogger.js +136 -0
- package/dist/main/security/AuditLogger.js.map +1 -0
- package/dist/main/security/PermissionManager.js +144 -0
- package/dist/main/security/PermissionManager.js.map +1 -0
- package/dist/main/security/SSOManager.js +173 -0
- package/dist/main/security/SSOManager.js.map +1 -0
- package/dist/main/security/SecurityManager.js +152 -0
- package/dist/main/security/SecurityManager.js.map +1 -0
- package/dist/main/skills/SkillsManager.js +223 -0
- package/dist/main/skills/SkillsManager.js.map +1 -0
- package/dist/main/ssh/SSHManager.js +65 -0
- package/dist/main/ssh/SSHManager.js.map +1 -0
- package/dist/main/streaming/StreamingManager.js +225 -0
- package/dist/main/streaming/StreamingManager.js.map +1 -0
- package/dist/main/sync/CloudSyncManager.js +422 -0
- package/dist/main/sync/CloudSyncManager.js.map +1 -0
- package/dist/main/types.js +28 -0
- package/dist/main/types.js.map +1 -0
- package/dist/main/verification/AutoVerifyManager.js +235 -0
- package/dist/main/verification/AutoVerifyManager.js.map +1 -0
- package/dist/main/vision/ComputerUseManager.js +376 -0
- package/dist/main/vision/ComputerUseManager.js.map +1 -0
- package/dist/main/vision/ImageVideoGenerationManager.js +401 -0
- package/dist/main/vision/ImageVideoGenerationManager.js.map +1 -0
- package/dist/main/vision/VisionManager.js +172 -0
- package/dist/main/vision/VisionManager.js.map +1 -0
- package/dist/renderer/assets/main-DJlZQBCA.js +304 -0
- package/dist/renderer/assets/main-N33ZXEr8.css +1 -0
- package/dist/renderer/index.html +21 -0
- package/dist/renderer/manifest.json +42 -0
- package/dist/renderer/sw.ts +109 -0
- package/dist/shared/types.js +35 -0
- package/dist/shared/types.js.map +1 -0
- package/docker-compose.yml +65 -0
- package/docs/API.md +307 -0
- package/docs/USER_GUIDE.md +476 -0
- package/examples/plugins/sample-plugin/package.json +41 -0
- package/examples/plugins/sample-plugin/src/index.ts +75 -0
- package/index.html +20 -0
- package/jest.config.js +39 -0
- package/package.json +180 -0
- package/packages/cli/package.json +29 -0
- package/packages/cli/src/commands/agents.ts +199 -0
- package/packages/cli/src/commands/tasks.ts +61 -0
- package/packages/cli/src/index.ts +91 -0
- package/packages/cli/src/utils/api.ts +45 -0
- package/packages/cli/src/utils/config.ts +61 -0
- package/packages/npm-installer/bin/codex-linux +126 -0
- package/packages/npm-installer/lib/download.js +273 -0
- package/packages/npm-installer/package.json +42 -0
- package/packages/vscode-extension/package.json +167 -0
- package/packages/vscode-extension/src/api.ts +68 -0
- package/packages/vscode-extension/src/extension.ts +161 -0
- package/packages/vscode-extension/src/panels/chatPanel.ts +265 -0
- package/packages/vscode-extension/src/panels/createAgentPanel.ts +227 -0
- package/packages/vscode-extension/src/providers/agentsProvider.ts +80 -0
- package/postcss.config.js +6 -0
- package/public/manifest.json +42 -0
- package/public/sw.ts +109 -0
- package/scripts/install-dev.sh +103 -0
- package/scripts/install.sh +275 -0
- package/src/main/DatabaseManager.ts +950 -0
- package/src/main/SettingsManager.ts +63 -0
- package/src/main/agents/AgentOrchestrator.ts +930 -0
- package/src/main/agents/AgentSDK.ts +269 -0
- package/src/main/agents/AgentTools.ts +380 -0
- package/src/main/agents/CodeIndex.ts +240 -0
- package/src/main/agents/EmbeddingService.ts +88 -0
- package/src/main/agents/NativeToolCalling.ts +245 -0
- package/src/main/api/APIServer.ts +316 -0
- package/src/main/api/RateLimiter.ts +165 -0
- package/src/main/api/WebSocketManager.ts +398 -0
- package/src/main/assistant/ContextOptimizer.ts +214 -0
- package/src/main/assistant/PredictedOutputManager.ts +265 -0
- package/src/main/assistant/PromptCacheManager.ts +280 -0
- package/src/main/assistant/PromptOptimizer.ts +746 -0
- package/src/main/assistant/SmartCodeAssistant.ts +234 -0
- package/src/main/auth/SessionManager.ts +415 -0
- package/src/main/automations/AdvancedWebhookSystem.ts +281 -0
- package/src/main/automations/AutomationScheduler.ts +272 -0
- package/src/main/automations/BatchProcessingSystem.ts +207 -0
- package/src/main/automations/BrowserAutomationManager.ts +203 -0
- package/src/main/automations/GitHubActionsManager.ts +151 -0
- package/src/main/automations/GitLabCIManager.ts +206 -0
- package/src/main/automations/PriorityQueueManager.ts +328 -0
- package/src/main/background/BackgroundModeManager.ts +130 -0
- package/src/main/backup/BackupManager.ts +287 -0
- package/src/main/backup/MigrationManager.ts +132 -0
- package/src/main/commands/SlashCommandManager.ts +407 -0
- package/src/main/config/ClaudeMdParser.ts +539 -0
- package/src/main/config/CustomizationManager.ts +493 -0
- package/src/main/config/LaunchConfigManager.ts +212 -0
- package/src/main/config/SettingsManager.ts +163 -0
- package/src/main/connectors/ConnectorManager.ts +175 -0
- package/src/main/connectors/DatabaseConnector.ts +212 -0
- package/src/main/cowork/CoworkManager.ts +431 -0
- package/src/main/evals/AgentEvalFramework.ts +665 -0
- package/src/main/evals/GraderManager.ts +417 -0
- package/src/main/git/GitWorktreeManager.ts +211 -0
- package/src/main/github/GitHubPRMonitor.ts +317 -0
- package/src/main/ide/ContinueInManager.ts +180 -0
- package/src/main/ide/IDEIntegration.ts +288 -0
- package/src/main/integrations/LinearManager.ts +327 -0
- package/src/main/integrations/SlackBotManager.ts +312 -0
- package/src/main/lsp/LSPManager.ts +445 -0
- package/src/main/main.ts +1221 -0
- package/src/main/mcp/MCPConfigurationManager.ts +281 -0
- package/src/main/mcp/MCPManager.ts +799 -0
- package/src/main/mcp/MCPRegistry.ts +273 -0
- package/src/main/monitoring/ErrorRecoveryManager.ts +359 -0
- package/src/main/monitoring/ErrorTracker.ts +60 -0
- package/src/main/monitoring/MetricsCollector.ts +196 -0
- package/src/main/monitoring/TraceGradingSystem.ts +196 -0
- package/src/main/notifications/NotificationManager.ts +96 -0
- package/src/main/pair/AIPairProgramming.ts +290 -0
- package/src/main/plugins/PluginManager.ts +266 -0
- package/src/main/plugins/PluginMarketplace.ts +318 -0
- package/src/main/preload.ts +215 -0
- package/src/main/preview/PreviewSessionManager.ts +186 -0
- package/src/main/providers/AIProviderManager.ts +394 -0
- package/src/main/providers/FineTuningManager.ts +390 -0
- package/src/main/providers/FreeModelsProvider.ts +1156 -0
- package/src/main/realtime/RealtimeManager.ts +147 -0
- package/src/main/remote/CloudEnvironmentManager.ts +253 -0
- package/src/main/remote/RemoteSessionManager.ts +323 -0
- package/src/main/search/DeepResearchManager.ts +458 -0
- package/src/main/search/WebSearchIntegration.ts +203 -0
- package/src/main/security/AdminConsoleManager.ts +244 -0
- package/src/main/security/AuditLogger.ts +143 -0
- package/src/main/security/PermissionManager.ts +184 -0
- package/src/main/security/SSOManager.ts +241 -0
- package/src/main/security/SecurityManager.ts +139 -0
- package/src/main/skills/SkillsManager.ts +218 -0
- package/src/main/ssh/SSHManager.ts +86 -0
- package/src/main/streaming/StreamingManager.ts +306 -0
- package/src/main/sync/CloudSyncManager.ts +532 -0
- package/src/main/verification/AutoVerifyManager.ts +285 -0
- package/src/main/vision/ComputerUseManager.ts +475 -0
- package/src/main/vision/ImageVideoGenerationManager.ts +526 -0
- package/src/main/vision/VisionManager.ts +186 -0
- package/src/renderer/App.tsx +314 -0
- package/src/renderer/components/AdvancedSettingsPanel.tsx +225 -0
- package/src/renderer/components/AgentPanel.tsx +760 -0
- package/src/renderer/components/AppPreview.tsx +220 -0
- package/src/renderer/components/AuditTrailPanel.tsx +148 -0
- package/src/renderer/components/AutomationPanel.tsx +220 -0
- package/src/renderer/components/ChatInterface.tsx +595 -0
- package/src/renderer/components/ChatTab.tsx +296 -0
- package/src/renderer/components/CodeEditor.tsx +257 -0
- package/src/renderer/components/CodeReviewPanel.tsx +256 -0
- package/src/renderer/components/CodeWorkspace.tsx +192 -0
- package/src/renderer/components/CodebaseDashboard.tsx +295 -0
- package/src/renderer/components/ComputerUsePanel.tsx +262 -0
- package/src/renderer/components/ConnectorsPanel.tsx +471 -0
- package/src/renderer/components/ContextMenu.tsx +155 -0
- package/src/renderer/components/ContextUsageDisplay.tsx +248 -0
- package/src/renderer/components/CoworkPanel.tsx +415 -0
- package/src/renderer/components/DiffViewer.tsx +452 -0
- package/src/renderer/components/ErrorBoundary.tsx +273 -0
- package/src/renderer/components/ExtendedThinkingToggle.tsx +244 -0
- package/src/renderer/components/FileAttachments.tsx +247 -0
- package/src/renderer/components/FileExplorer.tsx +242 -0
- package/src/renderer/components/FileExplorerPanel.tsx +302 -0
- package/src/renderer/components/GitPanel.tsx +154 -0
- package/src/renderer/components/Header.tsx +113 -0
- package/src/renderer/components/MCPPanel.tsx +326 -0
- package/src/renderer/components/MentionAutocomplete.tsx +239 -0
- package/src/renderer/components/PermissionPanel.tsx +159 -0
- package/src/renderer/components/PermissionSelector.tsx +203 -0
- package/src/renderer/components/PluginMarketplace.tsx +325 -0
- package/src/renderer/components/PromptOptimizerPanel.tsx +399 -0
- package/src/renderer/components/SearchPanel.tsx +173 -0
- package/src/renderer/components/SearchReplace.tsx +284 -0
- package/src/renderer/components/SessionSidebar.tsx +367 -0
- package/src/renderer/components/SettingsPanel.tsx +426 -0
- package/src/renderer/components/Sidebar.tsx +100 -0
- package/src/renderer/components/SkillsPanel.tsx +245 -0
- package/src/renderer/components/SplitPane.tsx +173 -0
- package/src/renderer/components/Terminal.tsx +190 -0
- package/src/renderer/components/VoiceCommand.tsx +129 -0
- package/src/renderer/components/WorktreePanel.tsx +163 -0
- package/src/renderer/components/ui/AriaComponents.tsx +193 -0
- package/src/renderer/components/ui/Button.tsx +68 -0
- package/src/renderer/components/ui/Card.tsx +102 -0
- package/src/renderer/components/ui/Input.tsx +44 -0
- package/src/renderer/components/ui/Skeleton.tsx +55 -0
- package/src/renderer/components/ui/VirtualList.tsx +196 -0
- package/src/renderer/i18n/I18nProvider.tsx +101 -0
- package/src/renderer/i18n/de.ts +161 -0
- package/src/renderer/i18n/en.ts +163 -0
- package/src/renderer/i18n/es.ts +161 -0
- package/src/renderer/i18n/fr.ts +161 -0
- package/src/renderer/i18n/index.ts +44 -0
- package/src/renderer/index.css +129 -0
- package/src/renderer/lib/accessibility.tsx +287 -0
- package/src/renderer/lib/hooks.ts +304 -0
- package/src/renderer/lib/utils.ts +6 -0
- package/src/renderer/main.tsx +25 -0
- package/src/renderer/styles/minimalist.css +539 -0
- package/src/renderer/sw.ts +180 -0
- package/src/renderer/types.d.ts +138 -0
- package/src/shared/types.ts +813 -0
- package/supabase/schema.sql +234 -0
- package/tailwind.config.js +78 -0
- package/tests/e2e/package.json +15 -0
- package/tests/e2e/playwright.config.ts +31 -0
- package/tests/e2e/specs/app.spec.ts +194 -0
- package/tests/setup.ts +99 -0
- package/tests/unit/AgentOrchestrator.test.ts +274 -0
- package/tests/unit/DatabaseManager.test.ts +262 -0
- package/tests/unit/GitWorktreeManager.test.ts +150 -0
- package/tests/unit/SecurityManager.test.ts +110 -0
- package/tsconfig.main.json +22 -0
- package/tsconfig.renderer.json +27 -0
- package/vite.config.ts +28 -0
package/src/main/main.ts
ADDED
|
@@ -0,0 +1,1221 @@
|
|
|
1
|
+
import { app, BrowserWindow, ipcMain, dialog, shell, Notification, Tray, Menu, globalShortcut, nativeImage } from 'electron';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import { spawn, ChildProcess } from 'child_process';
|
|
5
|
+
import log from 'electron-log';
|
|
6
|
+
import Store from 'electron-store';
|
|
7
|
+
import { autoUpdater } from 'electron-updater';
|
|
8
|
+
import { AgentOrchestrator } from './agents/AgentOrchestrator';
|
|
9
|
+
import { GitWorktreeManager } from './git/GitWorktreeManager';
|
|
10
|
+
import { SkillsManager } from './skills/SkillsManager';
|
|
11
|
+
import { AutomationScheduler } from './automations/AutomationScheduler';
|
|
12
|
+
import { DatabaseManager } from './DatabaseManager';
|
|
13
|
+
import { SettingsManager } from './SettingsManager';
|
|
14
|
+
import { AIProviderManager } from './providers/AIProviderManager';
|
|
15
|
+
import { SecurityManager } from './security/SecurityManager';
|
|
16
|
+
import { AuditLogger } from './security/AuditLogger';
|
|
17
|
+
import { BackupManager } from './backup/BackupManager';
|
|
18
|
+
import { MigrationManager } from './backup/MigrationManager';
|
|
19
|
+
import { APIServer } from './api/APIServer';
|
|
20
|
+
import { PluginManager } from './plugins/PluginManager';
|
|
21
|
+
import { CoworkManager } from './cowork/CoworkManager';
|
|
22
|
+
import { GitHubPRMonitor } from './github/GitHubPRMonitor';
|
|
23
|
+
import { MCPManager } from './mcp/MCPManager';
|
|
24
|
+
import { AIPairProgramming } from './pair/AIPairProgramming';
|
|
25
|
+
import { ErrorTracker } from './monitoring/ErrorTracker';
|
|
26
|
+
import { metrics, startSystemMetrics } from './monitoring/MetricsCollector';
|
|
27
|
+
import { NotificationManager } from './notifications/NotificationManager';
|
|
28
|
+
import { SmartCodeAssistant } from './assistant/SmartCodeAssistant';
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
|
|
31
|
+
// Validation schemas
|
|
32
|
+
const AgentConfigSchema = z.object({
|
|
33
|
+
name: z.string().min(1).max(100),
|
|
34
|
+
projectPath: z.string().min(1),
|
|
35
|
+
providerId: z.string(),
|
|
36
|
+
model: z.string(),
|
|
37
|
+
skills: z.array(z.string()).optional(),
|
|
38
|
+
systemPrompt: z.string().optional(),
|
|
39
|
+
}).strict();
|
|
40
|
+
|
|
41
|
+
const store = new Store();
|
|
42
|
+
let mainWindow: BrowserWindow | null = null;
|
|
43
|
+
let agentOrchestrator: AgentOrchestrator;
|
|
44
|
+
let gitWorktreeManager: GitWorktreeManager;
|
|
45
|
+
let skillsManager: SkillsManager;
|
|
46
|
+
let automationScheduler: AutomationScheduler;
|
|
47
|
+
let dbManager: DatabaseManager;
|
|
48
|
+
let settingsManager: SettingsManager;
|
|
49
|
+
let aiProviderManager: AIProviderManager;
|
|
50
|
+
let securityManager: SecurityManager;
|
|
51
|
+
let auditLogger: AuditLogger;
|
|
52
|
+
let backupManager: BackupManager;
|
|
53
|
+
let migrationManager: MigrationManager;
|
|
54
|
+
let apiServer: APIServer;
|
|
55
|
+
let pluginManager: PluginManager;
|
|
56
|
+
let coworkManager: CoworkManager;
|
|
57
|
+
let githubPRMonitor: GitHubPRMonitor;
|
|
58
|
+
let mcpManager: MCPManager;
|
|
59
|
+
let aiPairProgramming: AIPairProgramming;
|
|
60
|
+
let errorTracker: ErrorTracker;
|
|
61
|
+
let notificationManager: NotificationManager;
|
|
62
|
+
let smartCodeAssistant: SmartCodeAssistant;
|
|
63
|
+
|
|
64
|
+
log.info('Starting Codex Linux...');
|
|
65
|
+
|
|
66
|
+
function createWindow(): void {
|
|
67
|
+
mainWindow = new BrowserWindow({
|
|
68
|
+
width: 1600,
|
|
69
|
+
height: 1000,
|
|
70
|
+
minWidth: 1200,
|
|
71
|
+
minHeight: 800,
|
|
72
|
+
titleBarStyle: 'hiddenInset',
|
|
73
|
+
webPreferences: {
|
|
74
|
+
nodeIntegration: false,
|
|
75
|
+
contextIsolation: true,
|
|
76
|
+
preload: path.join(__dirname, 'preload.js'),
|
|
77
|
+
sandbox: false
|
|
78
|
+
},
|
|
79
|
+
show: false,
|
|
80
|
+
icon: path.join(__dirname, '../../assets/icon.png')
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (process.env.VITE_DEV_SERVER_URL) {
|
|
84
|
+
mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL);
|
|
85
|
+
mainWindow.webContents.openDevTools();
|
|
86
|
+
} else {
|
|
87
|
+
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
mainWindow.once('ready-to-show', () => {
|
|
91
|
+
mainWindow?.show();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
mainWindow.on('closed', () => {
|
|
95
|
+
mainWindow = null;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let tray: Tray | null = null;
|
|
100
|
+
|
|
101
|
+
function createTray(): void {
|
|
102
|
+
const iconPath = path.join(__dirname, '../../assets/icon.png');
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const icon = nativeImage.createFromPath(iconPath);
|
|
106
|
+
tray = new Tray(icon.isEmpty() ? nativeImage.createEmpty() : icon);
|
|
107
|
+
|
|
108
|
+
const contextMenu = Menu.buildFromTemplate([
|
|
109
|
+
{
|
|
110
|
+
label: 'Show Codex',
|
|
111
|
+
click: () => {
|
|
112
|
+
if (mainWindow) {
|
|
113
|
+
mainWindow.show();
|
|
114
|
+
mainWindow.focus();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{ type: 'separator' },
|
|
119
|
+
{
|
|
120
|
+
label: 'New Agent',
|
|
121
|
+
click: () => {
|
|
122
|
+
if (mainWindow) {
|
|
123
|
+
mainWindow.show();
|
|
124
|
+
mainWindow.webContents.send('create-new-agent');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
label: 'Open Settings',
|
|
130
|
+
click: () => {
|
|
131
|
+
if (mainWindow) {
|
|
132
|
+
mainWindow.show();
|
|
133
|
+
mainWindow.webContents.send('open-settings');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{ type: 'separator' },
|
|
138
|
+
{
|
|
139
|
+
label: 'Quit',
|
|
140
|
+
click: () => {
|
|
141
|
+
app.quit();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
tray.setToolTip('Codex Linux');
|
|
147
|
+
tray.setContextMenu(contextMenu);
|
|
148
|
+
|
|
149
|
+
tray.on('click', () => {
|
|
150
|
+
if (mainWindow) {
|
|
151
|
+
if (mainWindow.isVisible()) {
|
|
152
|
+
mainWindow.hide();
|
|
153
|
+
} else {
|
|
154
|
+
mainWindow.show();
|
|
155
|
+
mainWindow.focus();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
log.info('System tray initialized');
|
|
161
|
+
} catch (error) {
|
|
162
|
+
log.warn('Failed to create system tray:', error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function registerGlobalShortcuts(): void {
|
|
167
|
+
try {
|
|
168
|
+
globalShortcut.register('CommandOrControl+Shift+C', () => {
|
|
169
|
+
if (mainWindow) {
|
|
170
|
+
if (mainWindow.isVisible()) {
|
|
171
|
+
mainWindow.hide();
|
|
172
|
+
} else {
|
|
173
|
+
mainWindow.show();
|
|
174
|
+
mainWindow.focus();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
globalShortcut.register('CommandOrControl+Shift+N', () => {
|
|
180
|
+
if (mainWindow) {
|
|
181
|
+
mainWindow.show();
|
|
182
|
+
mainWindow.webContents.send('create-new-agent');
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
globalShortcut.register('CommandOrControl+Shift+K', () => {
|
|
187
|
+
if (mainWindow) {
|
|
188
|
+
mainWindow.show();
|
|
189
|
+
mainWindow.webContents.send('open-search');
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
log.info('Global shortcuts registered');
|
|
194
|
+
} catch (error) {
|
|
195
|
+
log.warn('Failed to register global shortcuts:', error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function initializeServices(): Promise<void> {
|
|
200
|
+
try {
|
|
201
|
+
// Initialize security first
|
|
202
|
+
securityManager = new SecurityManager();
|
|
203
|
+
await securityManager.initialize();
|
|
204
|
+
log.info('Security manager initialized');
|
|
205
|
+
|
|
206
|
+
// Initialize database
|
|
207
|
+
dbManager = new DatabaseManager();
|
|
208
|
+
await dbManager.initialize();
|
|
209
|
+
log.info('Database initialized');
|
|
210
|
+
|
|
211
|
+
// Run migrations
|
|
212
|
+
migrationManager = new MigrationManager(dbManager);
|
|
213
|
+
await migrationManager.initialize();
|
|
214
|
+
log.info('Migrations completed');
|
|
215
|
+
|
|
216
|
+
// Initialize settings
|
|
217
|
+
settingsManager = new SettingsManager(store);
|
|
218
|
+
log.info('Settings manager initialized');
|
|
219
|
+
|
|
220
|
+
// Initialize AI provider manager
|
|
221
|
+
aiProviderManager = new AIProviderManager(settingsManager);
|
|
222
|
+
log.info('AI provider manager initialized');
|
|
223
|
+
|
|
224
|
+
// Initialize notification manager
|
|
225
|
+
notificationManager = new NotificationManager();
|
|
226
|
+
log.info('Notification manager initialized');
|
|
227
|
+
|
|
228
|
+
// Initialize Git worktree manager
|
|
229
|
+
gitWorktreeManager = new GitWorktreeManager();
|
|
230
|
+
log.info('Git worktree manager initialized');
|
|
231
|
+
|
|
232
|
+
// Initialize skills manager
|
|
233
|
+
skillsManager = new SkillsManager();
|
|
234
|
+
await skillsManager.initialize();
|
|
235
|
+
log.info('Skills manager initialized');
|
|
236
|
+
|
|
237
|
+
// Initialize automation scheduler
|
|
238
|
+
automationScheduler = new AutomationScheduler();
|
|
239
|
+
automationScheduler.setNotificationManager(notificationManager);
|
|
240
|
+
await automationScheduler.initialize();
|
|
241
|
+
log.info('Automation scheduler initialized');
|
|
242
|
+
|
|
243
|
+
// Initialize agent orchestrator
|
|
244
|
+
agentOrchestrator = new AgentOrchestrator(
|
|
245
|
+
aiProviderManager,
|
|
246
|
+
gitWorktreeManager,
|
|
247
|
+
skillsManager,
|
|
248
|
+
dbManager
|
|
249
|
+
);
|
|
250
|
+
await agentOrchestrator.initialize();
|
|
251
|
+
log.info('Agent orchestrator initialized');
|
|
252
|
+
|
|
253
|
+
agentOrchestrator.on('changes:created', (payload: { agentId: string; changeId: string }) => {
|
|
254
|
+
if (mainWindow) {
|
|
255
|
+
mainWindow.webContents.send('changes:created', payload);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
agentOrchestrator.on('agent:taskCompleted', (payload: any) => {
|
|
260
|
+
if (mainWindow) {
|
|
261
|
+
mainWindow.webContents.send('agent:taskCompleted', payload);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
agentOrchestrator.on('agent:taskStarted', (payload: any) => {
|
|
266
|
+
if (mainWindow) {
|
|
267
|
+
mainWindow.webContents.send('agent:taskStarted', payload);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
agentOrchestrator.on('agent:taskFailed', (payload: any) => {
|
|
272
|
+
if (mainWindow) {
|
|
273
|
+
mainWindow.webContents.send('agent:taskFailed', payload);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
agentOrchestrator.on('agent:paused', (payload: any) => {
|
|
278
|
+
if (mainWindow) {
|
|
279
|
+
mainWindow.webContents.send('agent:paused', payload);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
agentOrchestrator.on('agent:resumed', (payload: any) => {
|
|
284
|
+
if (mainWindow) {
|
|
285
|
+
mainWindow.webContents.send('agent:resumed', payload);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
agentOrchestrator.on('agent:stopped', (payload: any) => {
|
|
290
|
+
if (mainWindow) {
|
|
291
|
+
mainWindow.webContents.send('agent:stopped', payload);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Initialize backup manager
|
|
296
|
+
backupManager = new BackupManager();
|
|
297
|
+
await backupManager.initialize();
|
|
298
|
+
log.info('Backup manager initialized');
|
|
299
|
+
|
|
300
|
+
// Initialize plugin manager
|
|
301
|
+
pluginManager = new PluginManager();
|
|
302
|
+
await pluginManager.initialize();
|
|
303
|
+
log.info('Plugin manager initialized');
|
|
304
|
+
|
|
305
|
+
// Initialize cowork manager
|
|
306
|
+
coworkManager = new CoworkManager(
|
|
307
|
+
agentOrchestrator,
|
|
308
|
+
dbManager,
|
|
309
|
+
notificationManager
|
|
310
|
+
);
|
|
311
|
+
await coworkManager.initialize();
|
|
312
|
+
log.info('Cowork manager initialized');
|
|
313
|
+
|
|
314
|
+
// Initialize AI pair programming
|
|
315
|
+
aiPairProgramming = new AIPairProgramming(
|
|
316
|
+
agentOrchestrator,
|
|
317
|
+
aiProviderManager
|
|
318
|
+
);
|
|
319
|
+
log.info('AI pair programming initialized');
|
|
320
|
+
|
|
321
|
+
// Initialize smart code assistant
|
|
322
|
+
smartCodeAssistant = new SmartCodeAssistant(aiProviderManager);
|
|
323
|
+
log.info('Smart code assistant initialized');
|
|
324
|
+
|
|
325
|
+
// Initialize MCP manager
|
|
326
|
+
mcpManager = new MCPManager();
|
|
327
|
+
await mcpManager.initialize();
|
|
328
|
+
log.info('MCP manager initialized');
|
|
329
|
+
|
|
330
|
+
// Initialize audit logger
|
|
331
|
+
auditLogger = new AuditLogger();
|
|
332
|
+
await auditLogger.initialize();
|
|
333
|
+
log.info('Audit logger initialized');
|
|
334
|
+
|
|
335
|
+
const sentryDsn = settingsManager.get('sentryDsn');
|
|
336
|
+
if (sentryDsn) {
|
|
337
|
+
errorTracker = new ErrorTracker();
|
|
338
|
+
errorTracker.initialize(sentryDsn);
|
|
339
|
+
log.info('Error tracker initialized');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Start system metrics
|
|
343
|
+
startSystemMetrics();
|
|
344
|
+
log.info('System metrics started');
|
|
345
|
+
|
|
346
|
+
// Initialize API server
|
|
347
|
+
apiServer = new APIServer(
|
|
348
|
+
agentOrchestrator,
|
|
349
|
+
securityManager,
|
|
350
|
+
auditLogger
|
|
351
|
+
);
|
|
352
|
+
await apiServer.start();
|
|
353
|
+
log.info('API server started on port 3001');
|
|
354
|
+
|
|
355
|
+
// Initialize GitHub PR Monitor if token available
|
|
356
|
+
const githubToken = settingsManager.get('githubToken');
|
|
357
|
+
if (githubToken) {
|
|
358
|
+
githubPRMonitor = new GitHubPRMonitor(
|
|
359
|
+
githubToken,
|
|
360
|
+
agentOrchestrator,
|
|
361
|
+
gitWorktreeManager,
|
|
362
|
+
notificationManager
|
|
363
|
+
);
|
|
364
|
+
await githubPRMonitor.initialize();
|
|
365
|
+
log.info('GitHub PR monitor initialized');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
} catch (error) {
|
|
369
|
+
log.error('Failed to initialize services:', error);
|
|
370
|
+
throw error;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// App event handlers
|
|
375
|
+
app.whenReady().then(async () => {
|
|
376
|
+
try {
|
|
377
|
+
await initializeServices();
|
|
378
|
+
createWindow();
|
|
379
|
+
createTray();
|
|
380
|
+
registerGlobalShortcuts();
|
|
381
|
+
setupIPC();
|
|
382
|
+
setupAutoUpdater();
|
|
383
|
+
setupAutoUpdaterIPC();
|
|
384
|
+
|
|
385
|
+
app.on('activate', () => {
|
|
386
|
+
if (BrowserWindow.getAllWindows().length === 0) {
|
|
387
|
+
createWindow();
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
} catch (error) {
|
|
391
|
+
log.error('Failed to start application:', error);
|
|
392
|
+
dialog.showErrorBox(
|
|
393
|
+
'Startup Error',
|
|
394
|
+
'Failed to initialize Codex Linux. Please check the logs.'
|
|
395
|
+
);
|
|
396
|
+
app.quit();
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
app.on('window-all-closed', () => {
|
|
401
|
+
if (process.platform !== 'darwin') {
|
|
402
|
+
app.quit();
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
app.on('before-quit', async () => {
|
|
407
|
+
await cleanup();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
function setupAutoUpdater(): void {
|
|
411
|
+
if (app.isPackaged) {
|
|
412
|
+
autoUpdater.logger = log;
|
|
413
|
+
autoUpdater.autoDownload = true;
|
|
414
|
+
autoUpdater.autoInstallOnAppQuit = true;
|
|
415
|
+
|
|
416
|
+
autoUpdater.on('checking-for-update', () => {
|
|
417
|
+
log.info('Checking for updates...');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
autoUpdater.on('update-available', (info) => {
|
|
421
|
+
log.info('Update available:', info.version);
|
|
422
|
+
if (mainWindow) {
|
|
423
|
+
mainWindow.webContents.send('update-available', info);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
autoUpdater.on('update-not-available', () => {
|
|
428
|
+
log.info('No updates available');
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
autoUpdater.on('download-progress', (progress) => {
|
|
432
|
+
log.info(`Download progress: ${progress.percent}%`);
|
|
433
|
+
if (mainWindow) {
|
|
434
|
+
mainWindow.webContents.send('update-progress', progress);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
autoUpdater.on('update-downloaded', (info) => {
|
|
439
|
+
log.info('Update downloaded:', info.version);
|
|
440
|
+
if (mainWindow) {
|
|
441
|
+
mainWindow.webContents.send('update-downloaded', info);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (Notification.isSupported()) {
|
|
445
|
+
new Notification({
|
|
446
|
+
title: 'Update Ready',
|
|
447
|
+
body: `Codex Linux ${info.version} is ready to install. Restart to update.`
|
|
448
|
+
}).show();
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
autoUpdater.on('error', (error) => {
|
|
453
|
+
log.error('Auto-updater error:', error);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
autoUpdater.checkForUpdatesAndNotify().catch(err => {
|
|
457
|
+
log.warn('Failed to check for updates:', err);
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function setupAutoUpdaterIPC(): void {
|
|
463
|
+
ipcMain.handle('update:check', async () => {
|
|
464
|
+
if (!app.isPackaged) {
|
|
465
|
+
return { available: false, message: 'Updates only work in packaged app' };
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
const result = await autoUpdater.checkForUpdates();
|
|
469
|
+
return { available: !!result?.updateInfo, version: result?.updateInfo?.version };
|
|
470
|
+
} catch (error) {
|
|
471
|
+
log.error('Update check failed:', error);
|
|
472
|
+
return { available: false, message: 'Update check failed' };
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
ipcMain.handle('update:download', async () => {
|
|
477
|
+
if (!app.isPackaged) {
|
|
478
|
+
return { success: false, message: 'Updates only work in packaged app' };
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
await autoUpdater.downloadUpdate();
|
|
482
|
+
return { success: true };
|
|
483
|
+
} catch (error) {
|
|
484
|
+
log.error('Update download failed:', error);
|
|
485
|
+
return { success: false, message: 'Update download failed' };
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
ipcMain.handle('update:install', () => {
|
|
490
|
+
autoUpdater.quitAndInstall(false, true);
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function cleanup(): Promise<void> {
|
|
495
|
+
log.info('Starting cleanup...');
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
globalShortcut.unregisterAll();
|
|
499
|
+
tray?.destroy();
|
|
500
|
+
tray = null;
|
|
501
|
+
|
|
502
|
+
if (agentOrchestrator) {
|
|
503
|
+
agentOrchestrator.removeAllListeners('changes:created');
|
|
504
|
+
agentOrchestrator.removeAllListeners('agent:taskCompleted');
|
|
505
|
+
agentOrchestrator.removeAllListeners('agent:taskStarted');
|
|
506
|
+
agentOrchestrator.removeAllListeners('agent:taskFailed');
|
|
507
|
+
agentOrchestrator.removeAllListeners('agent:paused');
|
|
508
|
+
agentOrchestrator.removeAllListeners('agent:resumed');
|
|
509
|
+
agentOrchestrator.removeAllListeners('agent:stopped');
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
await Promise.all([
|
|
513
|
+
agentOrchestrator?.cleanup(),
|
|
514
|
+
automationScheduler?.cleanup(),
|
|
515
|
+
coworkManager?.cleanup(),
|
|
516
|
+
apiServer?.stop(),
|
|
517
|
+
pluginManager?.cleanup(),
|
|
518
|
+
mcpManager?.cleanup(),
|
|
519
|
+
githubPRMonitor?.cleanup(),
|
|
520
|
+
backupManager?.cleanup?.(),
|
|
521
|
+
auditLogger?.cleanup?.(),
|
|
522
|
+
dbManager?.close()
|
|
523
|
+
]);
|
|
524
|
+
|
|
525
|
+
log.info('Cleanup completed');
|
|
526
|
+
} catch (error) {
|
|
527
|
+
log.error('Error during cleanup:', error);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function setupIPC(): void {
|
|
532
|
+
// Window controls
|
|
533
|
+
ipcMain.handle('window:minimize', () => {
|
|
534
|
+
mainWindow?.minimize();
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
ipcMain.handle('window:maximize', () => {
|
|
538
|
+
if (mainWindow?.isMaximized()) {
|
|
539
|
+
mainWindow.unmaximize();
|
|
540
|
+
} else {
|
|
541
|
+
mainWindow?.maximize();
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
ipcMain.handle('window:close', () => {
|
|
546
|
+
mainWindow?.close();
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
// File system operations
|
|
550
|
+
ipcMain.handle('dialog:selectFolder', async () => {
|
|
551
|
+
if (!mainWindow) {
|
|
552
|
+
throw new Error('No main window available');
|
|
553
|
+
}
|
|
554
|
+
const result = await dialog.showOpenDialog(mainWindow, {
|
|
555
|
+
properties: ['openDirectory']
|
|
556
|
+
});
|
|
557
|
+
return result.filePaths[0] || null;
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
ipcMain.handle('dialog:selectFile', async (event, filters) => {
|
|
561
|
+
if (!mainWindow) {
|
|
562
|
+
throw new Error('No main window available');
|
|
563
|
+
}
|
|
564
|
+
const result = await dialog.showOpenDialog(mainWindow, {
|
|
565
|
+
properties: ['openFile'],
|
|
566
|
+
filters
|
|
567
|
+
});
|
|
568
|
+
return result.filePaths[0] || null;
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
ipcMain.handle('shell:openExternal', (event, url: string) => {
|
|
572
|
+
shell.openExternal(url);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
ipcMain.handle('shell:openPath', (event, path: string) => {
|
|
576
|
+
shell.openPath(path);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// Agent operations with validation
|
|
580
|
+
ipcMain.handle('agent:create', async (event, config) => {
|
|
581
|
+
try {
|
|
582
|
+
const validatedConfig = AgentConfigSchema.parse(config);
|
|
583
|
+
const result = await agentOrchestrator.createAgent(validatedConfig as any);
|
|
584
|
+
await auditLogger.log('agent_created', { agentId: result.id });
|
|
585
|
+
return result;
|
|
586
|
+
} catch (error) {
|
|
587
|
+
log.error('Failed to create agent:', error);
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
ipcMain.handle('agent:list', async () => {
|
|
593
|
+
return await agentOrchestrator.listAgents();
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
ipcMain.handle('agent:get', async (event, agentId: string) => {
|
|
597
|
+
return await agentOrchestrator.getAgent(agentId);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
ipcMain.handle('agent:sendMessage', async (event, agentId: string, message: string) => {
|
|
601
|
+
try {
|
|
602
|
+
return await agentOrchestrator.sendMessage(agentId, message);
|
|
603
|
+
} catch (error) {
|
|
604
|
+
log.error('Failed to send message:', error);
|
|
605
|
+
throw error;
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
ipcMain.handle('agent:sendMessageStream', async (event, agentId: string, message: string) => {
|
|
610
|
+
try {
|
|
611
|
+
const stream = await agentOrchestrator.sendMessageStream(agentId, message, {
|
|
612
|
+
onChunk: (chunk: string) => {
|
|
613
|
+
event.sender.send('agent:streamChunk', { agentId, chunk });
|
|
614
|
+
},
|
|
615
|
+
onComplete: () => {
|
|
616
|
+
event.sender.send('agent:streamEnd', { agentId });
|
|
617
|
+
},
|
|
618
|
+
onError: (error: Error) => {
|
|
619
|
+
event.sender.send('agent:streamError', { agentId, error: error.message });
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
return { success: true };
|
|
624
|
+
} catch (error) {
|
|
625
|
+
log.error('Failed to start stream:', error);
|
|
626
|
+
throw error;
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
ipcMain.handle('agent:executeTask', async (event, agentId: string, task: string) => {
|
|
631
|
+
return await agentOrchestrator.executeTask(agentId, task);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
ipcMain.handle('agent:pause', async (event, agentId: string) => {
|
|
635
|
+
return await agentOrchestrator.pauseAgent(agentId);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
ipcMain.handle('agent:resume', async (event, agentId: string) => {
|
|
639
|
+
return await agentOrchestrator.resumeAgent(agentId);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
ipcMain.handle('agent:stop', async (event, agentId: string) => {
|
|
643
|
+
return await agentOrchestrator.stopAgent(agentId);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
ipcMain.handle('agent:delete', async (event, agentId: string) => {
|
|
647
|
+
try {
|
|
648
|
+
await agentOrchestrator.deleteAgent(agentId);
|
|
649
|
+
await auditLogger.log('agent_deleted', { agentId });
|
|
650
|
+
return { success: true };
|
|
651
|
+
} catch (error) {
|
|
652
|
+
log.error('Failed to delete agent:', error);
|
|
653
|
+
throw error;
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// Worktree operations
|
|
658
|
+
ipcMain.handle('worktree:create', async (event, repoPath: string, name: string) => {
|
|
659
|
+
return await gitWorktreeManager.createWorktree(repoPath, name);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
ipcMain.handle('worktree:list', async (event, repoPath: string) => {
|
|
663
|
+
return await gitWorktreeManager.listWorktrees(repoPath);
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
ipcMain.handle('worktree:remove', async (event, repoPath: string, name: string) => {
|
|
667
|
+
return await gitWorktreeManager.removeWorktree(repoPath, name);
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
// Skills operations
|
|
671
|
+
ipcMain.handle('skills:list', async () => {
|
|
672
|
+
return await skillsManager.listSkills();
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
ipcMain.handle('skills:get', async (event, skillId: string) => {
|
|
676
|
+
return await skillsManager.getSkill(skillId);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
ipcMain.handle('skills:create', async (event, skillConfig) => {
|
|
680
|
+
return await skillsManager.createSkill(skillConfig);
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
ipcMain.handle('skills:update', async (event, skillId: string, skillConfig) => {
|
|
684
|
+
return await skillsManager.updateSkill(skillId, skillConfig);
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
ipcMain.handle('skills:delete', async (event, skillId: string) => {
|
|
688
|
+
return await skillsManager.deleteSkill(skillId);
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
ipcMain.handle('skills:applyToAgent', async (event, agentId: string, skillIds: string[]) => {
|
|
692
|
+
return await agentOrchestrator.applySkills(agentId, skillIds);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// Automation operations
|
|
696
|
+
ipcMain.handle('automation:list', async () => {
|
|
697
|
+
return await automationScheduler.listAutomations();
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
ipcMain.handle('automation:create', async (event, config) => {
|
|
701
|
+
return await automationScheduler.createAutomation(config);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
ipcMain.handle('automation:update', async (event, automationId: string, config) => {
|
|
705
|
+
return await automationScheduler.updateAutomation(automationId, config);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
ipcMain.handle('automation:delete', async (event, automationId: string) => {
|
|
709
|
+
return await automationScheduler.deleteAutomation(automationId);
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
ipcMain.handle('automation:toggle', async (event, automationId: string, enabled: boolean) => {
|
|
713
|
+
return await automationScheduler.toggleAutomation(automationId, enabled);
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
// Settings operations
|
|
717
|
+
ipcMain.handle('settings:get', (event, key: string) => {
|
|
718
|
+
return settingsManager.getAny(key);
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
ipcMain.handle('settings:set', (event, key: string, value: any) => {
|
|
722
|
+
settingsManager.setAny(key, value);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
ipcMain.handle('settings:getAll', () => {
|
|
726
|
+
return settingsManager.getAll();
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
// AI Provider operations
|
|
730
|
+
ipcMain.handle('providers:list', () => {
|
|
731
|
+
return aiProviderManager.listProviders();
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
ipcMain.handle('providers:getActive', () => {
|
|
735
|
+
return aiProviderManager.getActiveProvider();
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
ipcMain.handle('providers:setActive', (event, providerId: string) => {
|
|
739
|
+
return aiProviderManager.setActiveProvider(providerId);
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
ipcMain.handle('providers:configure', (event, providerId: string, config: any) => {
|
|
743
|
+
return aiProviderManager.configureProvider(providerId, config);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
ipcMain.handle('providers:test', async (event, providerId: string) => {
|
|
747
|
+
return await aiProviderManager.testProvider(providerId);
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
// File system operations
|
|
751
|
+
ipcMain.handle('fs:readdir', async (event, dirPath: string, options?: { withFileTypes?: boolean }) => {
|
|
752
|
+
try {
|
|
753
|
+
const normalizedPath = path.normalize(dirPath);
|
|
754
|
+
if (normalizedPath.includes('..')) {
|
|
755
|
+
throw new Error('Path traversal not allowed');
|
|
756
|
+
}
|
|
757
|
+
const entries = await fs.readdir(normalizedPath, { withFileTypes: true });
|
|
758
|
+
return entries.map((entry: any) => ({
|
|
759
|
+
name: entry.name,
|
|
760
|
+
isDirectory: entry.isDirectory(),
|
|
761
|
+
isFile: entry.isFile()
|
|
762
|
+
}));
|
|
763
|
+
} catch (error) {
|
|
764
|
+
log.error('Failed to read directory:', error);
|
|
765
|
+
throw error;
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
ipcMain.handle('fs:readFile', async (event, filePath: string, encoding?: BufferEncoding) => {
|
|
770
|
+
try {
|
|
771
|
+
const normalizedPath = path.normalize(filePath);
|
|
772
|
+
if (normalizedPath.includes('..')) {
|
|
773
|
+
throw new Error('Path traversal not allowed');
|
|
774
|
+
}
|
|
775
|
+
const content = await fs.readFile(normalizedPath, encoding || 'utf-8');
|
|
776
|
+
return content;
|
|
777
|
+
} catch (error) {
|
|
778
|
+
log.error('Failed to read file:', error);
|
|
779
|
+
throw error;
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
ipcMain.handle('fs:writeFile', async (event, filePath: string, content: string) => {
|
|
784
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
785
|
+
throw new Error('File path is required');
|
|
786
|
+
}
|
|
787
|
+
const normalizedPath = path.normalize(filePath);
|
|
788
|
+
if (normalizedPath.includes('..') || normalizedPath.startsWith('/etc') || normalizedPath.startsWith('/root') || normalizedPath.startsWith('/sys') || normalizedPath.startsWith('/proc')) {
|
|
789
|
+
throw new Error('Path traversal or restricted path not allowed');
|
|
790
|
+
}
|
|
791
|
+
try {
|
|
792
|
+
await fs.writeFile(normalizedPath, content, 'utf-8');
|
|
793
|
+
} catch (error) {
|
|
794
|
+
log.error('Failed to write file:', error);
|
|
795
|
+
throw error;
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
ipcMain.handle('fs:stat', async (event, filePath: string) => {
|
|
800
|
+
try {
|
|
801
|
+
const normalizedPath = path.normalize(filePath);
|
|
802
|
+
if (normalizedPath.includes('..')) {
|
|
803
|
+
throw new Error('Path traversal not allowed');
|
|
804
|
+
}
|
|
805
|
+
const stats = await fs.stat(normalizedPath);
|
|
806
|
+
return {
|
|
807
|
+
isFile: stats.isFile(),
|
|
808
|
+
isDirectory: stats.isDirectory(),
|
|
809
|
+
size: stats.size,
|
|
810
|
+
mtime: stats.mtime,
|
|
811
|
+
ctime: stats.ctime
|
|
812
|
+
};
|
|
813
|
+
} catch (error) {
|
|
814
|
+
log.error('Failed to stat file:', error);
|
|
815
|
+
throw error;
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
// Terminal operations
|
|
820
|
+
const ALLOWED_COMMANDS = new Set([
|
|
821
|
+
'npm', 'node', 'pnpm', 'yarn', 'bun', 'deno',
|
|
822
|
+
'git', 'docker', 'docker-compose', 'kubectl',
|
|
823
|
+
'python', 'python3', 'pip', 'pip3',
|
|
824
|
+
'make', 'cmake', 'gcc', 'g++', 'clang', 'rustc',
|
|
825
|
+
'cargo', 'go', 'java', 'javac', 'gradle', 'maven',
|
|
826
|
+
'ls', 'cat', 'grep', 'find', 'chmod', 'chown', 'mkdir', 'rm', 'rmdir', 'cp', 'mv', 'touch',
|
|
827
|
+
'code', 'codex', 'opencode'
|
|
828
|
+
]);
|
|
829
|
+
const terminalProcesses = new Map<number, ChildProcess>();
|
|
830
|
+
let terminalIdCounter = 0;
|
|
831
|
+
|
|
832
|
+
function parseCommandLine(command: string): string[] {
|
|
833
|
+
const args: string[] = [];
|
|
834
|
+
let current = '';
|
|
835
|
+
let inQuote = false;
|
|
836
|
+
let quoteChar = '';
|
|
837
|
+
|
|
838
|
+
for (let i = 0; i < command.length; i++) {
|
|
839
|
+
const char = command[i];
|
|
840
|
+
if ((char === '"' || char === "'") && !inQuote) {
|
|
841
|
+
inQuote = true;
|
|
842
|
+
quoteChar = char;
|
|
843
|
+
} else if (char === quoteChar && inQuote) {
|
|
844
|
+
inQuote = false;
|
|
845
|
+
quoteChar = '';
|
|
846
|
+
} else if (char === ' ' && !inQuote) {
|
|
847
|
+
if (current) {
|
|
848
|
+
args.push(current);
|
|
849
|
+
current = '';
|
|
850
|
+
}
|
|
851
|
+
} else {
|
|
852
|
+
current += char;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (current) args.push(current);
|
|
856
|
+
return args;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
ipcMain.handle('terminal:execute', async (event, { command, cwd }: { command: string; cwd: string }) => {
|
|
860
|
+
if (!command || typeof command !== 'string') {
|
|
861
|
+
throw new Error('Command is required');
|
|
862
|
+
}
|
|
863
|
+
if (!cwd || typeof cwd !== 'string') {
|
|
864
|
+
throw new Error('CWD is required');
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const parsedArgs = parseCommandLine(command);
|
|
868
|
+
if (parsedArgs.length === 0) {
|
|
869
|
+
throw new Error('Command is required');
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
const cmd = parsedArgs[0];
|
|
873
|
+
const args = parsedArgs.slice(1);
|
|
874
|
+
|
|
875
|
+
if (!ALLOWED_COMMANDS.has(cmd)) {
|
|
876
|
+
throw new Error(`Command "${cmd}" is not allowed. Allowed commands: ${[...ALLOWED_COMMANDS].join(', ')}`);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const currentTerminalId = ++terminalIdCounter;
|
|
880
|
+
|
|
881
|
+
return new Promise((resolve) => {
|
|
882
|
+
const proc = spawn(cmd, args, {
|
|
883
|
+
cwd,
|
|
884
|
+
shell: false,
|
|
885
|
+
env: { ...process.env, FORCE_COLOR: '1' }
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
terminalProcesses.set(currentTerminalId, proc);
|
|
889
|
+
|
|
890
|
+
let stdout = '';
|
|
891
|
+
let stderr = '';
|
|
892
|
+
|
|
893
|
+
proc.stdout?.on('data', (data) => {
|
|
894
|
+
stdout += data.toString();
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
proc.stderr?.on('data', (data) => {
|
|
898
|
+
stderr += data.toString();
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
const cleanup = () => {
|
|
902
|
+
terminalProcesses.delete(currentTerminalId);
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
proc.on('close', (code) => {
|
|
906
|
+
cleanup();
|
|
907
|
+
resolve({
|
|
908
|
+
stdout,
|
|
909
|
+
stderr,
|
|
910
|
+
exitCode: code,
|
|
911
|
+
error: code !== 0 ? `Process exited with code ${code}` : null
|
|
912
|
+
});
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
proc.on('error', (error) => {
|
|
916
|
+
cleanup();
|
|
917
|
+
resolve({
|
|
918
|
+
stdout,
|
|
919
|
+
stderr,
|
|
920
|
+
exitCode: -1,
|
|
921
|
+
error: error.message
|
|
922
|
+
});
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
ipcMain.handle('terminal:kill', () => {
|
|
928
|
+
for (const [id, proc] of terminalProcesses) {
|
|
929
|
+
proc.kill();
|
|
930
|
+
terminalProcesses.delete(id);
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
// Code changes operations
|
|
935
|
+
ipcMain.handle('changes:list', async (event, agentId?: string) => {
|
|
936
|
+
return await dbManager.getCodeChanges(agentId);
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
ipcMain.handle('changes:approve', async (event, changeId: string) => {
|
|
940
|
+
return await dbManager.approveCodeChange(changeId);
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
ipcMain.handle('changes:reject', async (event, changeId: string, comment?: string) => {
|
|
944
|
+
return await dbManager.rejectCodeChange(changeId, comment);
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
ipcMain.handle('changes:apply', async (event, changeId: string) => {
|
|
948
|
+
return await dbManager.applyCodeChange(changeId);
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
// Checkpoints operations
|
|
952
|
+
ipcMain.handle('checkpoints:list', async (event, agentId?: string) => {
|
|
953
|
+
return await dbManager.listCheckpoints(agentId);
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
ipcMain.handle('checkpoints:restore', async (event, checkpointId: string) => {
|
|
957
|
+
return await dbManager.restoreCheckpoint(checkpointId);
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
ipcMain.handle('checkpoints:restoreLast', async (event, agentId: string) => {
|
|
961
|
+
return await dbManager.restoreLastCheckpoint(agentId);
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
// Agent queue operations
|
|
965
|
+
ipcMain.handle('queue:list', async (event, agentId: string) => {
|
|
966
|
+
return await dbManager.listAgentQueueItems(agentId);
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
ipcMain.handle('queue:enqueue', async (event, agentId: string, type: 'message' | 'task', content: string) => {
|
|
970
|
+
return await dbManager.enqueueAgentQueueItem(agentId, type, content);
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
ipcMain.handle('queue:delete', async (event, agentId: string, itemId: string) => {
|
|
974
|
+
return await dbManager.deleteAgentQueueItem(agentId, itemId);
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
ipcMain.handle('queue:moveUp', async (event, agentId: string, itemId: string) => {
|
|
978
|
+
return await dbManager.moveAgentQueueItemUp(agentId, itemId);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
ipcMain.handle('queue:claimNext', async (event, agentId: string) => {
|
|
982
|
+
return await dbManager.claimNextAgentQueueItem(agentId);
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
ipcMain.handle('queue:complete', async (event, agentId: string, itemId: string, outcome: 'completed' | 'failed', error?: string) => {
|
|
986
|
+
return await dbManager.completeAgentQueueItem(agentId, itemId, outcome, error);
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
ipcMain.handle('queue:history', async (event, agentId: string, limit?: number) => {
|
|
990
|
+
return await dbManager.getQueueHistory(agentId, limit);
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
// Git operations
|
|
994
|
+
ipcMain.handle('git:status', async (event, repoPath: string) => {
|
|
995
|
+
return await gitWorktreeManager.getChanges(repoPath);
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
ipcMain.handle('git:commit', async (event, { repoPath, message, files }: { repoPath: string; message: string; files?: string[] }) => {
|
|
999
|
+
return await gitWorktreeManager.commitChanges(repoPath, message, files);
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
ipcMain.handle('git:diff', async (event, { repoPath, filePath }: { repoPath: string; filePath?: string }) => {
|
|
1003
|
+
return await gitWorktreeManager.getDiff(repoPath, filePath);
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
// Search operations
|
|
1007
|
+
ipcMain.handle('search:files', async (event, { query, path, pattern }: { query: string; path: string; pattern?: string }) => {
|
|
1008
|
+
try {
|
|
1009
|
+
const results: Array<{ path: string; matches: Array<{ line: number; content: string }> }> = [];
|
|
1010
|
+
|
|
1011
|
+
async function searchDir(dirPath: string) {
|
|
1012
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
1013
|
+
|
|
1014
|
+
for (const entry of entries) {
|
|
1015
|
+
const fullPath = `${dirPath}/${entry.name}`;
|
|
1016
|
+
|
|
1017
|
+
if (entry.isDirectory()) {
|
|
1018
|
+
if (!entry.name.startsWith('.') &&
|
|
1019
|
+
entry.name !== 'node_modules' &&
|
|
1020
|
+
entry.name !== 'dist' &&
|
|
1021
|
+
entry.name !== 'build') {
|
|
1022
|
+
await searchDir(fullPath);
|
|
1023
|
+
}
|
|
1024
|
+
} else if (entry.isFile()) {
|
|
1025
|
+
if (pattern && !entry.name.match(pattern)) continue;
|
|
1026
|
+
|
|
1027
|
+
try {
|
|
1028
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
1029
|
+
const lines = content.split('\n');
|
|
1030
|
+
const matches: Array<{ line: number; content: string }> = [];
|
|
1031
|
+
|
|
1032
|
+
lines.forEach((line, index) => {
|
|
1033
|
+
if (line.toLowerCase().includes(query.toLowerCase())) {
|
|
1034
|
+
matches.push({ line: index + 1, content: line.trim() });
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
if (matches.length > 0) {
|
|
1039
|
+
results.push({ path: fullPath, matches });
|
|
1040
|
+
}
|
|
1041
|
+
} catch {
|
|
1042
|
+
// Skip binary or unreadable files
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
await searchDir(path);
|
|
1049
|
+
return results;
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
log.error('Search failed:', error);
|
|
1052
|
+
throw error;
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
// Audit log operations
|
|
1057
|
+
ipcMain.handle('audit:recent', async (event, limit: number = 100) => {
|
|
1058
|
+
return await auditLogger.getRecentEvents(limit);
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
ipcMain.handle('audit:byAction', async (event, action: string, limit: number = 100) => {
|
|
1062
|
+
return await auditLogger.getEventsByAction(action, limit);
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
ipcMain.handle('audit:export', async (event, exportPath: string) => {
|
|
1066
|
+
await auditLogger.exportLogs(exportPath);
|
|
1067
|
+
return { success: true };
|
|
1068
|
+
});
|
|
1069
|
+
ipcMain.handle('notification:show', (event, { title, body }: { title: string; body: string }) => {
|
|
1070
|
+
if (Notification.isSupported()) {
|
|
1071
|
+
new Notification({
|
|
1072
|
+
title,
|
|
1073
|
+
body,
|
|
1074
|
+
icon: path.join(__dirname, '../../assets/icon.png')
|
|
1075
|
+
}).show();
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
// Export/Import
|
|
1080
|
+
ipcMain.handle('data:export', async (event, exportPath: string) => {
|
|
1081
|
+
try {
|
|
1082
|
+
const data = {
|
|
1083
|
+
agents: await dbManager.getAllAgents(),
|
|
1084
|
+
automations: await automationScheduler.listAutomations(),
|
|
1085
|
+
skills: await skillsManager.listSkills(),
|
|
1086
|
+
settings: settingsManager.getAll(),
|
|
1087
|
+
exportedAt: new Date().toISOString()
|
|
1088
|
+
};
|
|
1089
|
+
|
|
1090
|
+
await fs.writeFile(exportPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
1091
|
+
return true;
|
|
1092
|
+
} catch (error) {
|
|
1093
|
+
log.error('Export failed:', error);
|
|
1094
|
+
throw error;
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
ipcMain.handle('data:import', async (event, importPath: string) => {
|
|
1099
|
+
try {
|
|
1100
|
+
const normalizedPath = path.normalize(importPath);
|
|
1101
|
+
if (normalizedPath.includes('..')) {
|
|
1102
|
+
throw new Error('Path traversal not allowed');
|
|
1103
|
+
}
|
|
1104
|
+
const content = await fs.readFile(normalizedPath, 'utf-8');
|
|
1105
|
+
let data: any;
|
|
1106
|
+
try {
|
|
1107
|
+
data = JSON.parse(content);
|
|
1108
|
+
} catch {
|
|
1109
|
+
throw new Error('Invalid JSON format');
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if (!data || typeof data !== 'object') {
|
|
1113
|
+
throw new Error('Invalid import data: must be an object');
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
if (data.agents) {
|
|
1117
|
+
if (!Array.isArray(data.agents)) {
|
|
1118
|
+
throw new Error('Invalid import data: agents must be an array');
|
|
1119
|
+
}
|
|
1120
|
+
for (const agent of data.agents) {
|
|
1121
|
+
if (!agent.name || !agent.projectPath || typeof agent.name !== 'string' || typeof agent.projectPath !== 'string') {
|
|
1122
|
+
throw new Error('Invalid agent: name and projectPath are required strings');
|
|
1123
|
+
}
|
|
1124
|
+
if (agent.projectPath.includes('..') || agent.projectPath.startsWith('/etc') || agent.projectPath.startsWith('/root')) {
|
|
1125
|
+
throw new Error('Invalid agent projectPath: path traversal or restricted path not allowed');
|
|
1126
|
+
}
|
|
1127
|
+
await dbManager.createAgent(agent);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
if (data.automations) {
|
|
1132
|
+
if (!Array.isArray(data.automations)) {
|
|
1133
|
+
throw new Error('Invalid import data: automations must be an array');
|
|
1134
|
+
}
|
|
1135
|
+
for (const automation of data.automations) {
|
|
1136
|
+
if (!automation.name || !automation.trigger) {
|
|
1137
|
+
throw new Error('Invalid automation: name and trigger are required');
|
|
1138
|
+
}
|
|
1139
|
+
await automationScheduler.createAutomation(automation);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (data.settings) {
|
|
1144
|
+
if (typeof data.settings !== 'object' || Array.isArray(data.settings)) {
|
|
1145
|
+
throw new Error('Invalid import data: settings must be an object');
|
|
1146
|
+
}
|
|
1147
|
+
for (const [key, value] of Object.entries(data.settings)) {
|
|
1148
|
+
if (typeof key !== 'string') {
|
|
1149
|
+
throw new Error('Invalid settings: keys must be strings');
|
|
1150
|
+
}
|
|
1151
|
+
settingsManager.setAny(key, value);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
return true;
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
log.error('Import failed:', error);
|
|
1158
|
+
throw error;
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
// Cowork operations
|
|
1163
|
+
ipcMain.handle('cowork:create', async (event, name: string, objective: string, projectPath: string, options: any) => {
|
|
1164
|
+
return await coworkManager.createSession(name, objective, projectPath, options);
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
ipcMain.handle('cowork:start', async (event, sessionId: string) => {
|
|
1168
|
+
return await coworkManager.startSession(sessionId);
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
ipcMain.handle('cowork:pause', async (event, sessionId: string) => {
|
|
1172
|
+
return await coworkManager.pauseSession(sessionId);
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
ipcMain.handle('cowork:stop', async (event, sessionId: string) => {
|
|
1176
|
+
return await coworkManager.stopSession(sessionId);
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
ipcMain.handle('cowork:list', async () => {
|
|
1180
|
+
return coworkManager.getSessions();
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
// Pair programming operations
|
|
1184
|
+
ipcMain.handle('pair:start', async (event, projectPath: string, mode: string, userId: string) => {
|
|
1185
|
+
const validModes = ['collaborative', 'teacher', 'reviewer'];
|
|
1186
|
+
const validMode = validModes.includes(mode) ? mode as 'collaborative' | 'teacher' | 'reviewer' : 'collaborative';
|
|
1187
|
+
return await aiPairProgramming.startSession(projectPath, validMode, userId);
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
ipcMain.handle('pair:chat', async (event, sessionId: string, message: string) => {
|
|
1191
|
+
return await aiPairProgramming.chat(sessionId, message);
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
ipcMain.handle('pair:end', async (event, sessionId: string) => {
|
|
1195
|
+
return await aiPairProgramming.endSession(sessionId);
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
// Smart code assistant
|
|
1199
|
+
ipcMain.handle('assistant:inlineCompletion', async (event, filePath: string, content: string, position: any) => {
|
|
1200
|
+
return await smartCodeAssistant.provideInlineCompletion(filePath, content, position);
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
ipcMain.handle('assistant:suggestFixes', async (event, filePath: string, content: string) => {
|
|
1204
|
+
return await smartCodeAssistant.suggestFixes(filePath, content);
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
ipcMain.handle('assistant:explain', async (event, code: string) => {
|
|
1208
|
+
return await smartCodeAssistant.explainCode(code, 'detailed');
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
// Metrics
|
|
1212
|
+
ipcMain.handle('metrics:get', () => {
|
|
1213
|
+
return metrics.getStats();
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
ipcMain.handle('metrics:export', () => {
|
|
1217
|
+
return metrics.exportMetrics();
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
log.info('IPC handlers setup completed');
|
|
1221
|
+
}
|