✨ 项目概述
AionUi 是一个基于 Electron 框架构建的跨平台桌面应用,旨在为用户提供一个统一、可视化的图形界面,用于与多种 AI 终端代理(例如 Gemini CLI、Claude Code、Qwen Code、Goose CLI、Auggie 等)进行协作。它遵循模块化架构原则,优先考虑用户体验、本地数据安全和开发者可维护性。
✨ 核心特性
- 多AI代理集成:支持通过标准化适配器连接多种AI终端代理,每个代理可独立管理和配置。
- 跨平台兼容:支持 macOS、Windows 和 Linux 操作系统。
- 模块化架构:采用桥接模式处理进程间通信,核心功能(如对话框、文件系统、会话管理)均实现为独立的、可测试的模块。
- 可视化交互界面:提供直观的聊天式界面,支持文件拖放、多会话管理和工作区集成。
- 本地数据安全:对话历史和设置默认存储在本地,API密钥经加密管理,未经用户明确同意不传输数据。
- WebUI模式:可通过嵌入式Web服务器启动,允许通过浏览器远程访问应用。
- 开发者友好:采用 TypeScript 保证类型安全,配置了 ESLint、Prettier、Jest 等工具确保代码质量和一致性,并集成了 Git Hooks 和规范化的提交信息格式。
- 丰富的技能库(Skills):内置支持 PDF、PPTX、DOCX、XLSX 等文档的创建、编辑与分析,并提供诸如“Manus风格文件规划”、“HUMAN 3.0 发展教练”等多种工作流技能。
🚀 安装指南
环境要求
- Node.js: (版本建议参考
package.json中的engines字段) - npm或yarn
- 系统: macOS / Windows / Linux
安装步骤
克隆仓库
gitclone https://github.com/iOfficeAI/AionUi.gitcdAionUi安装依赖
npminstall安装过程会自动运行
postinstall脚本,处理原生模块的安装。开发环境运行
npmstart这会启动 Electron 应用,并运行在开发模式下(支持热重载)。
构建与打包
项目使用 Electron Forge 和 electron-builder 进行构建。- 开发构建:
npmrun build - 生产环境打包:
特定平台或架构的打包脚本可在npmrun distpackage.json的scripts部分找到,如dist:win、dist:mac、dist:linux。
- 开发构建:
原生模块重建
项目依赖一些原生模块(如better-sqlite3、bcrypt等)。跨架构构建或特定平台下,脚本会自动处理重建。如需手动强制重建,可设置环境变量:
FORCE_NATIVE_REBUILD=truenpmrun dist📖 使用说明
基础使用
启动应用:
- 桌面模式:直接运行可执行文件或使用
npm start。 - WebUI 模式:
然后通过浏览器访问# Windows"C:\Program Files\AionUi\AionUi.exe"--webui# macOS/Applications/AionUi.app/Contents/MacOS/AionUi --webuihttp://localhost:3000(端口可能根据配置变化)。
- 桌面模式:直接运行可执行文件或使用
连接AI代理:
应用启动时会自动检测系统中已安装的、支持的AI CLI工具(如Gemini CLI, Claude Code等)。你可以在设置界面查看和管理已连接的代理。开始协作:
在主界面的聊天窗口中输入指令,选择你想要协作的AI代理,即可开始工作。支持文件拖拽上传、多轮对话和上下文管理。
技能(Skills)使用
AionUi 集成了强大的 Cowork 模式,支持通过预定义的“技能”执行复杂任务。当你的请求中包含特定关键词时,对应的技能会被自动激活。
例如:
- 处理 PDF 文件时,可使用
pdf技能进行合并、拆分、填写表单或转换图片。 - 需要创建 PPT 时,可使用
pptx技能,遵循html2pptx.js或pptxgenjs的工作流生成幻灯片。 - 需要进行复杂项目规划时,可激活 “Planning with Files - Manus风格文件规划” 技能,它将引导你创建
task_plan.md、findings.md和progress.md文件来系统化管理任务。
开发与配置
代码风格:项目使用 ESLint 和 Prettier。在提交代码前,Git Hooks 会自动运行检查和格式化。
# 手动运行代码检查npmrun lint# 自动修复可修复的问题npmrun lint:fix# 格式化代码npmrunformat环境变量:可以通过环境变量覆盖开发服务器端口,例如:
AIONUI_DEV_PORT=4000npmstart
💻 核心代码
1. 主进程入口 (main.ts)
此文件是 Electron 应用的主进程入口,负责初始化应用、创建浏览器窗口、设置全局错误处理、启动 Web 服务器和初始化与 AI 代理的通信桥梁。
/** * @license * Copyright 2025 AionUi (aionui.com) * SPDX-License-Identifier: Apache-2.0 */import'./utils/configureChromium';import{app,BrowserWindow,screen}from'electron';importfixPathfrom'fix-path';import*asfsfrom'fs';import*aspathfrom'path';import{initMainAdapterWithWindow}from'./adapter/main';// 初始化主进程与渲染进程的通信桥梁import{ipcBridge}from'./common';import{initializeProcess}from'./process';import{initializeAcpDetector}from'./process/bridge';// 初始化 ACP (AI 代理协议) 检测器import{registerWindowMaximizeListeners}from'./process/bridge/windowControlsBridge';importWorkerManagefrom'./process/WorkerManage';import{startWebServer}from'./webserver';// 启动 WebUI 模式的服务器import{SERVER_CONFIG}from'./webserver/config/constants';import{applyZoomToWindow}from'./process/utils/zoom';// 处理 Windows 安装器事件importelectronSquirrelStartupfrom'electron-squirrel-startup';// 修复 macOS/Linux 下 GUI 应用的 PATH 环境变量if(process.platform==='darwin'||process.platform==='linux'){fixPath();}// 处理 Squirrel 启动事件 (Windows)if(electronSquirrelStartup){app.quit();}// 全局未捕获异常处理器process.on('uncaughtException',(_error)=>{// 生产环境可记录日志或上报if(process.env.NODE_ENV!=='development'){// TODO: 添加错误处理逻辑}});// 应用准备就绪后创建窗口app.whenReady().then(()=>{createWindow();initializeAcpDetector();// 检测可用的 AI 代理// ... 其他初始化逻辑});// 创建浏览器窗口函数functioncreateWindow():void{const{width,height}=screen.getPrimaryDisplay().workAreaSize;constmainWindow=newBrowserWindow({width:Math.min(1440,width),height:Math.min(900,height),webPreferences:{preload:path.join(__dirname,'preload.js'),// 预加载脚本contextIsolation:true,nodeIntegration:false,},});// 加载 Webpack 打包的入口mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);// 初始化与该窗口的通信桥梁initMainAdapterWithWindow(mainWindow);}2. 预加载脚本 (preload.ts)
预加载脚本在渲染进程加载网页之前运行,负责将安全的、受控的 Electron API 暴露给渲染进程,是实现进程间通信(IPC)的关键。
/** * @license * Copyright 2025 AionUi (aionui.com) * SPDX-License-Identifier: Apache-2.0 */import{contextBridge,ipcRenderer,webUtils}from'electron';import{ADAPTER_BRIDGE_EVENT_KEY}from'./adapter/constant';// 将 `electronAPI` 对象安全地暴露给渲染进程的 window 对象contextBridge.exposeInMainWorld('electronAPI',{// 发送消息到主进程emit:(name:string,data:any)=>{returnipcRenderer.invoke(ADAPTER_BRIDGE_EVENT_KEY,JSON.stringify({name:name,data:data,})).catch((error)=>{console.error('IPC invoke error:',error);throwerror;});},// 监听来自主进程的消息on:(callback:any)=>{consthandler=(event:any,value:any)=>{callback({event,value});};ipcRenderer.on(ADAPTER_BRIDGE_EVENT_KEY,handler);return()=>{ipcRenderer.off(ADAPTER_BRIDGE_EVENT_KEY,handler);};},// 获取拖拽文件的绝对路径(用于 WebUI 模式下的文件选择)getPathForFile:(file:File)=>webUtils.getPathForFile(file),});3. AI代理连接与适配器 (AcpConnection.ts)
此类负责与支持 ACP (AI 代理协议) 的外部 CLI 工具建立连接、管理 JSON-RPC 通信和处理权限请求,是集成多种 AI 代理的核心。
/** * @license * Copyright 2025 AionUi (aionui.com) * SPDX-License-Identifier: Apache-2.0 */importtype{AcpBackend,AcpIncomingMessage,AcpMessage,AcpNotification,AcpPermissionRequest,AcpRequest,AcpResponse,AcpSessionUpdate}from'@/types/acpTypes';import{ACP_METHODS,JSONRPC_VERSION}from'@/types/acpTypes';importtype{ChildProcess,SpawnOptions}from'child_process';import{spawn}from'child_process';import{promisesasfs}from'fs';importpathfrom'path';interfacePendingRequest<T=unknown>{resolve:(value:T)=>void;reject:(error:Error)=>void;timeoutId?:NodeJS.Timeout;method:string;isPaused:boolean;startTime:number;timeoutDuration:number;}/** * 创建用于生成 ACP CLI 进程的配置。 */exportfunctioncreateGenericSpawnConfig(cliPath:string,workingDir:string,acpArgs?:string[],customEnv?:Record<string,string>){constisWindows=process.platform==='win32';constenv={...process.env,...customEnv};consteffectiveAcpArgs=acpArgs&&acpArgs.length>0?acpArgs:['--experimental-acp'];letspawnCommand:string;letspawnArgs:string[];if(cliPath.startsWith('npx ')){// 处理 npm 包命令constparts=cliPath.split(' ');spawnCommand=isWindows?'npx.cmd':'npx';spawnArgs=[...parts.slice(1),...effectiveAcpArgs];}else{// 处理直接路径或简单命令spawnCommand=cliPath;spawnArgs=effectiveAcpArgs;}constoptions:SpawnOptions={cwd:workingDir,env,stdio:['pipe','pipe','pipe'],// 标准输入、输出、错误流};return{spawnCommand,spawnArgs,options};}/** * ACP 连接管理类。 */exportclassAcpConnection{privatechild:ChildProcess|null=null;privatenextId=1;privatependingRequests=newMap<number|string,PendingRequest>();// ... 其他私有属性和方法/** * 启动 ACP 会话。 */asyncstartSession(cliPath:string,workingDir:string,acpArgs?:string[],customEnv?:Record<string,string>):Promise<void>{const{spawnCommand,spawnArgs,options}=createGenericSpawnConfig(cliPath,workingDir,acpArgs,customEnv);returnnewPromise((resolve,reject)=>{this.child=spawn(spawnCommand,spawnArgs,options);this.child.stdout?.on('data',(data)=>{this.handleStdout(data.toString());});this.child.stderr?.on('data',(data)=>{console.error(`[ACP STDERR]${data.toString()}`);});this.child.on('error',(error)=>{reject(newError(`Failed to spawn ACP process:${error.message}`));});this.child.on('close',(code)=>{constmessage=`ACP process exited with code${code}`;this.rejectAllPendingRequests(newError(message));});// 发送初始化请求以验证连接this.sendRequest('initialize',{protocolVersion:'0.1.0',capabilities:{tools:[]},}).then(resolve).catch(reject);});}/** * 发送 JSON-RPC 请求。 */privatesendRequest(method:string,params?:unknown,timeoutMs=30000):Promise<unknown>{returnnewPromise((resolve,reject)=>{constid=this.nextId++;constrequest:AcpRequest={jsonrpc:JSONRPC_VERSION,id,method,params,};consttimeoutId=setTimeout(()=>{this.pendingRequests.delete(id);reject(newError(`Request${method}timed out after${timeoutMs}ms`));},timeoutMs);this.pendingRequests.set(id,{resolve,reject,timeoutId,method,isPaused:false,startTime:Date.now(),timeoutDuration:timeoutMs,});if(this.child&&this.child.stdin&&!this.child.stdin.destroyed){this.child.stdin.write(JSON.stringify(request)+'\n');}else{reject(newError('Process stdin is not available'));}});}/** * 处理来自子进程标准输出的数据(解析 JSON-RPC 消息)。 */privatehandleStdout(data:string):void{try{constmessage=JSON.parse(data)asAcpIncomingMessage;if('id'inmessage&&message.id!==undefined){// 处理响应this.handleResponse(messageasAcpResponse);}elseif('method'inmessage){// 处理通知或请求(如权限请求)this.handleNotification(messageasAcpNotification);}}catch(error){console.warn('[ACP] Failed to parse stdout:',data,error);}}// ... 处理响应、通知、权限请求等其他方法}XfkHivgPackSeBYb8h6edGRK+FQI9gdrv8FBRI2tlQs=
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)