kuaifan 0ac4b546ba feat(ai-assistant): 实现 AI 前端操作能力
新增三个 MCP 工具的前端支持:
  - get_page_context: 基于 ARIA 角色收集页面元素,支持分页和区域筛选
  - execute_action: 执行导航操作(打开任务/对话、切换项目/页面)
  - execute_element_action: 元素级操作(click/type/select/focus/scroll/hover)

  新增文件:
  - operation-client.js: WebSocket 客户端,处理与 MCP Server 的通信
  - page-context-collector.js: 页面上下文收集器,ref 系统和 cursor:pointer 扫描
  - action-executor.js: 操作执行器,支持智能解析如 open_task_123
  - operation-module.js: 模块编排,整合上述模块

  修改文件:
  - float-button.vue: 集成 operation-module,AI 助手打开时启用
  - index.vue: 发射关闭事件供 float-button 监听
2026-01-18 01:35:13 +00:00

221 lines
5.2 KiB
JavaScript
Vendored
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* AI 助手前端操作模块
*
* 集成 WebSocket 客户端、页面上下文收集器和操作执行器,
* 提供给 AI 助手组件使用。
*/
import { OperationClient } from './operation-client';
import { collectPageContext } from './page-context-collector';
import { createActionExecutor } from './action-executor';
/**
* 创建操作模块实例
* @param {Object} options
* @param {Object} options.store - Vuex store 实例
* @param {Object} options.router - Vue Router 实例
* @returns {Object} 操作模块实例
*/
export function createOperationModule(options = {}) {
return new OperationModule(options);
}
class OperationModule {
constructor(options) {
this.store = options.store;
this.router = options.router;
this.enabled = false;
this.client = null;
this.executor = null;
this.sessionId = null;
// 回调函数
this.onSessionReady = options.onSessionReady;
this.onSessionLost = options.onSessionLost;
this.onError = options.onError;
}
/**
* 启用操作模块
*/
enable() {
if (this.enabled) {
return;
}
this.enabled = true;
// 创建操作执行器
this.executor = createActionExecutor(this.store, this.router);
// 创建 WebSocket 客户端
this.client = new OperationClient({
getToken: () => this.store.state.userToken,
onRequest: this.handleRequest.bind(this),
onConnected: this.handleConnected.bind(this),
onDisconnected: this.handleDisconnected.bind(this),
onError: this.handleError.bind(this),
});
// 建立连接
this.client.connect();
// 设置心跳
this.heartbeatTimer = setInterval(() => {
if (this.client) {
this.client.ping();
}
}, 30000);
}
/**
* 禁用操作模块
*/
disable() {
if (!this.enabled) {
return;
}
this.enabled = false;
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
if (this.client) {
this.client.disconnect();
this.client = null;
}
this.executor = null;
this.sessionId = null;
}
/**
* 处理来自 MCP 的请求
*/
async handleRequest(action, payload) {
switch (action) {
case 'get_page_context':
return this.getPageContext(payload);
case 'execute_action':
return this.executeAction(payload);
case 'execute_element_action':
return this.executeElementAction(payload);
default:
throw new Error(`未知的操作类型: ${action}`);
}
}
/**
* 获取页面上下文
*/
getPageContext(payload) {
const includeElements = payload?.include_elements !== false;
const interactiveOnly = payload?.interactive_only || false;
const maxElements = payload?.max_elements || 100;
const context = collectPageContext(this.store, {
include_elements: includeElements,
interactive_only: interactiveOnly,
max_elements: maxElements,
});
// 将 refMap 存储到 executor供后续元素操作使用
if (context.ref_map && this.executor) {
this.executor.setRefMap(context.ref_map);
}
return context;
}
/**
* 执行业务操作
*/
async executeAction(payload) {
if (!this.executor) {
throw new Error('操作执行器未初始化');
}
const actionName = payload?.name;
const params = payload?.params || {};
if (!actionName) {
throw new Error('缺少操作名称');
}
return this.executor.executeAction(actionName, params);
}
/**
* 执行元素操作
*/
async executeElementAction(payload) {
if (!this.executor) {
throw new Error('操作执行器未初始化');
}
const elementUid = payload?.element_uid;
const action = payload?.action;
const value = payload?.value;
if (!elementUid || !action) {
throw new Error('缺少必要参数');
}
return this.executor.executeElementAction(elementUid, action, value);
}
/**
* 处理连接成功
*/
handleConnected(sessionId) {
this.sessionId = sessionId;
this.onSessionReady?.(sessionId);
}
/**
* 处理连接断开
*/
handleDisconnected() {
this.sessionId = null;
this.onSessionLost?.();
}
/**
* 处理错误
*/
handleError(error) {
this.onError?.(error);
}
/**
* 获取当前 session ID
*/
getSessionId() {
return this.sessionId;
}
/**
* 检查是否已连接
*/
isConnected() {
return this.client?.isConnected() || false;
}
/**
* 重新连接
*/
reconnect() {
if (this.client) {
this.client.connect();
}
}
}
export default createOperationModule;