@xiaou66/vite-plugin-vue-mcp-next 0.0.2 → 0.0.5

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.
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/runtime/client.ts
@@ -423,6 +433,125 @@ function createTimeout(timeoutMs) {
423
433
  });
424
434
  }
425
435
 
436
+ // src/runtime/screenshot.ts
437
+ async function takeRuntimeScreenshot(options) {
438
+ const target = resolveScreenshotTarget(options.target, options.selector);
439
+ if (!target.ok) {
440
+ return target;
441
+ }
442
+ const loadSnapdom = options.loadSnapdom ?? loadDefaultSnapdom;
443
+ const { snapdom } = await loadSnapdom();
444
+ const snapdomOptions = await createSnapdomOptions(options);
445
+ const capture = await snapdom(target.element, snapdomOptions);
446
+ const blob = await capture.toBlob({
447
+ type: createMimeType(options.format),
448
+ quality: options.quality
449
+ });
450
+ const data = await blobToBase64(blob);
451
+ const rect = target.element.getBoundingClientRect();
452
+ return {
453
+ ok: true,
454
+ source: "snapdom",
455
+ data,
456
+ width: rect.width,
457
+ height: rect.height,
458
+ mimeType: createMimeType(options.format),
459
+ byteLength: blob.size,
460
+ limitations: [
461
+ "snapdom renders DOM to an image and may differ from browser pixels",
462
+ "cross-origin images, iframe content, video, WebGL, and complex CSS may be incomplete"
463
+ ]
464
+ };
465
+ }
466
+ async function loadDefaultSnapdom() {
467
+ return import("@zumer/snapdom");
468
+ }
469
+ function resolveScreenshotTarget(target, selector) {
470
+ if (target === "element" && !selector) {
471
+ return { ok: false, error: "selector is required when target is element" };
472
+ }
473
+ if (target === "element") {
474
+ if (!selector) {
475
+ return { ok: false, error: "selector is required when target is element" };
476
+ }
477
+ const elementSelector = selector;
478
+ const element = document.querySelector(elementSelector);
479
+ return element ? { ok: true, element } : { ok: false, error: `element not found: ${elementSelector}` };
480
+ }
481
+ return { ok: true, element: document.documentElement };
482
+ }
483
+ async function createSnapdomOptions(options) {
484
+ const snapdomOptions = {
485
+ ...options.snapdom.options,
486
+ quality: options.quality ?? options.snapdom.options.quality,
487
+ scale: options.scale ?? options.snapdom.options.scale,
488
+ plugins: await loadSnapdomPlugins(options)
489
+ };
490
+ const loadModule = createModuleLoader(options);
491
+ if (options.snapdom.filter) {
492
+ snapdomOptions.filter = await loadDefaultExport(
493
+ loadModule,
494
+ options.snapdom.filter
495
+ );
496
+ }
497
+ if (options.snapdom.fallbackURL) {
498
+ snapdomOptions.fallbackURL = await loadDefaultExport(
499
+ loadModule,
500
+ options.snapdom.fallbackURL
501
+ );
502
+ }
503
+ return removeUndefinedEntries(snapdomOptions);
504
+ }
505
+ async function loadSnapdomPlugins(options) {
506
+ const loadModule = createModuleLoader(options);
507
+ return Promise.all(
508
+ options.snapdom.plugins.map((plugin) => loadPluginImport(plugin, loadModule))
509
+ );
510
+ }
511
+ function createModuleLoader(options) {
512
+ return options.loadModule ?? loadConfiguredModule;
513
+ }
514
+ async function loadConfiguredModule(path) {
515
+ const config = await import("virtual:vite-plugin-vue-mcp-next/screenshot-config");
516
+ const registry = config.screenshotModuleRegistry;
517
+ const mod = registry[path];
518
+ if (!mod) {
519
+ throw new Error(`screenshot module is not registered: ${path}`);
520
+ }
521
+ return mod;
522
+ }
523
+ async function loadPluginImport(plugin, loadModule) {
524
+ const normalized = typeof plugin === "string" ? { path: plugin, exportName: "default" } : { exportName: "default", ...plugin };
525
+ const mod = await loadModule(normalized.path);
526
+ const exported = mod[normalized.exportName];
527
+ if (isPluginFactory(exported) && "options" in normalized) {
528
+ return exported(normalized.options);
529
+ }
530
+ return exported;
531
+ }
532
+ function isPluginFactory(value) {
533
+ return typeof value === "function";
534
+ }
535
+ async function loadDefaultExport(loadModule, path) {
536
+ return (await loadModule(path)).default;
537
+ }
538
+ function createMimeType(format) {
539
+ return `image/${format}`;
540
+ }
541
+ async function blobToBase64(blob) {
542
+ const bytes = new Uint8Array(await blob.arrayBuffer());
543
+ let binary = "";
544
+ for (const byte of bytes) {
545
+ binary += String.fromCharCode(byte);
546
+ }
547
+ return btoa(binary);
548
+ }
549
+ function removeUndefinedEntries(value) {
550
+ return Object.fromEntries(
551
+ Object.entries(value).filter(([, item]) => item !== void 0)
552
+ );
553
+ }
554
+
426
555
  // src/runtime/devtoolsBridge.ts
