@xiaou66/vite-plugin-vue-mcp-next 0.0.1
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/LICENSE +21 -0
- package/README.md +531 -0
- package/dist/index.cjs +1519 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +410 -0
- package/dist/index.d.ts +410 -0
- package/dist/index.js +1482 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/client.cjs +691 -0
- package/dist/runtime/client.cjs.map +1 -0
- package/dist/runtime/client.d.cts +30 -0
- package/dist/runtime/client.d.ts +30 -0
- package/dist/runtime/client.js +672 -0
- package/dist/runtime/client.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/runtime/client.ts","../../src/runtime/consoleHook.ts","../../src/shared/serialization.ts","../../src/runtime/networkHook.ts","../../src/shared/sanitize.ts","../../src/shared/url.ts","../../src/runtime/pageIdentity.ts","../../src/runtime/vueBridge.ts","../../src/runtime/domSnapshot.ts","../../src/runtime/evaluateExpression.ts","../../src/runtime/devtoolsBridge.ts"],"sourcesContent":["import { createHotContext } from 'vite-hot-client'\nimport { installConsoleHook } from './consoleHook'\nimport { installNetworkHook } from './networkHook'\nimport { getRuntimePageIdentity } from './pageIdentity'\nimport { installVueBridge } from './vueBridge'\nexport { evaluateExpression } from './evaluateExpression'\nexport type { RuntimeEvaluateRequest } from './evaluateExpression'\n\n/**\n * 启动浏览器端 Runtime Bridge。\n *\n * 运行时脚本负责连接 Vite WebSocket 并上报页面身份;Vue、Console、Network 等子能力\n * 会在后续任务中挂到这个启动流程中。\n */\nexport async function startRuntimeClient(): Promise<void> {\n const hot = await createHotContext('vite-plugin-vue-mcp-next', '/')\n\n if (!hot) {\n return\n }\n\n installVueBridge(hot)\n\n const identity = getRuntimePageIdentity({\n href: window.location.href,\n title: document.title,\n innerWidth: window.innerWidth,\n innerHeight: window.innerHeight,\n readyState: document.readyState\n })\n\n hot.send('vite-plugin-vue-mcp-next:page-connected', identity)\n installConsoleHook({\n pageId: identity.pageId,\n send(record) {\n hot.send('vite-plugin-vue-mcp-next:console-record', record)\n }\n })\n installNetworkHook({\n pageId: identity.pageId,\n maxBodySize: 100_000,\n maskHeaders: ['authorization', 'cookie', 'set-cookie'],\n send(record) {\n hot.send('vite-plugin-vue-mcp-next:network-record', record)\n }\n })\n}\n","import { nanoid } from 'nanoid'\nimport { safeStringify } from '../shared/serialization'\nimport type { ConsoleRecord } from '../types'\n\n/**\n * Console Hook 安装参数。\n *\n * Hook 运行在浏览器页面内,需要通过 send 回调交给 Vite WebSocket,而不是直接依赖服务器模块。\n */\nexport interface ConsoleHookOptions {\n /** 当前页面 ID,用于服务端区分多页面日志来源。 */\n readonly pageId: string\n /** 发送规范化日志记录的回调,由 runtime client 绑定到 Vite WebSocket。 */\n readonly send: (record: ConsoleRecord) => void\n}\n\n/**\n * 安装页面 Console 和错误 Hook。\n *\n * 即使启用 CDP,也保留该 Hook,因为早期日志可能发生在 CDP target 匹配完成之前。\n */\nexport function installConsoleHook(options: ConsoleHookOptions): () => void {\n const originalConsole = {\n log: console.log,\n info: console.info,\n warn: console.warn,\n error: console.error,\n debug: console.debug\n }\n\n const emit = (level: ConsoleRecord['level'], args: unknown[]): void => {\n options.send({\n id: nanoid(),\n pageId: options.pageId,\n source: 'hook',\n level,\n message: args.map((arg) => safeStringify(arg)).join(' '),\n args,\n timestamp: Date.now()\n })\n }\n\n ;(['log', 'info', 'warn', 'error', 'debug'] as const).forEach((level) => {\n console[level] = (...args: unknown[]) => {\n emit(level, args)\n originalConsole[level](...args)\n }\n })\n\n const onError = (event: ErrorEvent): void => {\n options.send({\n id: nanoid(),\n pageId: options.pageId,\n source: 'hook',\n level: 'error',\n message: event.message,\n stack: event.error instanceof Error ? event.error.stack : undefined,\n timestamp: Date.now()\n })\n }\n\n window.addEventListener('error', onError)\n\n return () => {\n Object.assign(console, originalConsole)\n window.removeEventListener('error', onError)\n }\n}\n","/**\n * 将未知值转换为适合 MCP 文本输出的字符串。\n *\n * Console 参数、脚本执行结果和 Network body 都可能包含循环引用,统一序列化能避免工具调用崩溃。\n */\nexport function safeStringify(value: unknown): string {\n if (typeof value === 'string') {\n return value\n }\n\n const seen = new WeakSet()\n\n const serialized = JSON.stringify(\n value,\n (_key: string, current: unknown): unknown => {\n if (typeof current !== 'object' || current === null) {\n return current\n }\n\n if (seen.has(current)) {\n return '[Circular]'\n }\n\n seen.add(current)\n return current\n }\n )\n\n return serialized\n}\n","import { nanoid } from 'nanoid'\nimport { maskHeaders, truncateText } from '../shared/sanitize'\nimport { parseRequestQuery } from '../shared/url'\nimport type { NetworkRecord } from '../types'\n\n/**\n * Hook Network 记录创建参数。\n *\n * 将记录创建抽成纯函数,便于测试脱敏、query 解析和字段标准化。\n */\nexport interface HookNetworkRecordInput {\n /** 当前页面 ID,用于服务端区分多页面请求来源。 */\n readonly pageId: string\n /** 请求 URL,用于记录接口地址和解析 query 参数。 */\n readonly url: string\n /** HTTP 方法,Hook 会从 fetch init 或 XHR open 中提取。 */\n readonly method: string\n /** 请求头快照,采集前会按 maskHeaders 脱敏。 */\n readonly requestHeaders?: Record<string, string>\n /** 请求体快照,用于调试提交参数。 */\n readonly requestBody?: unknown\n /** 需要脱敏的 header 名称。 */\n readonly maskHeaders: readonly string[]\n /** 请求开始时间,用于后续计算耗时。 */\n readonly startedAt: number\n}\n\n/**\n * Network Hook 安装参数。\n *\n * Hook 运行在浏览器页面内,通过 send 回调把记录交给 Vite WebSocket。\n */\nexport interface NetworkHookOptions {\n /** 当前页面 ID,用于服务端区分多页面请求来源。 */\n readonly pageId: string\n /** 请求体和响应体最大采集长度,避免大响应污染 MCP 上下文。 */\n readonly maxBodySize: number\n /** 需要脱敏的 header 名称。 */\n readonly maskHeaders: readonly string[]\n /** 发送规范化网络记录的回调。 */\n readonly send: (record: NetworkRecord) => void\n}\n\n/**\n * 创建 Hook 来源的 Network 记录。\n *\n * Hook 模式只覆盖 fetch/XHR,但它可以零配置提供业务接口的请求参数和响应值。\n */\nexport function createHookNetworkRecord(\n input: HookNetworkRecordInput\n): NetworkRecord {\n return {\n id: nanoid(),\n pageId: input.pageId,\n source: 'hook',\n url: input.url,\n method: input.method,\n requestHeaders: maskHeaders(input.requestHeaders, input.maskHeaders),\n requestQuery: parseRequestQuery(input.url),\n requestBody: input.requestBody,\n startedAt: input.startedAt\n }\n}\n\n/**\n * 安装 fetch 和 XHR 网络 Hook。\n *\n * Hook 不覆盖静态资源和浏览器内部请求,但能在无 CDP 配置时捕获大多数业务接口。\n */\nexport function installNetworkHook(options: NetworkHookOptions): () => void {\n const originalFetch = window.fetch.bind(window)\n const XMLHttpRequestCtor = window.XMLHttpRequest as\n | typeof XMLHttpRequest\n | undefined\n // eslint-disable-next-line @typescript-eslint/unbound-method -- XHR 原型方法必须保留动态 this,后续通过 Reflect.apply 绑定到具体实例。\n const originalOpen = XMLHttpRequestCtor?.prototype.open\n // eslint-disable-next-line @typescript-eslint/unbound-method -- XHR 原型方法必须保留动态 this,后续通过 Reflect.apply 绑定到具体实例。\n const originalSend = XMLHttpRequestCtor?.prototype.send\n\n window.fetch = createFetchHook(originalFetch, options)\n\n if (XMLHttpRequestCtor && originalOpen && originalSend) {\n installXhrHook(XMLHttpRequestCtor, originalOpen, originalSend, options)\n }\n\n return () => {\n window.fetch = originalFetch\n if (XMLHttpRequestCtor && originalOpen && originalSend) {\n XMLHttpRequestCtor.prototype.open = originalOpen\n XMLHttpRequestCtor.prototype.send = originalSend\n }\n }\n}\n\n/**\n * 创建 fetch 包装函数。\n *\n * 使用 response.clone() 读取响应体,避免调试采集破坏业务代码对 response 的消费。\n */\nfunction createFetchHook(\n originalFetch: typeof window.fetch,\n options: NetworkHookOptions\n): typeof window.fetch {\n return async (input, init) => {\n const startedAt = Date.now()\n const record = createHookNetworkRecord({\n pageId: options.pageId,\n url: getFetchUrl(input),\n method: getFetchMethod(input, init),\n requestHeaders: headersToRecord(\n init?.headers ?? (input instanceof Request ? input.headers : undefined)\n ),\n requestBody: init?.body,\n maskHeaders: options.maskHeaders,\n startedAt\n })\n\n try {\n const response = await originalFetch(input, init)\n const endedAt = Date.now()\n options.send({\n ...record,\n status: response.status,\n responseHeaders: headersToRecord(response.headers),\n responseBody: await readResponseBody(response, options.maxBodySize),\n endedAt,\n durationMs: endedAt - startedAt\n })\n return response\n } catch (error) {\n const endedAt = Date.now()\n options.send({\n ...record,\n error: error instanceof Error ? error.message : String(error),\n endedAt,\n durationMs: endedAt - startedAt\n })\n throw error\n }\n }\n}\n\n/**\n * 安装 XHR 包装。\n *\n * XHR 没有 fetch 那样的 clone 能力,因此只读取 responseText,并在失败时静默降级。\n */\nfunction installXhrHook(\n XMLHttpRequestCtor: typeof XMLHttpRequest,\n originalOpen: typeof XMLHttpRequest.prototype.open,\n originalSend: typeof XMLHttpRequest.prototype.send,\n options: NetworkHookOptions\n): void {\n const states = new WeakMap<\n XMLHttpRequest,\n { method: string; url: string; startedAt: number; body?: unknown }\n >()\n\n XMLHttpRequestCtor.prototype.open = function open(\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null\n ): void {\n const args = [method, url, async, username, password].filter(\n (item) => item !== undefined\n )\n states.set(this, { method, url: String(url), startedAt: 0 })\n Reflect.apply(originalOpen, this, args)\n }\n\n XMLHttpRequestCtor.prototype.send = function send(\n this: XMLHttpRequest,\n ...args: Parameters<typeof originalSend>\n ): void {\n const [body] = args\n const state = states.get(this)\n\n if (state) {\n state.startedAt = Date.now()\n state.body = body\n const record = createHookNetworkRecord({\n pageId: options.pageId,\n url: state.url,\n method: state.method,\n requestBody: body,\n maskHeaders: options.maskHeaders,\n startedAt: state.startedAt\n })\n this.addEventListener('loadend', () => {\n const endedAt = Date.now()\n options.send({\n ...record,\n status: this.status,\n responseHeaders: parseRawHeaders(this.getAllResponseHeaders()),\n responseBody: truncateText(\n safeReadXhrResponseText(this),\n options.maxBodySize\n ).text,\n endedAt,\n durationMs: endedAt - state.startedAt\n })\n })\n }\n\n Reflect.apply(originalSend, this, args)\n }\n}\n\n/**\n * 获取 fetch 请求 URL。\n *\n * fetch 支持字符串、URL 和 Request,多形态输入需要统一为字符串才能进入 NetworkRecord。\n */\nfunction getFetchUrl(input: RequestInfo | URL): string {\n if (input instanceof Request) {\n return input.url\n }\n\n return String(input)\n}\n\n/**\n * 获取 fetch 请求方法。\n *\n * init.method 优先级最高,其次复用 Request.method,最后回退到 GET。\n */\nfunction getFetchMethod(input: RequestInfo | URL, init?: RequestInit): string {\n if (init?.method) {\n return init.method.toUpperCase()\n }\n\n if (input instanceof Request) {\n return input.method.toUpperCase()\n }\n\n return 'GET'\n}\n\n/**\n * 将 HeadersInit 转成普通对象。\n *\n * MCP 输出需要 JSON 友好的结构,不能直接返回 Headers 实例。\n */\nfunction headersToRecord(headers?: HeadersInit): Record<string, string> {\n if (!headers) {\n return {}\n }\n\n return Object.fromEntries(new Headers(headers).entries())\n}\n\n/**\n * 读取 fetch 响应体。\n *\n * 使用 clone 防止消费业务响应;读取失败时返回 undefined,让 Hook 不影响页面逻辑。\n */\nasync function readResponseBody(\n response: Response,\n maxBodySize: number\n): Promise<string | undefined> {\n try {\n return truncateText(await response.clone().text(), maxBodySize).text\n } catch {\n return undefined\n }\n}\n\n/**\n * 解析 XHR 原始响应头。\n *\n * XHR 只提供字符串格式的响应头,拆成对象后 MCP 工具更容易过滤和展示。\n */\nfunction parseRawHeaders(rawHeaders: string): Record<string, string> {\n return Object.fromEntries(\n rawHeaders\n .trim()\n .split(/\\r?\\n/)\n .filter(Boolean)\n .map((line) => {\n const index = line.indexOf(':')\n return [\n line.slice(0, index).trim().toLowerCase(),\n line.slice(index + 1).trim()\n ]\n })\n )\n}\n\n/**\n * 安全读取 XHR responseText。\n *\n * 某些 responseType 下读取 responseText 会抛错,Hook 必须静默降级而不是影响业务请求。\n */\nfunction safeReadXhrResponseText(xhr: XMLHttpRequest): string {\n try {\n return xhr.responseText\n } catch {\n return ''\n }\n}\n","/**\n * 文本截断结果。\n *\n * MCP 输出需要明确告诉调用方内容被截断,否则 AI 可能误以为看到的是完整响应。\n */\nexport interface TruncatedText {\n /** 截断后的文本。 */\n readonly text: string\n /** 是否发生截断。 */\n readonly truncated: boolean\n /** 原始文本长度,用于判断丢失信息规模。 */\n readonly originalLength: number\n}\n\n/**\n * 截断长文本。\n *\n * DOM 文本和响应体都可能很大,统一截断策略可以避免不同工具输出行为不一致。\n */\nexport function truncateText(text: string, maxLength: number): TruncatedText {\n if (text.length <= maxLength) {\n return { text, truncated: false, originalLength: text.length }\n }\n\n return {\n text: text.slice(0, maxLength),\n truncated: true,\n originalLength: text.length\n }\n}\n\n/**\n * 对敏感 header 做脱敏。\n *\n * Network 调试需要展示 header,但认证和 Cookie 不应原样暴露给 AI 客户端。\n */\nexport function maskHeaders(\n headers: Record<string, string> = {},\n maskNames: readonly string[] = []\n): Record<string, string> {\n const normalizedMaskNames = new Set(\n maskNames.map((name) => name.toLowerCase())\n )\n\n return Object.fromEntries(\n Object.entries(headers).map(([name, value]) => [\n name,\n normalizedMaskNames.has(name.toLowerCase()) ? '[masked]' : value\n ])\n )\n}\n","/**\n * 解析请求 URL 中的 query 参数。\n *\n * Network 工具需要直接回答“请求参数是什么”,将 query 拆成结构化对象可以减少 AI 重复解析。\n */\nexport function parseRequestQuery(\n url: string\n): Record<string, string | string[]> {\n const parsed = new URL(url, 'http://vite-plugin-vue-mcp-next.local')\n const queryEntries = new Map<string, string | string[]>()\n\n for (const [key, value] of parsed.searchParams.entries()) {\n const existing = queryEntries.get(key)\n\n if (existing === undefined) {\n queryEntries.set(key, value)\n continue\n }\n\n if (Array.isArray(existing)) {\n existing.push(value)\n continue\n }\n\n queryEntries.set(key, [existing, value])\n }\n\n return Object.fromEntries(queryEntries)\n}\n\n/**\n * 获取 URL pathname。\n *\n * 页面 target 可能上报绝对 URL 或相对路径,该 helper 让展示逻辑不需要关心来源格式。\n */\nexport function safeUrlPathname(url: string): string {\n try {\n return new URL(url).pathname\n } catch {\n return url.split('?')[0] || '/'\n }\n}\n","import { nanoid } from 'nanoid'\nimport { safeUrlPathname } from '../shared/url'\n\n/**\n * 页面运行时身份输入。\n *\n * 测试中传入 window-like 对象可以避免直接依赖浏览器全局对象。\n */\nexport interface RuntimePageIdentityInput {\n /** 当前页面完整 URL,用于关联 runtime target 和 CDP target。 */\n readonly href: string\n /** 当前页面标题,用于多页面调试时辅助识别。 */\n readonly title: string\n /** 视口宽度,用于帮助 AI 判断页面当前布局状态。 */\n readonly innerWidth: number\n /** 视口高度,用于帮助 AI 判断页面当前布局状态。 */\n readonly innerHeight: number\n /** 文档加载状态,用于解释某些 DOM 或日志为何暂时不可用。 */\n readonly readyState: DocumentReadyState\n}\n\n/**\n * 页面运行时身份。\n *\n * Runtime Bridge 上报该结构后,服务端可以在没有 CDP 的情况下也维护可调试页面列表。\n */\nexport interface RuntimePageIdentity {\n /** runtime 页面唯一标识,同一路径多 tab 打开时仍可区分。 */\n readonly pageId: string\n /** 固定标记为 runtime,便于服务端区分 CDP target。 */\n readonly source: 'runtime'\n /** 当前页面完整 URL,用于展示和 target 关联。 */\n readonly url: string\n /** URL pathname,用于多入口页面的短路径展示。 */\n readonly pathname: string\n /** 页面标题,用于多页面调试时辅助识别。 */\n readonly title: string\n /** runtime 启动后页面默认处于可连接状态。 */\n readonly connected: true\n /** 文档加载状态,用于解释 DOM 快照时机。 */\n readonly readyState: DocumentReadyState\n /** 当前视口尺寸,用于帮助 AI 判断响应式布局状态。 */\n readonly viewport: {\n readonly width: number\n readonly height: number\n }\n}\n\n/**\n * 创建 runtime 页面 ID。\n *\n * 使用随机 ID 而不是 URL,是因为同一个页面可能在多个 tab 中同时打开。\n */\nexport function createRuntimePageId(): string {\n return `runtime-${nanoid()}`\n}\n\n/**\n * 读取页面身份信息。\n *\n * Runtime Bridge 启动后立即上报该信息,让 MCP 在没有 CDP 的情况下也能列出可调试页面。\n */\nexport function getRuntimePageIdentity(\n input: RuntimePageIdentityInput\n): RuntimePageIdentity {\n return {\n pageId: createRuntimePageId(),\n source: 'runtime',\n url: input.href,\n pathname: safeUrlPathname(input.href),\n title: input.title,\n connected: true,\n readyState: input.readyState,\n viewport: {\n width: input.innerWidth,\n height: input.innerHeight\n }\n }\n}\n","import {\n devtools,\n devtoolsRouterInfo,\n devtoolsState,\n getInspector,\n stringify,\n toggleHighPerfMode\n} from '@vue/devtools-kit'\nimport { createRPCClient } from 'vite-dev-rpc'\nimport type { ViteHotContext } from 'vite-hot-client'\nimport type { VueRuntimeRpc } from '../types'\nimport { createRuntimeDevtoolsRpc } from './devtoolsBridge'\n\nconst PINIA_INSPECTOR_ID = 'pinia'\nconst COMPONENTS_INSPECTOR_ID = 'components'\nconst COMPONENT_HIGHLIGHT_DURATION = 5000\n\nlet highlightComponentTimeout: ReturnType<typeof setTimeout> | undefined\n\n/**\n * 安装 Vue Runtime Bridge。\n *\n * Vue 组件树、组件状态、Router 和 Pinia 都是应用层语义,CDP 只能看到 DOM,\n * 因此这些能力必须直接使用 Vue DevTools runtime API 暴露给 MCP 服务端。\n */\nexport function installVueBridge(hot: ViteHotContext): void {\n devtools.init()\n\n const rpcRef: { current?: VueRuntimeRpc } = {}\n const rpc = createRPCClient<VueRuntimeRpc, VueRuntimeRpc>(\n 'vite-plugin-vue-mcp-next',\n hot,\n createClientVueRuntimeRpc(() => {\n if (!rpcRef.current) {\n throw new Error('Vue runtime RPC is not initialized')\n }\n return rpcRef.current\n }),\n { timeout: -1 }\n )\n rpcRef.current = rpc\n}\n\n/**\n * 创建浏览器端 Vue RPC 实现。\n *\n * 函数单独拆分可以让每个 Vue 能力的错误边界集中处理,避免 MCP 请求因为某个组件缺失而崩溃。\n */\nfunction createClientVueRuntimeRpc(getRpc: () => VueRuntimeRpc): VueRuntimeRpc {\n return {\n ...createRuntimeDevtoolsRpc(getRpc),\n async getInspectorTree(query) {\n const inspectorTree = await devtools.api.getInspectorTree({\n inspectorId: COMPONENTS_INSPECTOR_ID,\n filter: query.componentName ?? ''\n })\n getRpc().onInspectorTreeUpdated(query.event, inspectorTree[0])\n },\n onInspectorTreeUpdated: () => undefined,\n async getInspectorState(query) {\n const targetNode = await findComponentNode(query.componentName)\n if (!targetNode) {\n getRpc().onInspectorStateUpdated(\n query.event,\n createMissingComponentError(query.componentName)\n )\n return\n }\n\n const inspectorState = await devtools.api.getInspectorState({\n inspectorId: COMPONENTS_INSPECTOR_ID,\n nodeId: targetNode.id\n })\n getRpc().onInspectorStateUpdated(query.event, stringify(inspectorState))\n },\n onInspectorStateUpdated: () => undefined,\n async editComponentState(query) {\n const targetNode = await findComponentNode(query.componentName)\n if (!targetNode) {\n return\n }\n\n devtools.ctx.api.editInspectorState({\n app: null,\n inspectorId: COMPONENTS_INSPECTOR_ID,\n nodeId: targetNode.id,\n path: query.path,\n state: {\n remove: false,\n value: parseStateValue(query.value, query.valueType)\n },\n type: query.valueType,\n set: setStateValue\n })\n },\n async highlightComponent(query) {\n const targetNode = await findComponentNode(query.componentName)\n if (!targetNode) {\n return\n }\n\n if (highlightComponentTimeout) {\n clearTimeout(highlightComponentTimeout)\n }\n\n callVueDevtoolsHook('componentHighlight', { uid: targetNode.id })\n highlightComponentTimeout = setTimeout(() => {\n callVueDevtoolsHook('componentUnhighlight')\n }, COMPONENT_HIGHLIGHT_DURATION)\n },\n getRouterInfo(query) {\n getRpc().onRouterInfoUpdated(\n query.event,\n JSON.stringify(devtoolsRouterInfo, null, 2)\n )\n },\n onRouterInfoUpdated: () => undefined,\n async getPiniaTree(query) {\n const inspectorTree = await withPiniaHighPerfDisabled(() =>\n devtools.api.getInspectorTree({\n inspectorId: PINIA_INSPECTOR_ID,\n filter: ''\n })\n )\n getRpc().onPiniaTreeUpdated(query.event, inspectorTree)\n },\n onPiniaTreeUpdated: () => undefined,\n async getPiniaState(query) {\n const result = await withPiniaHighPerfDisabled(async () => {\n const payload = {\n inspectorId: PINIA_INSPECTOR_ID,\n nodeId: query.storeName\n }\n const inspector = getInspector(payload.inspectorId)\n\n if (inspector) {\n inspector.selectedNodeId = payload.nodeId\n }\n\n return devtools.ctx.api.getInspectorState(payload)\n })\n getRpc().onPiniaInfoUpdated(query.event, stringify(result))\n },\n onPiniaInfoUpdated: () => undefined\n }\n}\n\n/**\n * Vue DevTools editInspectorState 需要的 set 回调。\n *\n * 运行时 bridge 只负责把 MCP 请求转交给 DevTools API,实际状态写入由 DevTools 内部完成;\n * 这里提供保守赋值实现,保证新版类型要求满足且不引入额外依赖。\n */\nfunction setStateValue(\n object: unknown,\n path?: string | string[],\n value?: unknown\n): void {\n if (!object || typeof object !== 'object' || !path) {\n return\n }\n\n const keys = Array.isArray(path) ? path : [path]\n const lastKey = keys.at(-1)\n\n if (!lastKey) {\n return\n }\n\n ;(object as Record<string, unknown>)[lastKey] = value\n}\n\n/**\n * 按 MCP 输入类型解析组件状态值。\n *\n * DevTools Kit 当前只接收最终 value,不再接收旧版本的 type 字段,\n * 因此这里在 bridge 内部完成基础类型转换。\n */\nfunction parseStateValue(value: string, valueType: string): unknown {\n if (valueType === 'number') {\n return Number(value)\n }\n\n if (valueType === 'boolean') {\n return value === 'true'\n }\n\n if (valueType === 'object' || valueType === 'array') {\n try {\n return JSON.parse(value) as unknown\n } catch {\n return value\n }\n }\n\n return value\n}\n\n/**\n * 调用 Vue DevTools 内部 hook。\n *\n * 当前 @vue/devtools-kit 的公开类型没有覆盖组件高亮 hook,但运行时仍提供该能力;\n * 单独收敛类型逃逸可以避免把不稳定内部事件扩散到业务逻辑。\n */\nfunction callVueDevtoolsHook(name: string, payload?: unknown): void {\n const hooks = devtools.ctx.hooks as {\n callHook: (event: string, payload?: unknown) => void\n }\n hooks.callHook(name, payload)\n}\n\n/**\n * 查找组件节点。\n *\n * 组件名来自 MCP 输入,运行时必须处理找不到组件的情况,而不是直接访问 undefined.id。\n */\nasync function findComponentNode(\n componentName: string\n): Promise<{ id: string; name?: string; children?: unknown[] } | undefined> {\n const inspectorTree = await devtools.api.getInspectorTree({\n inspectorId: COMPONENTS_INSPECTOR_ID,\n filter: ''\n })\n const nodes = flattenTree(inspectorTree[0])\n\n return nodes.find((node) => node.name === componentName)\n}\n\n/**\n * 展平 Vue inspector tree。\n *\n * Vue DevTools 返回树状结构,按组件名查找状态和高亮目标时需要递归展开。\n */\nfunction flattenTree(\n root: unknown\n): Array<{ id: string; name?: string; children?: unknown[] }> {\n const result: Array<{ id: string; name?: string; children?: unknown[] }> = []\n\n const traverse = (node: unknown): void => {\n if (!isInspectorNode(node)) {\n return\n }\n\n result.push(node)\n node.children?.forEach((child) => {\n traverse(child)\n })\n }\n\n traverse(root)\n return result\n}\n\n/**\n * 校验 Vue inspector 节点的最小结构。\n *\n * DevTools 数据结构可能随版本变化,使用窄类型保护可以降低运行时异常风险。\n */\nfunction isInspectorNode(\n node: unknown\n): node is { id: string; name?: string; children?: unknown[] } {\n return Boolean(\n node &&\n typeof node === 'object' &&\n typeof (node as { id?: unknown }).id === 'string'\n )\n}\n\n/**\n * 临时关闭 Pinia high perf mode 后执行读取。\n *\n * Pinia inspector 在高性能模式下可能不返回完整状态,读取后恢复原状态可以避免影响用户调试体验。\n */\nasync function withPiniaHighPerfDisabled<T>(\n callback: () => Promise<T>\n): Promise<T> {\n const highPerfModeEnabled = devtoolsState.highPerfModeEnabled\n\n if (highPerfModeEnabled) {\n toggleHighPerfMode(false)\n }\n\n try {\n return await callback()\n } finally {\n if (highPerfModeEnabled) {\n toggleHighPerfMode(true)\n }\n }\n}\n\n/**\n * 创建组件缺失错误。\n *\n * 返回结构化错误比抛异常更适合 MCP 场景,AI 可以直接把原因反馈给用户。\n */\nfunction createMissingComponentError(componentName: string): {\n ok: false\n error: string\n} {\n return {\n ok: false,\n error: `component not found: ${componentName}`\n }\n}\n","import { truncateText } from '../shared/sanitize'\nimport type { DomOptions } from '../types'\n\n/**\n * DOM 节点快照。\n *\n * MCP 不应该返回真实 DOM 节点对象,而应该返回可序列化结构,便于 AI 理解和传输。\n */\nexport interface DomNodeSnapshot {\n /** 节点标签名,文本节点使用 `#text`。 */\n readonly tag: string\n /** 节点属性,敏感字段会被脱敏。 */\n readonly attrs?: Record<string, string>\n /** 节点文本,按配置截断。 */\n readonly text?: string\n /** 子节点,受最大深度和最大节点数限制。 */\n readonly children?: DomNodeSnapshot[]\n}\n\n/**\n * selector 查询结果。\n *\n * 查询工具只返回定位所需的摘要信息,避免把完整节点对象暴露给 MCP 客户端。\n */\nexport interface DomElementQueryResult {\n /** 元素标签名,用于快速判断命中的节点类型。 */\n readonly tag: string\n /** 元素聚合文本,用于 AI 判断该节点是否是目标控件。 */\n readonly text: string\n /** 元素属性快照,敏感字段会被脱敏。 */\n readonly attrs: Record<string, string>\n /** 元素布局矩形,便于后续判断可见区域和点击位置。 */\n readonly rect: Record<string, number>\n}\n\n/**\n * 创建裁剪后的 DOM 快照。\n *\n * DOM 输出必须裁剪,因为 MCP 上下文有限,大页面直接返回会导致 AI 无法消费。\n */\nexport function createDomSnapshot(\n root: Element,\n options: Required<DomOptions>\n): DomNodeSnapshot {\n let count = 0\n\n /**\n * 递归访问 DOM 节点。\n *\n * 将递归放在闭包内可以共享节点计数,确保 maxNodes 是整棵树的全局限制。\n */\n function visit(node: Node, depth: number): DomNodeSnapshot | null {\n if (count >= options.maxNodes || depth > options.maxDepth) {\n return null\n }\n\n if (node.nodeType === Node.TEXT_NODE) {\n return createTextSnapshot(node, options, () => {\n count += 1\n })\n }\n\n if (!(node instanceof Element)) {\n return null\n }\n\n const tag = node.tagName.toLowerCase()\n\n if (['script', 'style', 'noscript'].includes(tag)) {\n return null\n }\n\n count += 1\n\n return createElementSnapshot(node, tag, (child) => visit(child, depth + 1))\n }\n\n return visit(root, 0) ?? { tag: root.tagName.toLowerCase() }\n}\n\n/**\n * 查询 DOM 元素摘要。\n *\n * selector 查询用于让 AI 快速定位关键元素,不需要返回整棵 DOM。\n */\nexport function queryDomElements(\n selector: string,\n limit: number\n): DomElementQueryResult[] {\n return Array.from(document.querySelectorAll(selector))\n .slice(0, limit)\n .map((element) => ({\n tag: element.tagName.toLowerCase(),\n text: element.textContent.trim(),\n attrs: collectAttrs(element),\n rect: serializeRect(element.getBoundingClientRect())\n }))\n}\n\n/**\n * 创建文本节点快照。\n *\n * 空白文本在调试时通常是布局噪声,过滤它们可以让 AI 更专注于真实内容。\n */\nfunction createTextSnapshot(\n node: Node,\n options: Required<DomOptions>,\n markVisited: () => void\n): DomNodeSnapshot | null {\n const text = node.textContent?.trim()\n\n if (!text) {\n return null\n }\n\n markVisited()\n\n return { tag: '#text', text: truncateText(text, options.maxTextLength).text }\n}\n\n/**\n * 创建元素节点快照。\n *\n * 属性和子节点拆开处理,是为了后续可以单独扩展属性脱敏或节点过滤策略。\n */\nfunction createElementSnapshot(\n node: Element,\n tag: string,\n visitChild: (child: Node) => DomNodeSnapshot | null\n): DomNodeSnapshot {\n const attrs = collectAttrs(node)\n const children = Array.from(node.childNodes)\n .map((child) => visitChild(child))\n .filter((child): child is DomNodeSnapshot => Boolean(child))\n\n return {\n tag,\n ...(Object.keys(attrs).length ? { attrs } : {}),\n ...(children.length ? { children } : {})\n }\n}\n\n/**\n * 收集元素属性并隐藏敏感值。\n *\n * 密码输入框的 value 不能泄露给 MCP 客户端,即使它只在本地开发环境使用。\n */\nfunction collectAttrs(element: Element): Record<string, string> {\n const attrs: Record<string, string> = {}\n\n for (const attr of Array.from(element.attributes)) {\n attrs[attr.name] = attr.value\n }\n\n if (element instanceof HTMLInputElement && element.type === 'password') {\n attrs.value = '[masked]'\n }\n\n return attrs\n}\n\n/**\n * 序列化 DOMRect。\n *\n * 浏览器返回的 DOMRect 不是普通 JSON 对象,显式挑选字段可以让 MCP 输出稳定且可测试。\n */\nfunction serializeRect(rect: DOMRect): Record<string, number> {\n return {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height,\n top: rect.top,\n right: rect.right,\n bottom: rect.bottom,\n left: rect.left\n }\n}\n","/**\n * 运行时脚本执行请求。\n *\n * Hook fallback 仅支持表达式风格执行,复杂语句执行优先交给 CDP adapter。\n */\nexport interface RuntimeEvaluateRequest {\n /** 需要执行的表达式。 */\n readonly expression: string\n /** 是否等待 Promise 结果,默认由调用方决定。 */\n readonly awaitPromise?: boolean\n /** 执行超时时间,避免页面长任务阻塞调试链路。 */\n readonly timeoutMs: number\n}\n\n/**\n * 执行表达式风格脚本。\n *\n * 使用 Function 构造器而不是直接 eval,可以明确限定为表达式返回值;\n * 语句级调试留给 CDP Runtime.evaluate,以减少 Hook fallback 的行为边界。\n */\nexport async function evaluateExpression(\n request: RuntimeEvaluateRequest\n): Promise<unknown> {\n const value = runExpression(request.expression)\n const result =\n request.awaitPromise === false ? value : await Promise.resolve(value)\n\n return Promise.race([\n Promise.resolve(result),\n createTimeout(request.timeoutMs)\n ])\n}\n\n/**\n * 执行表达式并返回结果。\n *\n * 这里必须动态执行用户传入表达式,但 MCP 工具默认关闭该能力,只有用户显式配置\n * `runtime.evaluate.enabled` 后才会暴露入口;因此把例外集中在该函数,便于后续安全审查。\n */\nfunction runExpression(expression: string): unknown {\n // eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call -- evaluate_script 的职责就是执行显式授权后的调试表达式。\n return new Function(`return (${expression})`)() as unknown\n}\n\n/**\n * 创建执行超时 Promise。\n *\n * 控制台执行必须有硬边界,否则 MCP 调用可能被页面内长任务永久挂起。\n */\nfunction createTimeout(timeoutMs: number): Promise<never> {\n return new Promise((_, reject) => {\n window.setTimeout(() => {\n reject(\n new Error(`evaluate_script timed out after ${String(timeoutMs)}ms`)\n )\n }, timeoutMs)\n })\n}\n","import type { VueRuntimeRpc } from '../types'\nimport { createDomSnapshot, queryDomElements } from './domSnapshot'\nimport { evaluateExpression } from './evaluateExpression'\n\n/**\n * 创建通用 Runtime DevTools RPC。\n *\n * 这些能力是 CDP 不可用时的 Hook fallback,和 Vue 专属能力放在同一条 Vite RPC 通道里,\n * 可以避免再维护第二套浏览器到服务端的请求协议。\n */\nexport function createRuntimeDevtoolsRpc(\n getRpc: () => VueRuntimeRpc\n): Pick<\n VueRuntimeRpc,\n | 'getDomTree'\n | 'onDomTreeUpdated'\n | 'queryDom'\n | 'onDomQueryUpdated'\n | 'evaluateScript'\n | 'onEvaluateScriptUpdated'\n> {\n return {\n getDomTree(options) {\n getRpc().onDomTreeUpdated(\n options.event,\n createDomSnapshot(document.documentElement, {\n maxDepth: options.maxDepth,\n maxNodes: options.maxNodes,\n maxTextLength: options.maxTextLength\n })\n )\n },\n onDomTreeUpdated: () => undefined,\n queryDom(options) {\n getRpc().onDomQueryUpdated(\n options.event,\n queryDomElements(options.selector, options.limit)\n )\n },\n onDomQueryUpdated: () => undefined,\n async evaluateScript(options) {\n try {\n getRpc().onEvaluateScriptUpdated(options.event, {\n ok: true,\n value: await evaluateExpression(options)\n })\n } catch (error) {\n getRpc().onEvaluateScriptUpdated(options.event, {\n ok: false,\n error: error instanceof Error ? error.message : String(error)\n })\n }\n },\n onEvaluateScriptUpdated: () => undefined\n }\n}\n"],"mappings":"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","names":["import_nanoid","import_nanoid"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 运行时脚本执行请求。
|
|
3
|
+
*
|
|
4
|
+
* Hook fallback 仅支持表达式风格执行,复杂语句执行优先交给 CDP adapter。
|
|
5
|
+
*/
|
|
6
|
+
interface RuntimeEvaluateRequest {
|
|
7
|
+
/** 需要执行的表达式。 */
|
|
8
|
+
readonly expression: string;
|
|
9
|
+
/** 是否等待 Promise 结果,默认由调用方决定。 */
|
|
10
|
+
readonly awaitPromise?: boolean;
|
|
11
|
+
/** 执行超时时间,避免页面长任务阻塞调试链路。 */
|
|
12
|
+
readonly timeoutMs: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 执行表达式风格脚本。
|
|
16
|
+
*
|
|
17
|
+
* 使用 Function 构造器而不是直接 eval,可以明确限定为表达式返回值;
|
|
18
|
+
* 语句级调试留给 CDP Runtime.evaluate,以减少 Hook fallback 的行为边界。
|
|
19
|
+
*/
|
|
20
|
+
declare function evaluateExpression(request: RuntimeEvaluateRequest): Promise<unknown>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 启动浏览器端 Runtime Bridge。
|
|
24
|
+
*
|
|
25
|
+
* 运行时脚本负责连接 Vite WebSocket 并上报页面身份;Vue、Console、Network 等子能力
|
|
26
|
+
* 会在后续任务中挂到这个启动流程中。
|
|
27
|
+
*/
|
|
28
|
+
declare function startRuntimeClient(): Promise<void>;
|
|
29
|
+
|
|
30
|
+
export { type RuntimeEvaluateRequest, evaluateExpression, startRuntimeClient };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 运行时脚本执行请求。
|
|
3
|
+
*
|
|
4
|
+
* Hook fallback 仅支持表达式风格执行,复杂语句执行优先交给 CDP adapter。
|
|
5
|
+
*/
|
|
6
|
+
interface RuntimeEvaluateRequest {
|
|
7
|
+
/** 需要执行的表达式。 */
|
|
8
|
+
readonly expression: string;
|
|
9
|
+
/** 是否等待 Promise 结果,默认由调用方决定。 */
|
|
10
|
+
readonly awaitPromise?: boolean;
|
|
11
|
+
/** 执行超时时间,避免页面长任务阻塞调试链路。 */
|
|
12
|
+
readonly timeoutMs: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 执行表达式风格脚本。
|
|
16
|
+
*
|
|
17
|
+
* 使用 Function 构造器而不是直接 eval,可以明确限定为表达式返回值;
|
|
18
|
+
* 语句级调试留给 CDP Runtime.evaluate,以减少 Hook fallback 的行为边界。
|
|
19
|
+
*/
|
|
20
|
+
declare function evaluateExpression(request: RuntimeEvaluateRequest): Promise<unknown>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 启动浏览器端 Runtime Bridge。
|
|
24
|
+
*
|
|
25
|
+
* 运行时脚本负责连接 Vite WebSocket 并上报页面身份;Vue、Console、Network 等子能力
|
|
26
|
+
* 会在后续任务中挂到这个启动流程中。
|
|
27
|
+
*/
|
|
28
|
+
declare function startRuntimeClient(): Promise<void>;
|
|
29
|
+
|
|
30
|
+
export { type RuntimeEvaluateRequest, evaluateExpression, startRuntimeClient };
|