vite-plugin-aipanel 1.2.22 → 1.2.24

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/README.md CHANGED
@@ -10,25 +10,14 @@
10
10
  - Chrome / Edge / Arc / Brave 等 Chromium 内核浏览器
11
11
  - 任选其一:OpenCode CLI(默认)或 DeepSeek Harness (dsh) CLI
12
12
 
13
- ## 快速开始
14
-
15
- ### 1. 安装 AI 引擎
16
-
17
- **默认引擎 OpenCode CLI:**
18
-
19
- ```bash
20
- curl -fsSL https://opencode.ai/install | bash
21
- opencode --version # 验证
22
- ```
23
-
24
- > 使用 DeepSeek Harness 请改装:`npm i -g @deepseek-ai/dsh` + `npm i -D @aipanel/provider-deepseek`,并配置 `provider: "deepseek"`(见下)。
25
-
26
- ### 2. 安装并配置 Vite 插件
13
+ ## 安装
27
14
 
28
15
  ```bash
29
16
  npm install -D vite-plugin-aipanel
30
17
  ```
31
18
 
19
+ ## 快速开始
20
+
32
21
  ```ts
33
22
  // vite.config.ts
34
23
  import { defineConfig } from "vite";
@@ -39,90 +28,26 @@ export default defineConfig({
39
28
  });
40
29
  ```
41
30
 
