/** * MetonaAI Desktop — Electron 主进程入口 * * 启动流程(按架构规范强制顺序): * 1. 初始化日志系统 * 2. 选择/创建工作空间 → 校验必需文件(缺失自动创建) * 3. 连接 SQLite → 执行 schema 迁移 → 加载配置 * 4. 初始化 Provider Adapter * 5. 加载 4 个磁盘文件 → 构建 System Prompt * 6. 注册内置工具 + 连接 MCP Servers * 7. 启动 React UI → Agent 就绪 * * @see docs/MetonaAI-Desktop 架构与交互设计.html — 启动流程 * @see docs/MetonaAI-Desktop UI UX 设计集成方案.html — 窗口管理 */ import { app, shell, Menu } from 'electron'; import { join } from 'path'; import { electronApp, optimizer } from '@electron-toolkit/utils'; import log from 'electron-log'; import { DatabaseService } from './services/database.service'; import { SessionService } from './services/session.service'; import { ConfigService } from './services/config.service'; import { WorkspaceService } from './services/workspace.service'; import { AuditService } from './services/audit.service'; import { SessionRecorder } from './services/session-recorder.service'; import { TrayManager } from './services/tray-manager.service'; import { WindowManager } from './services/window-manager.service'; import { MCPManager } from './services/mcp-manager.service'; import { ContextBuilder } from './harness/prompts/context-builder'; import { MemoryManager } from './harness/memory/manager'; import { registerAllIPCHandlers } from './ipc/handlers'; import { AgentLoopEngine } from './harness/agent-loop'; import { ToolRegistry } from './harness/tools/registry'; import { DeepSeekAdapter } from './harness/adapters/deepseek.adapter'; import { AgnesAdapter } from './harness/adapters/agnes-ai.adapter'; import { OllamaAdapter } from './harness/adapters/ollama.adapter'; import { ReadFileTool, WriteFileTool, ListDirectoryTool, SearchFilesTool, WebSearchTool, WebExtractTool, MemoryStoreTool, MemorySearchTool, RunCommandTool, } from './harness/tools/built-in'; import { AuditLogHook, MemoryTriggerHook, PermissionCheckHook, RateLimitHook } from './harness/hooks'; import { PolicyEngine } from './harness/sandbox/permissions'; import { SandboxManager } from './harness/sandbox/sandbox'; import { PromptInjectionDefender } from './harness/security/prompt-injection-defense'; import { OutputValidator } from './harness/verification/output-validator'; // ===== 步骤 1: 初始化日志系统(SYS 层)===== log.transports.file.level = 'info'; log.transports.console.level = 'debug'; let databaseService: DatabaseService | null = null; let trayManager: TrayManager | null = null; let windowManager: WindowManager | null = null; async function initialize(): Promise { log.info('MetonaAI Desktop starting...'); electronApp.setAppUserModelId('com.metona.ai-desktop'); // 移除原生菜单栏 Menu.setApplicationMenu(null); app.on('browser-window-created', (_, window) => { optimizer.watchWindowShortcuts(window); }); // ===== 步骤 2: 工作空间 ===== const workspaceService = new WorkspaceService(); const workspaceInfo = workspaceService.initialize(); log.info(`Workspace: ${workspaceInfo.path} (missing: ${workspaceInfo.missingFiles.join(', ') || 'none'})`); // ===== 步骤 3: SQLite ===== databaseService = new DatabaseService(workspaceInfo.path); databaseService.initialize(); const db = databaseService.getDB(); const sessionService = new SessionService(() => db); const configService = new ConfigService(() => db); // ===== 日志服务 ===== const auditService = new AuditService(() => db); const sessionRecorder = new SessionRecorder(workspaceInfo.path); // ===== 记忆系统 ===== const memoryManager = new MemoryManager(() => db); memoryManager.initialize(); // ===== 步骤 4: Provider Adapter ===== const provider = configService.get('llm.provider') ?? ''; const model = configService.get('llm.model') ?? ''; const apiKey = configService.get('llm.apiKey') ?? ''; const baseURL = configService.get('llm.baseURL') ?? ''; if (!provider || !baseURL || !model) { log.warn('LLM not configured. Please set provider, baseURL, and model in Settings.'); } const adapterConfig = { provider, baseURL, apiKey, defaultModel: model }; let adapter; switch (provider) { case 'agnes': adapter = new AgnesAdapter(adapterConfig); break; case 'ollama': adapter = new OllamaAdapter(adapterConfig); break; default: adapter = new DeepSeekAdapter(adapterConfig); break; } // ===== 步骤 5: 工作空间文件 + System Prompt ===== const contextBuilder = new ContextBuilder(); // ===== 步骤 6: 注册 9 个内置工具 ===== const toolRegistry = new ToolRegistry(); toolRegistry.registerBuiltin(new ReadFileTool()); toolRegistry.registerBuiltin(new WriteFileTool()); toolRegistry.registerBuiltin(new ListDirectoryTool()); toolRegistry.registerBuiltin(new SearchFilesTool()); toolRegistry.registerBuiltin(new WebSearchTool()); toolRegistry.registerBuiltin(new WebExtractTool()); toolRegistry.registerBuiltin(new MemoryStoreTool(memoryManager)); toolRegistry.registerBuiltin(new MemorySearchTool(memoryManager)); toolRegistry.registerBuiltin(new RunCommandTool()); log.info(`Registered ${toolRegistry.size} built-in tools`); // ===== MCP Manager ===== const mcpManager = new MCPManager(() => db, toolRegistry); mcpManager.initialize().catch((err) => { log.warn('MCP Manager initialization error:', err); }); // ===== 安全模块 ===== const policyEngine = new PolicyEngine(); const sandboxManager = new SandboxManager({ allowedPaths: [workspaceInfo.path], networkPolicy: 'allowlist', }); const promptDefender = new PromptInjectionDefender(); const outputValidator = new OutputValidator(); // ===== Hooks ===== const preToolHooks = [ new PermissionCheckHook(policyEngine), new RateLimitHook(20), ]; const postToolHooks = [ new AuditLogHook(auditService), new MemoryTriggerHook(memoryManager), ]; // ===== Agent Loop ===== const agentLoop = new AgentLoopEngine({}, adapter, toolRegistry, preToolHooks, postToolHooks); agentLoop.setTools(toolRegistry.listTools()); agentLoop.setWorkspacePath(workspaceInfo.path); // ===== 窗口管理 ===== windowManager = new WindowManager(); const mainWindow = windowManager.createWindow({ id: 'main', workspacePath: workspaceInfo.path, title: 'MetonaAI Desktop', }); // ===== 系统托盘 ===== const resourcesPath = join(__dirname, '../../assets'); trayManager = new TrayManager(resourcesPath); trayManager.initialize(mainWindow); // ===== 全局快捷键 ===== windowManager.registerGlobalShortcuts(); // ===== Agent 状态同步到托盘 ===== agentLoop.on('stateChange', (data: { previous: string; current: string }) => { const statusMap: Record = { INIT: 'idle', THINKING: 'thinking', PARSING: 'thinking', EXECUTING: 'executing', OBSERVING: 'executing', REFLECTING: 'thinking', COMPRESSING: 'thinking', TERMINATED: 'idle', }; trayManager?.setStatus(statusMap[data.current] ?? 'idle'); }); // ===== Agent 完成时发送系统通知 ===== agentLoop.on('complete', (data: { sessionId: string; durationMs: number }) => { trayManager?.sendNotification( 'MetonaAI — 任务完成', `Agent 已完成任务 (${(data.durationMs / 1000).toFixed(1)}s)`, () => windowManager?.focusWindow(), ); }); // ===== 注册 IPC ===== registerAllIPCHandlers( mainWindow, sessionService, configService, workspaceService, contextBuilder, agentLoop, toolRegistry, auditService, sessionRecorder, memoryManager, mcpManager, ); // ===== 应用生命周期 ===== app.on('window-all-closed', () => { // macOS: 保持应用运行(托盘模式) }); app.on('activate', () => { if (windowManager && windowManager.count === 0) { windowManager.createWindow({ id: 'main', workspacePath: workspaceInfo.path }); } else { windowManager?.focusWindow(); } }); app.on('before-quit', () => { // 标记为正在退出,允许窗口关闭 (global as Record).isQuitting = true; windowManager?.unregisterGlobalShortcuts(); trayManager?.destroy(); windowManager?.closeAll(); mcpManager.shutdown().catch(() => {}); if (databaseService) { databaseService.close(); databaseService = null; } }); log.info(`MetonaAI Desktop initialized [Provider: ${provider}, Model: ${model}, Tools: ${toolRegistry.size}]`); } app.whenReady().then(initialize); app.on('web-contents-created', (_, contents) => { contents.setWindowOpenHandler(({ url }) => { shell.openExternal(url); return { action: 'deny' }; }); });