427
556
  function createRuntimeDevtoolsRpc(getRpc) {
428
557
  return {
@@ -457,7 +586,21 @@ function createRuntimeDevtoolsRpc(getRpc) {
457
586
  });
458
587
  }
459
588
  },
460
- onEvaluateScriptUpdated: () => void 0
589
+ onEvaluateScriptUpdated: () => void 0,
590
+ async takeScreenshot(options) {
591
+ try {
592
+ getRpc().onScreenshotTaken(
593
+ options.event,
594
+ await takeRuntimeScreenshot(options)
595
+ );
596
+ } catch (error) {
597
+ getRpc().onScreenshotTaken(options.event, {
598
+ ok: false,
599
+ error: error instanceof Error ? error.message : String(error)
600
+ });
601
+ }
602
+ },
603
+ onScreenshotTaken: () => void 0
461
604
  };
462
605
  }
463
606
 
@@ -1 +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"]}
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/screenshot.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","/**\n * 浏览器端 snapdom 截图降级能力。\n *\n * 该文件只在页面 runtime 中执行,适用于没有 CDP 但仍希望让 MCP 客户端看到页面视觉结果的场景;\n * 函数型扩展通过 Vite import 路径加载,避免 Node 侧配置直接跨运行时传递函数。\n */\nimport type {\n RuntimeScreenshotResult,\n ScreenshotFormat,\n ScreenshotTarget,\n SnapdomPluginImport,\n SnapdomScreenshotOptions\n} from '../types'\n\n/**\n * Runtime 截图参数。\n *\n * loader 只用于测试替换和 Vite 动态 import 边界;正常业务调用只需要传截图目标和 snapdom 配置。\n */\nexport interface RuntimeScreenshotOptions {\n /** 截图目标范围,用于选择 documentElement 或 selector 元素。 */\n readonly target: ScreenshotTarget\n /** 元素截图选择器,只在 `target: \"element\"` 时使用。 */\n readonly selector?: string\n /** 输出格式,决定 Blob mime type 和压缩策略。 */\n readonly format: ScreenshotFormat\n /** 有损格式质量,适用于 jpeg/webp 控制响应体积。 */\n readonly quality?: number\n /** 单次调用缩放倍率覆盖值,适合临时请求高清局部截图。 */\n readonly scale?: number\n /** 已解析 snapdom 配置,函数型配置仍以 Vite import 路径表达。 */\n readonly snapdom: Required<\n Pick<SnapdomScreenshotOptions, 'options' | 'plugins'>\n > &\n Omit<SnapdomScreenshotOptions, 'options' | 'plugins'>\n /** 测试或特殊运行环境替换 snapdom 加载方式时使用。 */\n readonly loadSnapdom?: () => Promise<{ snapdom: SnapdomFunction }>\n /** 测试或特殊运行环境替换 Vite 动态 import 时使用。 */\n readonly loadModule?: (path: string) => Promise<Record<string, unknown>>\n}\n\n/**\n * snapdom 函数边界。\n *\n * 只声明当前截图流程需要的最小能力,可以让本插件不绑定 snapdom 的完整内部类型。\n */\ntype SnapdomFunction = (\n element: Element,\n options: Record<string, unknown>\n) => Promise<{ toBlob: (options?: Record<string, unknown>) => Promise<Blob> }>\n\n/**\n * 执行 runtime DOM 截图。\n *\n * snapdom 截图是 CDP 不可用时的兼容路径,因此返回结果必须带 `source: \"snapdom\"` 和限制说明。\n */\nexport async function takeRuntimeScreenshot(\n options: RuntimeScreenshotOptions\n): Promise<RuntimeScreenshotResult & { source?: 'snapdom' }> {\n const target = resolveScreenshotTarget(options.target, options.selector)\n\n if (!target.ok) {\n return target\n }\n\n const loadSnapdom = options.loadSnapdom ?? loadDefaultSnapdom\n const { snapdom } = await loadSnapdom()\n const snapdomOptions = await createSnapdomOptions(options)\n const capture = await snapdom(target.element, snapdomOptions)\n const blob = await capture.toBlob({\n type: createMimeType(options.format),\n quality: options.quality\n })\n const data = await blobToBase64(blob)\n const rect = target.element.getBoundingClientRect()\n\n return {\n ok: true,\n source: 'snapdom',\n data,\n width: rect.width,\n height: rect.height,\n mimeType: createMimeType(options.format),\n byteLength: blob.size,\n limitations: [\n 'snapdom renders DOM to an image and may differ from browser pixels',\n 'cross-origin images, iframe content, video, WebGL, and complex CSS may be incomplete'\n ]\n }\n}\n\n/**\n * 加载默认 snapdom 实现。\n *\n * 动态 import 保留在浏览器端,适合 Vite dev server 按需解析依赖并减少初始 runtime 成本。\n */\nasync function loadDefaultSnapdom(): Promise<{ snapdom: SnapdomFunction }> {\n return import('@zumer/snapdom')\n}\n\n/**\n * 解析截图目标元素。\n *\n * runtime 降级截图只能读取页面自身 DOM;在边界处返回明确错误可以让 MCP 工具解释失败原因。\n */\nfunction resolveScreenshotTarget(\n target: ScreenshotTarget,\n selector?: string\n):\n | { ok: true; element: Element }\n | { ok: false; error: string } {\n if (target === 'element' && !selector) {\n return { ok: false, error: 'selector is required when target is element' }\n }\n\n if (target === 'element') {\n if (!selector) {\n return { ok: false, error: 'selector is required when target is element' }\n }\n\n const elementSelector = selector\n const element = document.querySelector(elementSelector)\n\n return element\n ? { ok: true, element }\n : { ok: false, error: `element not found: ${elementSelector}` }\n }\n\n return { ok: true, element: document.documentElement }\n}\n\n/**\n * 组装 snapdom options。\n *\n * 项目级配置和单次调用配置需要在浏览器端合并,因为插件、filter、fallbackURL 都依赖 Vite import。\n */\nasync function createSnapdomOptions(\n options: RuntimeScreenshotOptions\n): Promise<Record<string, unknown>> {\n const snapdomOptions: Record<string, unknown> = {\n ...options.snapdom.options,\n quality: options.quality ?? options.snapdom.options.quality,\n scale: options.scale ?? options.snapdom.options.scale,\n plugins: await loadSnapdomPlugins(options)\n }\n const loadModule = createModuleLoader(options)\n\n if (options.snapdom.filter) {\n snapdomOptions.filter = await loadDefaultExport(\n loadModule,\n options.snapdom.filter\n )\n }\n\n if (options.snapdom.fallbackURL) {\n snapdomOptions.fallbackURL = await loadDefaultExport(\n loadModule,\n options.snapdom.fallbackURL\n )\n }\n\n return removeUndefinedEntries(snapdomOptions)\n}\n\n/**\n * 加载 snapdom 插件。\n *\n * 插件只能通过 Vite import 路径进入浏览器 runtime,适用于用户插件依赖源码别名或 Vite transform 的场景。\n */\nasync function loadSnapdomPlugins(\n options: RuntimeScreenshotOptions\n): Promise<unknown[]> {\n const loadModule = createModuleLoader(options)\n\n return Promise.all(\n options.snapdom.plugins.map((plugin) => loadPluginImport(plugin, loadModule))\n )\n}\n\n/**\n * 创建模块加载器。\n *\n * 独立函数可以让测试注入 fake loader,同时真实运行时保留 Vite 对动态 import 的处理。\n */\nfunction createModuleLoader(\n options: RuntimeScreenshotOptions\n): (path: string) => Promise<Record<string, unknown>> {\n return options.loadModule ?? loadConfiguredModule\n}\n\n/**\n * 从虚拟模块读取用户配置模块。\n *\n * Vite import 路径需要经过 Vite 解析,不能交给浏览器原生动态 import;虚拟模块用静态 import 保留 alias 能力。\n */\nasync function loadConfiguredModule(\n path: string\n): Promise<Record<string, unknown>> {\n const config = await import('virtual:vite-plugin-vue-mcp-next/screenshot-config')\n const registry = config.screenshotModuleRegistry as Partial<\n Record<string, Record<string, unknown>>\n >\n const mod = registry[path]\n\n if (!mod) {\n throw new Error(`screenshot module is not registered: ${path}`)\n }\n\n return mod\n}\n\n/**\n * 加载单个插件声明。\n *\n * 插件模块可能直接导出插件对象,也可能导出插件工厂;这里按是否提供 options 决定是否调用工厂。\n */\nasync function loadPluginImport(\n plugin: SnapdomPluginImport,\n loadModule: (path: string) => Promise<Record<string, unknown>>\n): Promise<unknown> {\n const normalized =\n typeof plugin === 'string'\n ? { path: plugin, exportName: 'default' }\n : { exportName: 'default', ...plugin }\n const mod = await loadModule(normalized.path)\n const exported = mod[normalized.exportName]\n\n if (isPluginFactory(exported) && 'options' in normalized) {\n return exported(normalized.options)\n }\n\n return exported\n}\n\n/**\n * 判断导出值是否是插件工厂。\n *\n * 动态 import 的模块导出是 unknown,先收窄再调用可以避免把任意值当函数执行。\n */\nfunction isPluginFactory(value: unknown): value is (options: unknown) => unknown {\n return typeof value === 'function'\n}\n\n/**\n * 加载默认导出函数。\n *\n * filter 和 fallbackURL 是 snapdom 原生函数型扩展,路径化加载能保留函数能力但不跨运行时序列化函数。\n */\nasync function loadDefaultExport(\n loadModule: (path: string) => Promise<Record<string, unknown>>,\n path: string\n): Promise<unknown> {\n return (await loadModule(path)).default\n}\n\n/**\n * 生成图片 mime type。\n *\n * MCP 响应需要明确 mime type,方便客户端按格式解码 base64 图片。\n */\nfunction createMimeType(format: ScreenshotFormat): string {\n return `image/${format}`\n}\n\n/**\n * 将 Blob 转成 base64。\n *\n * runtime 运行在浏览器里不能依赖 Node Buffer,因此使用 ArrayBuffer 和 btoa 完成编码。\n */\nasync function blobToBase64(blob: Blob): Promise<string> {\n const bytes = new Uint8Array(await blob.arrayBuffer())\n let binary = ''\n\n for (const byte of bytes) {\n binary += String.fromCharCode(byte)\n }\n\n return btoa(binary)\n}\n\n/**\n * 删除 undefined 配置。\n *\n * snapdom options 里保留 undefined 没有语义,清理后更容易在测试和调试时确认真实传参。\n */\nfunction removeUndefinedEntries(\n value: Record<string, unknown>\n): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(value).filter(([, item]) => item !== undefined)\n )\n}\n","import type { VueRuntimeRpc } from '../types'\nimport { createDomSnapshot, queryDomElements } from './domSnapshot'\nimport { evaluateExpression } from './evaluateExpression'\nimport { takeRuntimeScreenshot } from './screenshot'\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 | 'takeScreenshot'\n | 'onScreenshotTaken'\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 async takeScreenshot(options) {\n try {\n getRpc().onScreenshotTaken(\n options.event,\n await takeRuntimeScreenshot(options)\n )\n } catch (error) {\n getRpc().onScreenshotTaken(options.event, {\n ok: false,\n error: error instanceof Error ? error.message : String(error)\n })\n }\n },\n onScreenshotTaken: () => undefined\n }\n}\n"],"mappings":"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","names":["import_nanoid","import_nanoid"]}
@@ -405,6 +405,125 @@ function createTimeout(timeoutMs) {
405
405
  });
