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
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import log from 'electron-log';
|
|
3
|
+
import fetch from 'node-fetch';
|
|
4
|
+
|
|
5
|
+
export interface SSOConfig {
|
|
6
|
+
provider: 'okta' | 'azure-ad' | 'google' | 'custom';
|
|
7
|
+
clientId: string;
|
|
8
|
+
clientSecret?: string;
|
|
9
|
+
tenantId?: string;
|
|
10
|
+
domain?: string;
|
|
11
|
+
redirectUri: string;
|
|
12
|
+
scopes: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SSOUser {
|
|
16
|
+
id: string;
|
|
17
|
+
email: string;
|
|
18
|
+
name: string;
|
|
19
|
+
groups: string[];
|
|
20
|
+
roles: string[];
|
|
21
|
+
accessToken: string;
|
|
22
|
+
refreshToken?: string;
|
|
23
|
+
expiresAt: Date;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AuthResult {
|
|
27
|
+
success: boolean;
|
|
28
|
+
user?: SSOUser | null;
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class SSOManager extends EventEmitter {
|
|
33
|
+
private config: SSOConfig | null = null;
|
|
34
|
+
private currentUser: SSOUser | null = null;
|
|
35
|
+
private tokenEndpoint = '';
|
|
36
|
+
private userEndpoint = '';
|
|
37
|
+
|
|
38
|
+
constructor() {
|
|
39
|
+
super();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
configure(config: SSOConfig): void {
|
|
43
|
+
this.config = config;
|
|
44
|
+
|
|
45
|
+
// Set endpoints based on provider
|
|
46
|
+
switch (config.provider) {
|
|
47
|
+
case 'okta':
|
|
48
|
+
this.tokenEndpoint = `${config.domain}/oauth2/v1/token`;
|
|
49
|
+
this.userEndpoint = `${config.domain}/oauth2/v1/userinfo`;
|
|
50
|
+
break;
|
|
51
|
+
case 'azure-ad':
|
|
52
|
+
this.tokenEndpoint = `https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0/token`;
|
|
53
|
+
this.userEndpoint = 'https://graph.microsoft.com/v1.0/me';
|
|
54
|
+
break;
|
|
55
|
+
case 'google':
|
|
56
|
+
this.tokenEndpoint = 'https://oauth2.googleapis.com/token';
|
|
57
|
+
this.userEndpoint = 'https://www.googleapis.com/oauth2/v2/userinfo';
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
if (config.domain) {
|
|
61
|
+
this.tokenEndpoint = `${config.domain}/oauth/token`;
|
|
62
|
+
this.userEndpoint = `${config.domain}/oauth/userinfo`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
log.info('SSO configured', { provider: config.provider });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getAuthorizationUrl(state: string): string {
|
|
70
|
+
if (!this.config) throw new Error('SSO not configured');
|
|
71
|
+
|
|
72
|
+
const params = new URLSearchParams({
|
|
73
|
+
client_id: this.config.clientId,
|
|
74
|
+
redirect_uri: this.config.redirectUri,
|
|
75
|
+
response_type: 'code',
|
|
76
|
+
scope: this.config.scopes.join(' '),
|
|
77
|
+
state,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
switch (this.config.provider) {
|
|
81
|
+
case 'okta':
|
|
82
|
+
return `${this.config.domain}/oauth2/v1/authorize?${params}`;
|
|
83
|
+
case 'azure-ad':
|
|
84
|
+
return `https://login.microsoftonline.com/${this.config.tenantId}/oauth2/v2.0/authorize?${params}`;
|
|
85
|
+
case 'google':
|
|
86
|
+
return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
|
|
87
|
+
default:
|
|
88
|
+
return `${this.config?.domain}/oauth/authorize?${params}`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async handleCallback(code: string): Promise<AuthResult> {
|
|
93
|
+
if (!this.config) return { success: false, error: 'SSO not configured' };
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const tokens = await this.exchangeCodeForTokens(code);
|
|
97
|
+
const user = await this.fetchUserInfo(tokens.access_token);
|
|
98
|
+
|
|
99
|
+
this.currentUser = {
|
|
100
|
+
id: user.id || '',
|
|
101
|
+
email: user.email || '',
|
|
102
|
+
name: user.name || '',
|
|
103
|
+
groups: user.groups || [],
|
|
104
|
+
roles: user.roles || [],
|
|
105
|
+
accessToken: tokens.access_token,
|
|
106
|
+
refreshToken: tokens.refresh_token,
|
|
107
|
+
expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
this.emit('user:loggedIn', this.currentUser);
|
|
111
|
+
return { success: true, user: this.currentUser };
|
|
112
|
+
} catch (error: unknown) {
|
|
113
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
114
|
+
log.error('SSO callback error:', error);
|
|
115
|
+
return { success: false, error: message };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private async exchangeCodeForTokens(code: string): Promise<{
|
|
120
|
+
access_token: string;
|
|
121
|
+
refresh_token?: string;
|
|
122
|
+
expires_in: number;
|
|
123
|
+
}> {
|
|
124
|
+
const params = new URLSearchParams({
|
|
125
|
+
code,
|
|
126
|
+
client_id: this.config!.clientId,
|
|
127
|
+
client_secret: this.config!.clientSecret || '',
|
|
128
|
+
redirect_uri: this.config!.redirectUri,
|
|
129
|
+
grant_type: 'authorization_code',
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const response = await fetch(this.tokenEndpoint, {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
135
|
+
body: params.toString(),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
throw new Error(`Token exchange failed: ${response.statusText}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const data = await response.json() as {
|
|
143
|
+
access_token: string;
|
|
144
|
+
refresh_token?: string;
|
|
145
|
+
expires_in: number;
|
|
146
|
+
};
|
|
147
|
+
return data;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async fetchUserInfo(accessToken: string): Promise<Partial<SSOUser>> {
|
|
151
|
+
const response = await fetch(this.userEndpoint, {
|
|
152
|
+
headers: { 'Authorization': `Bearer ${accessToken}` },
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
if (!response.ok) {
|
|
156
|
+
throw new Error(`User info fetch failed: ${response.statusText}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const data = await response.json() as {
|
|
160
|
+
sub?: string;
|
|
161
|
+
id?: string;
|
|
162
|
+
email?: string;
|
|
163
|
+
mail?: string;
|
|
164
|
+
userPrincipalName?: string;
|
|
165
|
+
name?: string;
|
|
166
|
+
groups?: string[];
|
|
167
|
+
roles?: string[];
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
id: data.sub || data.id || '',
|
|
172
|
+
email: data.email || data.mail || data.userPrincipalName || '',
|
|
173
|
+
name: data.name || '',
|
|
174
|
+
groups: data.groups || [],
|
|
175
|
+
roles: data.roles || [],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async refreshToken(): Promise<boolean> {
|
|
180
|
+
if (!this.currentUser?.refreshToken) return false;
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const params = new URLSearchParams({
|
|
184
|
+
refresh_token: this.currentUser.refreshToken,
|
|
185
|
+
client_id: this.config!.clientId,
|
|
186
|
+
client_secret: this.config!.clientSecret || '',
|
|
187
|
+
grant_type: 'refresh_token',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const response = await fetch(this.tokenEndpoint, {
|
|
191
|
+
method: 'POST',
|
|
192
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
193
|
+
body: params.toString(),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
throw new Error('Token refresh failed');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const tokens = await response.json() as {
|
|
201
|
+
access_token: string;
|
|
202
|
+
refresh_token?: string;
|
|
203
|
+
expires_in: number;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
this.currentUser.accessToken = tokens.access_token;
|
|
207
|
+
this.currentUser.refreshToken = tokens.refresh_token || this.currentUser.refreshToken;
|
|
208
|
+
this.currentUser.expiresAt = new Date(Date.now() + tokens.expires_in * 1000);
|
|
209
|
+
|
|
210
|
+
this.emit('token:refreshed', this.currentUser);
|
|
211
|
+
return true;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
log.error('Token refresh error:', error);
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
logout(): void {
|
|
219
|
+
this.currentUser = null;
|
|
220
|
+
this.emit('user:loggedOut');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
getCurrentUser(): SSOUser | null {
|
|
224
|
+
return this.currentUser;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
isAuthenticated(): boolean {
|
|
228
|
+
return this.currentUser !== null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
getAccessToken(): string | null {
|
|
232
|
+
return this.currentUser?.accessToken || null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
cleanup(): void {
|
|
236
|
+
this.currentUser = null;
|
|
237
|
+
this.removeAllListeners();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export default SSOManager;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { app } from 'electron';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as fs from 'fs/promises';
|
|
5
|
+
import log from 'electron-log';
|
|
6
|
+
|
|
7
|
+
const API_KEYS = new Set<string>();
|
|
8
|
+
|
|
9
|
+
export class SecurityManager {
|
|
10
|
+
private algorithm = 'aes-256-gcm';
|
|
11
|
+
private keyFile: string;
|
|
12
|
+
private masterKey: Buffer | null = null;
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
this.keyFile = path.join(app.getPath('userData'), 'encryption.key');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async initialize(): Promise<void> {
|
|
19
|
+
try {
|
|
20
|
+
const keyData = await fs.readFile(this.keyFile);
|
|
21
|
+
this.masterKey = keyData;
|
|
22
|
+
} catch {
|
|
23
|
+
this.masterKey = crypto.randomBytes(32);
|
|
24
|
+
await fs.mkdir(path.dirname(this.keyFile), { recursive: true });
|
|
25
|
+
await fs.writeFile(this.keyFile, this.masterKey, { mode: 0o600 });
|
|
26
|
+
}
|
|
27
|
+
await this.loadApiKeys();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private async loadApiKeys(): Promise<void> {
|
|
31
|
+
const apiKeysPath = path.join(app.getPath('userData'), 'api-keys.enc');
|
|
32
|
+
try {
|
|
33
|
+
const encryptedData = await fs.readFile(apiKeysPath, 'utf-8');
|
|
34
|
+
const keys = JSON.parse(this.decrypt(encryptedData)) as string[];
|
|
35
|
+
keys.forEach((k) => API_KEYS.add(k));
|
|
36
|
+
} catch {
|
|
37
|
+
const defaultKey = crypto.randomBytes(32).toString('hex');
|
|
38
|
+
API_KEYS.add(defaultKey);
|
|
39
|
+
await this.saveApiKeys();
|
|
40
|
+
log.info('Generated new default API key');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private async saveApiKeys(): Promise<void> {
|
|
45
|
+
const apiKeysPath = path.join(app.getPath('userData'), 'api-keys.enc');
|
|
46
|
+
const encrypted = this.encrypt(JSON.stringify([...API_KEYS]));
|
|
47
|
+
await fs.writeFile(apiKeysPath, encrypted, { mode: 0o600 });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async validateApiKey(apiKey: string): Promise<boolean> {
|
|
51
|
+
return API_KEYS.has(apiKey);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async addApiKey(apiKey: string): Promise<void> {
|
|
55
|
+
API_KEYS.add(apiKey);
|
|
56
|
+
await this.saveApiKeys();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async removeApiKey(apiKey: string): Promise<void> {
|
|
60
|
+
API_KEYS.delete(apiKey);
|
|
61
|
+
await this.saveApiKeys();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
encrypt(text: string): string {
|
|
65
|
+
if (!this.masterKey) {
|
|
66
|
+
throw new Error('Security manager not initialized');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const iv = crypto.randomBytes(16);
|
|
70
|
+
const cipher = crypto.createCipheriv(this.algorithm, this.masterKey, iv);
|
|
71
|
+
|
|
72
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
73
|
+
encrypted += cipher.final('hex');
|
|
74
|
+
|
|
75
|
+
const authTag = (cipher as any).getAuthTag();
|
|
76
|
+
|
|
77
|
+
return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
decrypt(encryptedData: string): string {
|
|
81
|
+
if (!this.masterKey) {
|
|
82
|
+
throw new Error('Security manager not initialized');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const parts = encryptedData.split(':');
|
|
86
|
+
if (parts.length !== 3) {
|
|
87
|
+
throw new Error('Invalid encrypted data format');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
91
|
+
const authTag = Buffer.from(parts[1], 'hex');
|
|
92
|
+
const encrypted = parts[2];
|
|
93
|
+
|
|
94
|
+
const decipher = crypto.createDecipheriv(this.algorithm, this.masterKey, iv);
|
|
95
|
+
(decipher as any).setAuthTag(authTag);
|
|
96
|
+
|
|
97
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
98
|
+
decrypted += decipher.final('utf8');
|
|
99
|
+
|
|
100
|
+
return decrypted;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
hash(data: string): string {
|
|
104
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
generateSecureToken(length: number = 32): string {
|
|
108
|
+
return crypto.randomBytes(length).toString('base64url');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async rotateKey(): Promise<void> {
|
|
112
|
+
const newKey = crypto.randomBytes(32);
|
|
113
|
+
|
|
114
|
+
const backupFile = this.keyFile + '.backup';
|
|
115
|
+
await fs.copyFile(this.keyFile, backupFile);
|
|
116
|
+
|
|
117
|
+
await fs.writeFile(this.keyFile, newKey, { mode: 0o600 });
|
|
118
|
+
|
|
119
|
+
this.masterKey = newKey;
|
|
120
|
+
await this.saveApiKeys();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async reEncryptData(_dataMap: Map<string, string>): Promise<void> {
|
|
124
|
+
throw new Error('Key rotation with data re-encryption is not yet implemented. All encrypted data will become unreadable after key rotation. Manual intervention required.');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
secureCompare(a: string, b: string): boolean {
|
|
128
|
+
if (a.length !== b.length) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let result = 0;
|
|
133
|
+
for (let i = 0; i < a.length; i++) {
|
|
134
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return result === 0;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import * as yaml from 'yaml';
|
|
4
|
+
import log from 'electron-log';
|
|
5
|
+
import { Skill, SkillFile, SkillConfig, SkillParameter } from '../../shared/types';
|
|
6
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
7
|
+
|
|
8
|
+
const DEFAULT_SKILLS_DIR = path.join(__dirname, '../../assets/skills');
|
|
9
|
+
const USER_SKILLS_DIR = path.join(process.env.HOME || '~', '.config', 'codex', 'skills');
|
|
10
|
+
|
|
11
|
+
export class SkillsManager {
|
|
12
|
+
private skills: Map<string, Skill> = new Map();
|
|
13
|
+
private userSkillsPath: string;
|
|
14
|
+
|
|
15
|
+
constructor(userSkillsPath?: string) {
|
|
16
|
+
this.userSkillsPath = userSkillsPath || USER_SKILLS_DIR;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async initialize(): Promise<void> {
|
|
20
|
+
// Ensure user skills directory exists
|
|
21
|
+
await fs.mkdir(this.userSkillsPath, { recursive: true });
|
|
22
|
+
|
|
23
|
+
// Load built-in skills
|
|
24
|
+
await this.loadSkillsFromDirectory(DEFAULT_SKILLS_DIR);
|
|
25
|
+
|
|
26
|
+
// Load user skills
|
|
27
|
+
await this.loadSkillsFromDirectory(this.userSkillsPath);
|
|
28
|
+
|
|
29
|
+
log.info(`Loaded ${this.skills.size} skills`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async listSkills(): Promise<Skill[]> {
|
|
33
|
+
return Array.from(this.skills.values()).sort((a, b) =>
|
|
34
|
+
a.name.localeCompare(b.name)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async getSkill(skillId: string): Promise<Skill | null> {
|
|
39
|
+
return this.skills.get(skillId) || null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async createSkill(config: Partial<Skill>): Promise<Skill> {
|
|
43
|
+
const skill: Skill = {
|
|
44
|
+
id: uuidv4(),
|
|
45
|
+
name: config.name || 'Untitled Skill',
|
|
46
|
+
description: config.description || '',
|
|
47
|
+
version: config.version || '1.0.0',
|
|
48
|
+
author: config.author || 'Anonymous',
|
|
49
|
+
tags: config.tags || [],
|
|
50
|
+
files: config.files || [],
|
|
51
|
+
config: config.config || {
|
|
52
|
+
entryPoint: 'index.md',
|
|
53
|
+
parameters: [],
|
|
54
|
+
dependencies: [],
|
|
55
|
+
permissions: []
|
|
56
|
+
},
|
|
57
|
+
createdAt: new Date(),
|
|
58
|
+
updatedAt: new Date()
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
this.skills.set(skill.id, skill);
|
|
62
|
+
await this.saveSkill(skill);
|
|
63
|
+
|
|
64
|
+
log.info(`Created skill ${skill.id} (${skill.name})`);
|
|
65
|
+
return skill;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async updateSkill(skillId: string, updates: Partial<Skill>): Promise<Skill> {
|
|
69
|
+
const skill = this.skills.get(skillId);
|
|
70
|
+
if (!skill) {
|
|
71
|
+
throw new Error(`Skill ${skillId} not found`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Object.assign(skill, updates, { updatedAt: new Date() });
|
|
75
|
+
await this.saveSkill(skill);
|
|
76
|
+
|
|
77
|
+
log.info(`Updated skill ${skillId}`);
|
|
78
|
+
return skill;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async deleteSkill(skillId: string): Promise<void> {
|
|
82
|
+
const skill = this.skills.get(skillId);
|
|
83
|
+
if (!skill) {
|
|
84
|
+
throw new Error(`Skill ${skillId} not found`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Delete skill directory
|
|
88
|
+
const skillPath = path.join(this.userSkillsPath, skill.name);
|
|
89
|
+
await fs.rmdir(skillPath, { recursive: true }).catch(() => {});
|
|
90
|
+
|
|
91
|
+
this.skills.delete(skillId);
|
|
92
|
+
log.info(`Deleted skill ${skillId}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async searchSkills(query: string, tags?: string[]): Promise<Skill[]> {
|
|
96
|
+
let results = Array.from(this.skills.values());
|
|
97
|
+
|
|
98
|
+
if (query) {
|
|
99
|
+
const lowerQuery = query.toLowerCase();
|
|
100
|
+
results = results.filter(skill =>
|
|
101
|
+
skill.name.toLowerCase().includes(lowerQuery) ||
|
|
102
|
+
skill.description.toLowerCase().includes(lowerQuery) ||
|
|
103
|
+
skill.tags.some(tag => tag.toLowerCase().includes(lowerQuery))
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (tags && tags.length > 0) {
|
|
108
|
+
results = results.filter(skill =>
|
|
109
|
+
tags.some(tag => skill.tags.includes(tag))
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async loadSkillsFromDirectory(dir: string): Promise<void> {
|
|
117
|
+
try {
|
|
118
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
119
|
+
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (entry.isDirectory()) {
|
|
122
|
+
await this.loadSkillFromPath(path.join(dir, entry.name));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
log.warn(`Failed to load skills from ${dir}:`, error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async loadSkillFromPath(skillPath: string): Promise<void> {
|
|
131
|
+
try {
|
|
132
|
+
const configPath = path.join(skillPath, 'skill.yaml');
|
|
133
|
+
let config: any = {};
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const configContent = await fs.readFile(configPath, 'utf-8');
|
|
137
|
+
config = yaml.parse(configContent);
|
|
138
|
+
} catch {
|
|
139
|
+
// No config file, use defaults
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Load files
|
|
143
|
+
const files: SkillFile[] = [];
|
|
144
|
+
const entries = await fs.readdir(skillPath, { withFileTypes: true });
|
|
145
|
+
|
|
146
|
+
for (const entry of entries) {
|
|
147
|
+
if (!entry.isDirectory()) {
|
|
148
|
+
const filePath = path.join(skillPath, entry.name);
|
|
149
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
150
|
+
|
|
151
|
+
let type: SkillFile['type'] = 'instruction';
|
|
152
|
+
if (entry.name.endsWith('.template')) type = 'template';
|
|
153
|
+
else if (entry.name.endsWith('.tool')) type = 'tool';
|
|
154
|
+
else if (entry.name === 'skill.yaml') type = 'config';
|
|
155
|
+
|
|
156
|
+
files.push({
|
|
157
|
+
path: entry.name,
|
|
158
|
+
content,
|
|
159
|
+
type
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const skill: Skill = {
|
|
165
|
+
id: config.id || uuidv4(),
|
|
166
|
+
name: config.name || path.basename(skillPath),
|
|
167
|
+
description: config.description || '',
|
|
168
|
+
version: config.version || '1.0.0',
|
|
169
|
+
author: config.author || 'Unknown',
|
|
170
|
+
tags: config.tags || [],
|
|
171
|
+
files,
|
|
172
|
+
config: {
|
|
173
|
+
entryPoint: config.entryPoint || 'index.md',
|
|
174
|
+
parameters: config.parameters || [],
|
|
175
|
+
dependencies: config.dependencies || [],
|
|
176
|
+
permissions: config.permissions || []
|
|
177
|
+
},
|
|
178
|
+
createdAt: config.createdAt ? new Date(config.createdAt) : new Date(),
|
|
179
|
+
updatedAt: config.updatedAt ? new Date(config.updatedAt) : new Date()
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
this.skills.set(skill.id, skill);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
log.error(`Failed to load skill from ${skillPath}:`, error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private async saveSkill(skill: Skill): Promise<void> {
|
|
189
|
+
const skillPath = path.join(this.userSkillsPath, skill.name);
|
|
190
|
+
await fs.mkdir(skillPath, { recursive: true });
|
|
191
|
+
|
|
192
|
+
// Save config
|
|
193
|
+
const configPath = path.join(skillPath, 'skill.yaml');
|
|
194
|
+
const config = {
|
|
195
|
+
id: skill.id,
|
|
196
|
+
name: skill.name,
|
|
197
|
+
description: skill.description,
|
|
198
|
+
version: skill.version,
|
|
199
|
+
author: skill.author,
|
|
200
|
+
tags: skill.tags,
|
|
201
|
+
entryPoint: skill.config.entryPoint,
|
|
202
|
+
parameters: skill.config.parameters,
|
|
203
|
+
dependencies: skill.config.dependencies,
|
|
204
|
+
permissions: skill.config.permissions,
|
|
205
|
+
createdAt: skill.createdAt,
|
|
206
|
+
updatedAt: skill.updatedAt
|
|
207
|
+
};
|
|
208
|
+
await fs.writeFile(configPath, yaml.stringify(config), 'utf-8');
|
|
209
|
+
|
|
210
|
+
// Save files
|
|
211
|
+
for (const file of skill.files) {
|
|
212
|
+
if (file.type !== 'config') {
|
|
213
|
+
const filePath = path.join(skillPath, file.path);
|
|
214
|
+
await fs.writeFile(filePath, file.content, 'utf-8');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import log from 'electron-log';
|
|
3
|
+
|
|
4
|
+
interface SSHConnection {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
host: string;
|
|
8
|
+
port: number;
|
|
9
|
+
username: string;
|
|
10
|
+
privateKeyPath?: string;
|
|
11
|
+
password?: string;
|
|
12
|
+
status: 'disconnected' | 'connecting' | 'connected' | 'error';
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class SSHManager extends EventEmitter {
|
|
17
|
+
private connections: Map<string, SSHConnection> = new Map();
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
21
|
+
log.warn('SSH support requires "ssh2" package. Run: npm install ssh2');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async addConnection(config: Omit<SSHConnection, 'id' | 'status'>): Promise<SSHConnection> {
|
|
25
|
+
const connection: SSHConnection = {
|
|
26
|
+
...config,
|
|
27
|
+
id: `ssh-${Date.now()}`,
|
|
28
|
+
status: 'disconnected'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
this.connections.set(connection.id, connection);
|
|
32
|
+
this.emit('connection:added', connection);
|
|
33
|
+
log.info(`SSH connection added: ${connection.name} (${connection.host})`);
|
|
34
|
+
|
|
35
|
+
return connection;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async removeConnection(connectionId: string): Promise<void> {
|
|
39
|
+
this.connections.delete(connectionId);
|
|
40
|
+
this.emit('connection:removed', { connectionId });
|
|
41
|
+
log.info(`SSH connection removed: ${connectionId}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async connect(_connectionId: string): Promise<void> {
|
|
45
|
+
throw new Error('SSH support not available. Install ssh2 package.');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async disconnect(_connectionId: string): Promise<void> {
|
|
49
|
+
// No-op
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async executeCommand(_connectionId: string, _command: string): Promise<string> {
|
|
53
|
+
throw new Error('SSH support not available. Install ssh2 package.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async readFile(_connectionId: string, _remotePath: string): Promise<string> {
|
|
57
|
+
throw new Error('SSH support not available. Install ssh2 package.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async writeFile(_connectionId: string, _remotePath: string, _content: string): Promise<void> {
|
|
61
|
+
throw new Error('SSH support not available. Install ssh2 package.');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async listDirectory(_connectionId: string, _remotePath: string): Promise<any[]> {
|
|
65
|
+
throw new Error('SSH support not available. Install ssh2 package.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getConnections(): SSHConnection[] {
|
|
69
|
+
return Array.from(this.connections.values());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getConnection(connectionId: string): SSHConnection | undefined {
|
|
73
|
+
return this.connections.get(connectionId);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
isConnected(_connectionId: string): boolean {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
cleanup(): void {
|
|
81
|
+
this.connections.clear();
|
|
82
|
+
this.removeAllListeners();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default SSHManager;
|