42
- ### 3. 启动开发服务器
43
-
44
- ```bash
45
- npm run dev
46
- ```
47
-
48
- 插件自动完成:校验引擎 → 启动 AIPanel Web 服务(默认 5097)与代理(默认 6097,端口占用自动换)→ 复用/创建当前项目会话。
49
-
50
- ### 4. 安装浏览器扩展
51
-
52
- 1. [下载扩展包](https://github.com/code-farmer-i/vite-plugin-aipanel/raw/main/packages/extension/aipanel-assistant.zip)
53
- 2. Chrome 打开 `chrome://extensions/`,开启右上角**「开发者模式」**
54
- 3. 解压 `.zip` → **「加载已解压的扩展程序」** → 选择解压目录
55
- 4. 打开 `localhost` 开发页面,点击工具栏 **AIPanel 图标**(或 `Ctrl/Cmd + K`)唤起侧边栏
56
-
57
- > Edge / Arc / Brave 入口分别是 `edge://extensions/` / `arc://extensions/` / `brave://extensions/`。
31
+ 启动 `npm run dev` 后,插件自动完成:校验引擎 → 启动 AIPanel Web 服务(默认 5097)与代理(默认 6097,端口占用自动换)→ 复用/创建当前项目会话。
58
32
 
59
33
  ## 切换 AI 引擎
60
34
 
61
35
  插件默认 `provider: "default"`(等价 `"opencode"`,适配已内置,仅需安装 opencode CLI)。
62
36
 
63
- | provider | 引擎 | 额外依赖 | 说明 |
64
- | --------- | ---------------------- | ------------------------------------------ | --------------------------- |
65
- | `default` | OpenCode CLI | 无(适配内置;需另装 opencode CLI) | 默认值,同 `opencode` |
66
- | `opencode` | OpenCode CLI | 无(适配内置;需另装 opencode CLI) | 显式指定 OpenCode |
67
- | `deepseek` | DeepSeek Harness (dsh) | `@aipanel/provider-deepseek` + dsh CLI | DeepSeek 官方 Web 对话界面 |
68
-
69
- ```ts
70
- // 使用 DeepSeek Harness 引擎(先安装其依赖)
71
- aipanelAssistant({
72
- provider: "deepseek",
73
- providerOptions: {
74
- // home: "~/.dsh", // dsh 数据目录
75
- // agentPreset: "standard", // 新建会话的默认 Agent 预设
76
- // permissionPreset: "read-only", // 默认权限预设
77
- // busyEnter: "queue", // 繁忙时 Enter 行为
78
- },
79
- });
80
- ```
81
-
82
- > **注意**:插件**不会自动探测**已安装的引擎,引擎由 `provider` 字段决定;切换前请确认对应 CLI 已安装。
37
+ | provider | 引擎 | 额外依赖 |
38
+ | ----------- | ---------------------- | ---------------------------------------- |
39
+ | `default` | OpenCode CLI | 无(适配内置;需另装 opencode CLI) |
40
+ | `opencode` | OpenCode CLI | 无(适配内置;需另装 opencode CLI) |
41
+ | `deepseek` | DeepSeek Harness (dsh) | `@aipanel/provider-deepseek` + dsh CLI |
83
42
 
84
43
  ## 纯净 MCP 模式
85
44
 
86
- `mcpOnly: true` 时,插件只暴露 MCP 工具服务(Chrome DevTools 控制、Vue DevTools、日志读取等),不启动 AI 引擎、不注入对话界面,适合作为独立 MCP server 供外部 Agent 消费:
87
-
88
- ```ts
89
- aipanelAssistant({ mcpOnly: true });
90
- ```
91
-
92
- 端点挂在 Vite dev server 上(需保持 `vite dev` 运行),外部 MCP 客户端按 Streamable HTTP 配置:
93
-
94
- ```json
95
- {
96
- "mcpServers": {
97
- "aipanel": {
98
- "type": "http",
99
- "url": "http://localhost:5173/__aipanel_mcp__"
100
- }
101
- }
102
- }
103
- ```
104
-
105
- > 端口随 Vite dev server 变化(默认 5173),以启动日志中的 `MCP endpoint` 为准;`Claude Code` / `Cursor` 等客户端配置详见在线文档。
106
-
107
- ## 工作原理
108
-
109
- ```
110
- ┌────────────────┐ ┌────────────────┐ ┌─────────────┐
111
- │ Vite 插件 │ │ 浏览器扩展 │ │ OpenCode/dsh │
112
- │ 启动 Web 服务 │◀────▶│ 侧边栏面板 │◀────▶│ AI 引擎 │
113
- │ 代理跨域 │ │ 页面上下文同步 │ │ 会话管理 │
114
- └────────────────┘ └────────────────┘ └─────────────┘
115
- ```
116
-
117
- - Vite 插件在项目启动时自动拉起 AIPanel Web 服务
118
- - 浏览器扩展检测到 `localhost` 页面后自动连接该服务
119
- - 实时同步页面 URL、标题等上下文给 AI
120
- - 多标签页自动切换对应项目的会话
45
+ `mcpOnly: true` 时插件只暴露 MCP 工具服务(Chrome DevTools 控制、Vue DevTools、日志读取等),不启动 AI 引擎、不注入对话界面,适合作为独立 MCP server 供外部 Agent 消费。
121
46
 
122
47
  ## 文档
123
48
 
124
- 完整使用指南请访问 [在线文档](https://code-farmer-i.github.io/vite-plugin-aipanel/)。
49
+ 完整使用指南(浏览器扩展安装、MCP 客户端配置等)请访问 [在线文档](https://code-farmer-i.github.io/vite-plugin-aipanel/),仓库见 [code-farmer-i/vite-plugin-aipanel](https://github.com/code-farmer-i/vite-plugin-aipanel)
125
50
 
126
51
  ## License
127
52
 
128
- MIT
53
+ MIT
@@ -5,9 +5,17 @@
5
5
  * (schema/描述运行时同步自官方 tools/list,见 official-tools.ts)+ 本项目自定义工具。
6
6
  * 工具层限制(白名单、必填 pageId、仅项目页)由本文件 + endpoints/mcp.ts 的 tools/call 共同执行。
7
7
  */
8
+ import { type VueDevtoolsAction } from "@aipanel/core";
8
9
  /** 模型可见名前缀:官方短名 → chrome-devtools_<name> */
9
10
  export declare const MCP_PREFIX = "chrome-devtools_";
10
11
  export declare function displayToolName(short: string): string;
12
+ /** 本地桥接工具族前缀(tools/list 暴露面与 tools/call 路由共用) */
13
+ export declare const VUE_DEVTOOLS_PREFIX = "vue-devtools_";
14
+ export declare const LOGS_DEVTOOLS_PREFIX = "logs-devtools_";
15
+ /** Vite 进程日志工具名(工具定义与调用路由共用) */
16
+ export declare const VITE_LOGS_TOOL_NAME = "logs-devtools_vite_logs";
17
+ /** 服务日志文件工具名(按 LogFileConfig.name 生成,暴露与路由共用) */
18
+ export declare function serviceLogToolName(name: string): string;
11
19
  export interface CustomTool {
12
20
  name: string;
13
21
  description: string;
@@ -44,6 +52,20 @@ export declare function withPageIdSchema(schema: CustomTool["inputSchema"]): Cus
44
52
  export declare const PROJECT_DESCRIPTION_NOTES: Record<string, string>;
45
53
  /** 自定义工具(官方无对应,工具层本地实现) */
46
54
  export declare const CUSTOM_TOOLS: CustomTool[];
55
+ /**
56
+ * 页面桥接工具:描述与输入 schema(模型可见)+ 后端 action(调用分发)单一来源。
57
+ * tools/list 与 tools/call 都取自本定义,避免"清单"与"路由"两份来源各自漂移。
58
+ * 只在注入 Vue DevTools 桥的项目(Vue 项目)暴露。
59
+ */
60
+ export interface VueDevtoolsTool extends CustomTool {
61
+ /** 页面桥后端 action(endpoints/vue-devtools.ts executeAction) */
62
+ action: VueDevtoolsAction;
63
+ }
64
+ export declare const VUE_DEVTOOLS_TOOLS: readonly VueDevtoolsTool[];
65
+ /** vue-devtools_* 工具名 → 定义(未知名称返回 undefined) */
66
+ export declare function findVueDevtoolsTool(name: string): VueDevtoolsTool | undefined;
67
+ /** vue-devtools_* 的 tools/list 暴露面(剥掉仅用于路由分发的 action 字段) */
68
+ export declare function vueDevtoolsToolList(): CustomTool[];
47
69
  /** 官方声明了条件、且属于可开启范围的工具(由官方元数据派生,无手写) */
48
70
  export declare function isOfficialExtraTool(short: string): boolean;
49
71
  /** 由官方 conditions 推导所需 flag(experimentalX → --experimental-x,与官方 CLI 一致) */
@@ -1,8 +1,15 @@
1
+ import { VUE_DEVTOOLS_ACTIONS } from "@aipanel/core";
1
2
  import { OFFICIAL_TOOL_META } from "./official-meta.mjs";
2
3
  const MCP_PREFIX = "chrome-devtools_";
3
4
  function displayToolName(short) {
4
5
  return MCP_PREFIX + short;
5
6
  }
7
+ const VUE_DEVTOOLS_PREFIX = "vue-devtools_";
8
+ const LOGS_DEVTOOLS_PREFIX = "logs-devtools_";
9
+ const VITE_LOGS_TOOL_NAME = `${LOGS_DEVTOOLS_PREFIX}vite_logs`;
10
+ function serviceLogToolName(name) {
11
+ return `${LOGS_DEVTOOLS_PREFIX}${name}_logs`;
12
+ }
6
13
  const OFFICIAL_GLOBAL_POLICY = [
7
14
  "evaluate_script",
8
15
  "list_console_messages",
@@ -33,8 +40,153 @@ const CUSTOM_TOOLS = [
33
40
  name: displayToolName("current_page"),
34
41
  description: "Get the page the user is currently browsing (URL, title, and page ID). Resolves the project page with injected context; for allowOrigins pages use list_pages and pass its pageId to page tools instead.",
35
42
  inputSchema: { type: "object", properties: {} }
43
+ },
44
+ {
45
+ name: VITE_LOGS_TOOL_NAME,
46
+ description: `\u83B7\u53D6 Vite \u5F00\u53D1\u670D\u52A1\u5668\u7684\u8FD0\u884C\u65E5\u5FD7\u3002
47
+
48
+ **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
49
+ - \u7528\u6237\u62A5\u544A"\u9875\u9762\u6CA1\u66F4\u65B0"\u3001"\u70ED\u66F4\u65B0\u4E0D\u5DE5\u4F5C"\u3001"HMR \u5931\u6548"\u65F6
50
+ - \u6784\u5EFA\u62A5\u9519\u6216\u7F16\u8BD1\u5931\u8D25\uFF0C\u9700\u8981\u67E5\u770B\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F
51
+ - \u9875\u9762\u767D\u5C4F\u3001\u6837\u5F0F\u4E22\u5931\u3001\u6A21\u5757\u52A0\u8F7D\u5931\u8D25\u7B49\u5F00\u53D1\u95EE\u9898
52
+ - \u7528\u6237\u63D0\u5230"\u5F00\u53D1\u670D\u52A1\u5668\u6709\u95EE\u9898"\u3001"vite \u62A5\u9519"
53
+ - \u9700\u8981\u786E\u8BA4\u6700\u8FD1\u7684\u6587\u4EF6\u53D8\u66F4\u662F\u5426\u88AB Vite \u6B63\u786E\u5904\u7406
54
+
55
+ **\u65E5\u5FD7\u5185\u5BB9**\uFF1A
56
+ - Vite HMR \u70ED\u66F4\u65B0\u65E5\u5FD7\uFF08\u54EA\u4E9B\u6587\u4EF6\u88AB\u66F4\u65B0\u3001\u66F4\u65B0\u72B6\u6001\uFF09
57
+ - \u6784\u5EFA\u7F16\u8BD1\u65E5\u5FD7\uFF08\u9519\u8BEF\u3001\u8B66\u544A\u3001\u6210\u529F\u4FE1\u606F\uFF09
58
+ - OpenCode Web \u8FDB\u7A0B\u8F93\u51FA
59
+ - \u63D2\u4EF6\u8FD0\u884C\u65E5\u5FD7
60
+
61
+ \u65E5\u5FD7\u4FDD\u5B58\u5728\u5185\u5B58\u7F13\u51B2\u533A\uFF08\u6700\u8FD1 500 \u6761\uFF09\u3002`,
62
+ inputSchema: {
63
+ type: "object",
64
+ properties: {
65
+ level: {
66
+ type: "string",
67
+ description: "\u65E5\u5FD7\u7EA7\u522B\u8FC7\u6EE4\uFF1Aerror(\u9519\u8BEF)\u3001warn(\u8B66\u544A)\u3001info(\u4FE1\u606F)\u3001debug(\u8C03\u8BD5)\u3001log(\u666E\u901A)\u3002\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF0C\u5982 'error,warn'"
68
+ },
69
+ limit: {
70
+ type: "integer",
71
+ minimum: 1,
72
+ maximum: 200,
73
+ default: 50,
74
+ description: "\u8FD4\u56DE\u6761\u6570\uFF0C\u9ED8\u8BA4 50\uFF0C\u6700\u5927 200"
75
+ },
76
+ source: {
77
+ type: "string",
78
+ description: "\u6765\u6E90\u8FC7\u6EE4\uFF1Aconsole(\u63A7\u5236\u53F0)\u3001provider-stdout(\u670D\u52A1\u8F93\u51FA)\u3001provider-stderr(\u670D\u52A1\u9519\u8BEF)"
79
+ }
80
+ }
81
+ }
82
+ }
83
+ ];
84
+ const VUE_DEVTOOLS_TOOLS = [
85
+ {
86
+ name: `${VUE_DEVTOOLS_PREFIX}get_apps`,
87
+ action: VUE_DEVTOOLS_ACTIONS.GET_APPS,
88
+ description: `\u83B7\u53D6\u6307\u5B9A\u9875\u9762\u6240\u6709 Vue \u5E94\u7528\u5B9E\u4F8B\u5217\u8868\u3002
89
+
90
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
91
+ - \u6392\u67E5\u5FAE\u524D\u7AEF/\u591A\u5B9E\u4F8B\u573A\u666F\u4E0B\u64CD\u4F5C\u7684\u662F\u54EA\u4E2A\u5E94\u7528
92
+ - \u5207\u6362\u6D3B\u8DC3\u5E94\u7528\u524D\u67E5\u770B\u6709\u54EA\u4E9B\u53EF\u7528`,
93
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
94
+ },
95
+ {
96
+ name: `${VUE_DEVTOOLS_PREFIX}set_active_app`,
97
+ action: VUE_DEVTOOLS_ACTIONS.TOGGLE_APP,
98
+ description: `\u5207\u6362\u6307\u5B9A\u9875\u9762\u7684\u6D3B\u8DC3 Vue \u5E94\u7528\u5B9E\u4F8B\u3002\u540E\u7EED\u6240\u6709 vue-devtools_* \u5DE5\u5177\u90FD\u64CD\u4F5C\u8FD9\u4E2A\u5E94\u7528\u3002`,
99
+ inputSchema: withPageIdSchema({
100
+ type: "object",
101
+ properties: {
102
+ appId: { type: "string", description: "\u5E94\u7528 ID\uFF08\u4ECE vue-devtools_get_apps \u83B7\u53D6\uFF09" }
103
+ },
104
+ required: ["appId"]
105
+ })
106
+ },
107
+ {
108
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_tree`,
109
+ action: VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_TREE,
110
+ description: `\u83B7\u53D6\u6307\u5B9A\u9875\u9762\u5F53\u524D\u6D3B\u8DC3 Vue \u5E94\u7528\u7684\u7EC4\u4EF6\u6811\u3002
111
+
112
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
113
+ - \u4E86\u89E3\u9875\u9762\u7EC4\u4EF6\u5C42\u7EA7\u7ED3\u6784
114
+ - \u627E\u5230\u76EE\u6807\u7EC4\u4EF6\u7684 nodeId\uFF08\u540E\u7EED\u67E5\u72B6\u6001\u7528\uFF09
115
+ - \u6392\u67E5\u7EC4\u4EF6\u672A\u6E32\u67D3\u95EE\u9898
116
+ - \u53EA\u5173\u5FC3\u67D0\u7C7B\u7EC4\u4EF6\u65F6\u7528 filter \u7F29\u5C0F\u8303\u56F4
117
+
118
+ **\u8FD4\u56DE**\uFF1A\u7EC4\u4EF6\u6811 [{ id, name, children, file, ... }]`,
119
+ inputSchema: withPageIdSchema({
120
+ type: "object",
121
+ properties: {
122
+ filter: { type: "string", description: "\u6309\u7EC4\u4EF6\u540D\u8FC7\u6EE4\uFF08\u5927\u5C0F\u5199\u4E0D\u654F\u611F\u7684\u5B50\u4E32\u5339\u914D\uFF09\uFF0C\u53EF\u9009" }
123
+ }
124
+ })
125
+ },
126
+ {
127
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_state`,
128
+ action: VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_STATE,
129
+ description: `\u83B7\u53D6\u6307\u5B9A\u7EC4\u4EF6\u7684\u5B8C\u6574\u8FD0\u884C\u65F6\u72B6\u6001\u3002
130
+
131
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
132
+ - \u6392\u67E5 props \u4F20\u503C\u662F\u5426\u6B63\u786E
133
+ - \u67E5\u770B ref/reactive \u54CD\u5E94\u5F0F\u6570\u636E\u7684\u5F53\u524D\u503C
134
+ - \u68C0\u67E5 computed \u8BA1\u7B97\u7ED3\u679C
135
+ - \u67E5\u770B attrs / events / inject / provide / template refs`,
136
+ inputSchema: withPageIdSchema({
137
+ type: "object",
138
+ properties: {
139
+ nodeId: {
140
+ type: "string",
141
+ description: "\u7EC4\u4EF6\u8282\u70B9 ID\uFF08\u4ECE vue-devtools_get_component_tree \u83B7\u53D6\uFF09"
142
+ }
143
+ },
144
+ required: ["nodeId"]
145
+ })
146
+ },
147
+ {
148
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_render_code`,
149
+ action: VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_RENDER_CODE,
150
+ description: `\u83B7\u53D6\u7EC4\u4EF6\u7684\u6E32\u67D3\u51FD\u6570\u6E90\u7801\u3002`,
151
+ inputSchema: withPageIdSchema({
152
+ type: "object",
153
+ properties: { nodeId: { type: "string", description: "\u7EC4\u4EF6\u8282\u70B9 ID" } },
154
+ required: ["nodeId"]
155
+ })
156
+ },
157
+ {
158
+ name: `${VUE_DEVTOOLS_PREFIX}get_current_route`,
159
+ action: VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
160
+ description: `\u83B7\u53D6 Vue Router \u7684\u5F53\u524D\u8DEF\u7531\u4FE1\u606F\u3002
161
+
162
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
163
+ - \u6392\u67E5\u8DEF\u7531\u8DF3\u8F6C\u95EE\u9898
164
+ - \u67E5\u770B\u5F53\u524D\u8DEF\u7531 path/params/query/hash
165
+ - \u786E\u8BA4\u8DEF\u7531\u5B88\u536B\u548C matched \u8BB0\u5F55`,
166
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
167
+ },
168
+ {
169
+ name: `${VUE_DEVTOOLS_PREFIX}get_routes`,
170
+ action: VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
171
+ description: `\u83B7\u53D6 Vue Router \u7684\u5B8C\u6574\u8DEF\u7531\u8868\u3002
172
+
173
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
174
+ - \u67E5\u770B\u6240\u6709\u5DF2\u6CE8\u518C\u8DEF\u7531
175
+ - \u786E\u8BA4\u8DEF\u7531\u914D\u7F6E\u662F\u5426\u6B63\u786E
176
+ - \u67E5\u770B\u8DEF\u7531\u5D4C\u5957\u5173\u7CFB`,
177
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
36
178
  }
37
179
  ];
180
+ function findVueDevtoolsTool(name) {
181
+ return VUE_DEVTOOLS_TOOLS.find((t) => t.name === name);
182
+ }
183
+ function vueDevtoolsToolList() {
184
+ return VUE_DEVTOOLS_TOOLS.map(({ name, description, inputSchema }) => ({
185
+ name,
186
+ description,
187
+ inputSchema
188
+ }));
189
+ }
38
190
  const UNSAFE_CATEGORIES = /* @__PURE__ */ new Set(["EXTENSIONS", "PWA", "THIRD_PARTY", "WEBMCP"]);
39
191
  function metaByName(short) {
40
192
  return OFFICIAL_TOOL_META.find((m) => m.name === short);
@@ -106,18 +258,25 @@ function isAllowedToolName(name) {
106
258
  }
107
259
  export {
108
260
  CUSTOM_TOOLS,
261
+ LOGS_DEVTOOLS_PREFIX,
109
262
  MCP_PREFIX,
110
263
  OFFICIAL_GLOBAL_POLICY,
111
264
  OFFICIAL_NO_PAGE_TOOLS,
112
265
  PAGE_ID_PROP,
113
266
  PROJECT_DESCRIPTION_NOTES,
267
+ VITE_LOGS_TOOL_NAME,
268
+ VUE_DEVTOOLS_PREFIX,
269
+ VUE_DEVTOOLS_TOOLS,
114
270
  configureToolScope,
115
271
  currentOfficialShorts,
116
272
  displayToolName,
117
273
  extraToolFlag,
274
+ findVueDevtoolsTool,
118
275
  isAllowedToolName,
119
276
  isOfficialExtraTool,
120
277
  officialDefaultShorts,
121
278
  officialExtraCandidates,
279
+ serviceLogToolName,
280
+ vueDevtoolsToolList,
122
281
  withPageIdSchema
123
282
  };
@@ -6,4 +6,6 @@ import type { McpProxy } from "../core/mcp-proxy";
6
6
  import { LOGS_API_PATH, type ChromeProjectOptions, type LogFileConfig } from "@aipanel/core";
7
7
  export * from "./types";
8
8
  export { LOGS_API_PATH, MCP_API_PATH, VUE_DEVTOOLS_API_PATH };
9
- export declare function setupMiddlewares(server: ViteDevServer, ctx: EndpointContext, mcp?: McpProxy, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions): void;
9
+ export declare function setupMiddlewares(server: ViteDevServer, ctx: EndpointContext, mcp?: McpProxy, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions,
10
+ /** 是否暴露 vue-devtools_* 工具(Vue 项目才有页面桥;未识别框架按保守策略视为 Vue) */
11
+ exposeVueDevtools?: boolean): void;
@@ -10,7 +10,7 @@ import { setupMcpEndpoint, MCP_API_PATH } from "./mcp.mjs";
10
10
  import { setupVueDevtoolsEndpoint, VUE_DEVTOOLS_API_PATH } from "./vue-devtools.mjs";
11
11
  import { LOGS_API_PATH } from "@aipanel/core";
12
12
  export * from "./types.mjs";
13
- function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject) {
13
+ function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject, exposeVueDevtools = true) {
14
14
  setupWidgetEndpoints(server, ctx);
15
15
  setupContextEndpoint(server, ctx);
16
16
  setupStartEndpoint(server, ctx);
@@ -20,7 +20,14 @@ function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject) {
20
20
  setupWarmupEndpoint(server, ctx);
21
21
  setupLogsEndpoint(server);
22
22
  if (mcp) {
23
- setupMcpEndpoint(server, mcp, () => ctx.getPageContext(), logFiles ?? [], chromeProject);
23
+ setupMcpEndpoint(
24
+ server,
25
+ mcp,
26
+ () => ctx.getPageContext(),
27
+ logFiles ?? [],
28
+ chromeProject,
29
+ exposeVueDevtools
30
+ );
24
31
  setupVueDevtoolsEndpoint(server, mcp);
25
32
  }
26
33
  }
@@ -3,4 +3,6 @@ import type { ChromeProjectOptions, LogFileConfig, PageContext } from "@aipanel/
3
3
  import { MCP_API_PATH } from "@aipanel/core";
4
4
  import { McpProxy } from "../core/mcp-proxy";
5
5
  export { MCP_API_PATH };
6
- export declare function setupMcpEndpoint(server: ViteDevServer, mcp: McpProxy, getPageContext: () => PageContext, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions): void;
6
+ export declare function setupMcpEndpoint(server: ViteDevServer, mcp: McpProxy, getPageContext: () => PageContext, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions,
7
+ /** Vue 项目才注入 vue-devtools 桥;非 Vue 项目既不上架也不路由这些工具,避免模型调用必失败的工具 */
8
+ exposeVueDevtools?: boolean): void;
@@ -1,4 +1,4 @@
1
- import { MCP_API_PATH, VUE_DEVTOOLS_ACTIONS } from "@aipanel/core";
1
+ import { MCP_API_PATH } from "@aipanel/core";
2
2
  import {
3
3
  createLogger,
4
4
  getProcessLogBuffer,
@@ -12,12 +12,20 @@ import {
12
12
  validatePageId
13
13
  } from "../core/mcp-chrome.mjs";
14
14
  import { resolveProjectScope } from "../core/chrome-project.mjs";
15
- import { CUSTOM_TOOLS, isAllowedToolName } from "../core/mcp-tools.mjs";
15
+ import {
16
+ CUSTOM_TOOLS,
17
+ findVueDevtoolsTool,
18
+ isAllowedToolName,
19
+ serviceLogToolName,
20
+ VITE_LOGS_TOOL_NAME,
21
+ VUE_DEVTOOLS_PREFIX,
22
+ vueDevtoolsToolList
23
+ } from "../core/mcp-tools.mjs";
16
24
  import { getOfficialProjectTools } from "../core/official-tools.mjs";
17
25
  import { executeAction } from "./vue-devtools.mjs";
18
26
  import { findGitRoot } from "@aipanel/core/node";
19
27
  const log = createLogger("McpEndpoint");
20
- function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProject) {
28
+ function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProject, exposeVueDevtools = true) {
21
29
  const projectRoot = findGitRoot(process.cwd());
22
30
  server.middlewares.use(async (req, res, next) => {
23
31
  if (!req.url?.startsWith(MCP_API_PATH)) return next();
@@ -50,7 +58,8 @@ function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProj
50
58
  scope.includeExtensions,
51
59
  getPageContext,
52
60
  logFiles,
53
- projectRoot
61
+ projectRoot,
62
+ exposeVueDevtools
54
63
  );
55
64
  return;
56
65
  }
@@ -68,7 +77,7 @@ function handleGetSse(req, res, mcp) {
68
77
  const keepAlive = setInterval(() => res.write(":ping\n\n"), 15e3);
69
78
  req.on("close", () => clearInterval(keepAlive));
70
79
  }
71
- async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot) {
80
+ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot, exposeVueDevtools) {
72
81
  try {
73
82
  const body = await readBody(req);
74
83
  if (!body) {
@@ -80,7 +89,7 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
80
89
  log.debug("MCP request", { method, body: body.substring(0, 150) });
81
90
  switch (method) {
82
91
  case "tools/list":
83
- return await handleToolsList(res, id, mcp, logFiles);
92
+ return await handleToolsList(res, id, mcp, logFiles, exposeVueDevtools);
84
93
  case "tools/call":
85
94
  return await handleToolsCall(
86
95
  res,
@@ -92,7 +101,8 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
92
101
  includeExtensions,
93
102
  getPageContext,
94
103
  logFiles,
95
- projectRoot
104
+ projectRoot,
105
+ exposeVueDevtools
96
106
  );
97
107
  default:
98
108
  return await handleForward(res, body, mcp);
@@ -105,7 +115,7 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
105
115
  });
106
116
  }
107
117
  }
108
- async function handleToolsList(res, id, mcp, logFiles) {
118
+ async function handleToolsList(res, id, mcp, logFiles, exposeVueDevtools) {
109
119
  let official = [];
110
120
  try {
111
121
  official = await getOfficialProjectTools(mcp);
@@ -114,20 +124,28 @@ async function handleToolsList(res, id, mcp, logFiles) {
114
124
  error: e instanceof Error ? e.message : String(e)
115
125
  });
116
126
  }
117
- const tools = [...official, ...CUSTOM_TOOLS, ...buildServiceLogTools(logFiles)];
127
+ const tools = [
128
+ ...official,
129
+ ...CUSTOM_TOOLS,
130
+ ...exposeVueDevtools ? vueDevtoolsToolList() : [],
131
+ ...buildServiceLogTools(logFiles)
132
+ ];
118
133
  sendMcpJson(res, 200, { jsonrpc: "2.0", id, result: { tools } }, mcp.sessionId);
119
134
  }
120
- function handleToolsCall(res, id, body, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot) {
135
+ function handleToolsCall(res, id, body, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot, exposeVueDevtools) {
121
136
  const params = tryParseParams(body);
122
137
  const toolName = params?.name;
123
138
  const args = params?.arguments ?? {};
124
139
  if (toolName === "chrome-devtools_current_page") {
125
140
  return handleGetPageContext(res, id, mcp, operationsOrigins, includeExtensions, getPageContext);
126
141
  }
127
- if (toolName?.startsWith("vue-devtools_")) {
142
+ if (toolName?.startsWith(VUE_DEVTOOLS_PREFIX)) {
143
+ if (!exposeVueDevtools) {
144
+ return sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
145
+ }
128
146
  return handleVueDevtoolsTool(res, id, mcp, projectOrigins, toolName, args);
129
147
  }
130
- if (toolName === "logs-devtools_vite_logs") {
148
+ if (toolName === VITE_LOGS_TOOL_NAME) {
131
149
  return handleViteLogsTool(res, id, args, mcp.sessionId);
132
150
  }
133
151
  if (toolName && isServiceLogTool(toolName, logFiles)) {
@@ -388,23 +406,14 @@ async function handleDevTool(res, id, mcp, mapped, args, operationsOrigins, incl
388
406
  sendMcpError(res, id, -32603, `MCP \u8C03\u7528\u5931\u8D25: ${e.message}`, mcp.sessionId);
389
407
  }
390
408
  }
391
- const VUE_DEVTOOLS_TOOL_ACTIONS = {
392
- "vue-devtools_get_apps": VUE_DEVTOOLS_ACTIONS.GET_APPS,
393
- "vue-devtools_set_active_app": VUE_DEVTOOLS_ACTIONS.TOGGLE_APP,
394
- "vue-devtools_get_component_tree": VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_TREE,
395
- "vue-devtools_get_component_state": VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_STATE,
396
- "vue-devtools_get_component_render_code": VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_RENDER_CODE,
397
- "vue-devtools_get_current_route": VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
398
- "vue-devtools_get_routes": VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO
399
- };
400
409
  async function handleVueDevtoolsTool(res, id, mcp, projectOrigins, toolName, args) {
401
410
  try {
402
- const action = VUE_DEVTOOLS_TOOL_ACTIONS[toolName];
403
- if (!action) {
411
+ const tool = findVueDevtoolsTool(toolName);
412
+ if (!tool) {
404
413
  sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
405
414
  return;
406
415
  }
407
- const result = await executeAction(action, args, mcp, projectOrigins);
416
+ const result = await executeAction(tool.action, args, mcp, projectOrigins);
408
417
  switch (toolName) {
409
418
  case "vue-devtools_set_active_app":
410
419
  sendMcpResult(res, id, `\u5DF2\u5207\u6362\u5230\u5E94\u7528 ${args["appId"]}`, mcp.sessionId);
@@ -483,7 +492,7 @@ function parseProcessLevelFilter(level) {
483
492
  }
484
493
  function buildServiceLogTools(logFiles) {
485
494
  return logFiles.map((cfg) => ({
486
- name: `logs-devtools_${cfg.name}_logs`,
495
+ name: serviceLogToolName(cfg.name),
487
496
  description: `\u83B7\u53D6 ${cfg.name} \u7684\u65E5\u5FD7\u3002
488
497
 
489
498
  **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
@@ -511,11 +520,11 @@ ${cfg.description}
511
520
  }));
512
521
  }
513
522
  function isServiceLogTool(toolName, logFiles) {
514
- return logFiles.some((cfg) => `logs-devtools_${cfg.name}_logs` === toolName);
523
+ return logFiles.some((cfg) => serviceLogToolName(cfg.name) === toolName);
515
524
  }
516
525
  async function handleServiceLogsTool(res, id, toolName, args, logFiles, projectRoot, sessionId) {
517
526
  try {
518
- const cfg = logFiles.find((c) => `logs-devtools_${c.name}_logs` === toolName);
527
+ const cfg = logFiles.find((c) => serviceLogToolName(c.name) === toolName);
519
528
  if (!cfg) {
520
529
  sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, sessionId);
521
530
  return;
package/es/index.mjs CHANGED
@@ -210,7 +210,8 @@ function createAIPanelPlugin(options = {}) {
210
210
  },
211
211
  mcpProxy,
212
212
  config.logFiles,
213
- chromeProjectOptions
213
+ chromeProjectOptions,
214
+ isVueProject
214
215
  );
215
216
  if (!config.mcpOnly) {
216
217
  try {