406
406
  }
407
407
 
408
+ // src/runtime/screenshot.ts
409
+ async function takeRuntimeScreenshot(options) {
410
+ const target = resolveScreenshotTarget(options.target, options.selector);
411
+ if (!target.ok) {
412
+ return target;
413
+ }
414
+ const loadSnapdom = options.loadSnapdom ?? loadDefaultSnapdom;
415
+ const { snapdom } = await loadSnapdom();
416
+ const snapdomOptions = await createSnapdomOptions(options);
417
+ const capture = await snapdom(target.element, snapdomOptions);
418
+ const blob = await capture.toBlob({
419
+ type: createMimeType(options.format),
420
+ quality: options.quality
421
+ });
422
+ const data = await blobToBase64(blob);
423
+ const rect = target.element.getBoundingClientRect();
424
+ return {
425
+ ok: true,
426
+ source: "snapdom",
427
+ data,
428
+ width: rect.width,
429
+ height: rect.height,
430
+ mimeType: createMimeType(options.format),
431
+ byteLength: blob.size,
432
+ limitations: [
433
+ "snapdom renders DOM to an image and may differ from browser pixels",
434
+ "cross-origin images, iframe content, video, WebGL, and complex CSS may be incomplete"
435
+ ]
436
+ };
437
+ }
438
+ async function loadDefaultSnapdom() {
439
+ return import("@zumer/snapdom");
440
+ }
441
+ function resolveScreenshotTarget(target, selector) {
442
+ if (target === "element" && !selector) {
443
+ return { ok: false, error: "selector is required when target is element" };
444
+ }
445
+ if (target === "element") {
446
+ if (!selector) {
447
+ return { ok: false, error: "selector is required when target is element" };
448
+ }
449
+ const elementSelector = selector;
450
+ const element = document.querySelector(elementSelector);
451
+ return element ? { ok: true, element } : { ok: false, error: `element not found: ${elementSelector}` };
452
+ }
453
+ return { ok: true, element: document.documentElement };
454
+ }
455
+ async function createSnapdomOptions(options) {
456
+ const snapdomOptions = {
457
+ ...options.snapdom.options,
458
+ quality: options.quality ?? options.snapdom.options.quality,
459
+ scale: options.scale ?? options.snapdom.options.scale,
460
+ plugins: await loadSnapdomPlugins(options)
461
+ };
462
+ const loadModule = createModuleLoader(options);
463
+ if (options.snapdom.filter) {
464
+ snapdomOptions.filter = await loadDefaultExport(
465
+ loadModule,
466
+ options.snapdom.filter
467
+ );
468
+ }
469
+ if (options.snapdom.fallbackURL) {
470
+ snapdomOptions.fallbackURL = await loadDefaultExport(
471
+ loadModule,
472
+ options.snapdom.fallbackURL
473
+ );
474
+ }
475
+ return removeUndefinedEntries(snapdomOptions);
476
+ }
477
+ async function loadSnapdomPlugins(options) {
478
+ const loadModule = createModuleLoader(options);
479
+ return Promise.all(
480
+ options.snapdom.plugins.map((plugin) => loadPluginImport(plugin, loadModule))
481
+ );
482
+ }
483
+ function createModuleLoader(options) {
484
+ return options.loadModule ?? loadConfiguredModule;
485
+ }
486
+ async function loadConfiguredModule(path) {
487
+ const config = await import("virtual:vite-plugin-vue-mcp-next/screenshot-config");
488
+ const registry = config.screenshotModuleRegistry;
489
+ const mod = registry[path];
490
+ if (!mod) {
491
+ throw new Error(`screenshot module is not registered: ${path}`);
492
+ }
493
+ return mod;
494
+ }
495
+ async function loadPluginImport(plugin, loadModule) {
496
+ const normalized = typeof plugin === "string" ? { path: plugin, exportName: "default" } : { exportName: "default", ...plugin };
497
+ const mod = await loadModule(normalized.path);
498
+ const exported = mod[normalized.exportName];
499
+ if (isPluginFactory(exported) && "options" in normalized) {
500
+ return exported(normalized.options);
501
+ }
502
+ return exported;
503
+ }
504
+ function isPluginFactory(value) {
505
+ return typeof value === "function";
506
+ }
507
+ async function loadDefaultExport(loadModule, path) {
508
+ return (await loadModule(path)).default;
509
+ }
510
+ function createMimeType(format) {
511
+ return `image/${format}`;
512
+ }
513
+ async function blobToBase64(blob) {
514
+ const bytes = new Uint8Array(await blob.arrayBuffer());
515
+ let binary = "";
516
+ for (const byte of bytes) {
517
+ binary += String.fromCharCode(byte);
518
+ }
519
+ return btoa(binary);
520
+ }
521
+ function removeUndefinedEntries(value) {
522
+ return Object.fromEntries(
523
+ Object.entries(value).filter(([, item]) => item !== void 0)
524
+ );
525
+ }
526
+
408
527
  // src/runtime/devtoolsBridge.ts
409
528
  function createRuntimeDevtoolsRpc(getRpc) {
410
529
  return {
@@ -439,7 +558,21 @@ function createRuntimeDevtoolsRpc(getRpc) {
439
558
  });
440
559
  }
441
560
  },
442
- onEvaluateScriptUpdated: () => void 0
561
+ onEvaluateScriptUpdated: () => void 0,
562
+ async takeScreenshot(options) {
563
+ try {
564
+ getRpc().onScreenshotTaken(
565
+ options.event,
566
+ await takeRuntimeScreenshot(options)
567
+ );
568
+ } catch (error) {
569
+ getRpc().onScreenshotTaken(options.event, {
570
+ ok: false,
571
+ error: error instanceof Error ? error.message : String(error)
572
+ });
573
+ }
574
+ },
575
+ onScreenshotTaken: () => void 0
443
576
  };
444
577
  }
445
578