vite-plugin-aipanel 1.2.8 → 1.2.10

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.
Files changed (58) hide show
  1. package/es/core/chrome-project.d.ts +15 -0
  2. package/es/core/chrome-project.mjs +13 -0
  3. package/es/core/mcp-chrome.d.ts +12 -8
  4. package/es/core/mcp-chrome.mjs +52 -10
  5. package/es/core/mcp-proxy.d.ts +21 -1
  6. package/es/core/mcp-proxy.mjs +83 -12
  7. package/es/core/mcp-tools.d.ts +51 -3
  8. package/es/core/mcp-tools.mjs +109 -659
  9. package/es/core/official-meta.d.ts +9 -0
  10. package/es/core/official-meta.mjs +62 -0
  11. package/es/core/official-tools.d.ts +7 -0
  12. package/es/core/official-tools.mjs +54 -0
  13. package/es/core/proxy-server.mjs +16 -13
  14. package/es/core/service.mjs +4 -5
  15. package/es/endpoints/context.mjs +2 -2
  16. package/es/endpoints/host-events.mjs +1 -2
  17. package/es/endpoints/index.d.ts +2 -2
  18. package/es/endpoints/index.mjs +2 -2
  19. package/es/endpoints/mcp.d.ts +2 -2
  20. package/es/endpoints/mcp.mjs +116 -87
  21. package/es/endpoints/sse.mjs +3 -3
  22. package/es/endpoints/vue-devtools.d.ts +5 -2
  23. package/es/endpoints/vue-devtools.mjs +3 -4
  24. package/es/index.mjs +29 -6
  25. package/lib/client.js +2380 -2369
  26. package/lib/core/chrome-project.cjs +36 -0
  27. package/lib/core/chrome-project.d.ts +15 -0
  28. package/lib/core/mcp-chrome.cjs +64 -10
  29. package/lib/core/mcp-chrome.d.ts +12 -8
  30. package/lib/core/mcp-proxy.cjs +87 -13
  31. package/lib/core/mcp-proxy.d.ts +21 -1
  32. package/lib/core/mcp-tools.cjs +124 -660
  33. package/lib/core/mcp-tools.d.ts +51 -3
  34. package/lib/core/official-meta.cjs +85 -0
  35. package/lib/core/official-meta.d.ts +9 -0
  36. package/lib/core/official-tools.cjs +70 -0
  37. package/lib/core/official-tools.d.ts +7 -0
  38. package/lib/core/proxy-server.cjs +16 -13
  39. package/lib/core/service.cjs +4 -5
  40. package/lib/endpoints/context.cjs +1 -1
  41. package/lib/endpoints/host-events.cjs +0 -1
  42. package/lib/endpoints/index.cjs +2 -2
  43. package/lib/endpoints/index.d.ts +2 -2
  44. package/lib/endpoints/mcp.cjs +114 -85
  45. package/lib/endpoints/mcp.d.ts +2 -2
  46. package/lib/endpoints/sse.cjs +2 -2
  47. package/lib/endpoints/vue-devtools.cjs +4 -5
  48. package/lib/endpoints/vue-devtools.d.ts +5 -2
  49. package/lib/index.cjs +29 -7
  50. package/package.json +10 -7
  51. package/es/utils/format-bridge.d.ts +0 -9
  52. package/es/utils/format-bridge.mjs +0 -80
  53. package/es/utils/system.d.ts +0 -3
  54. package/es/utils/system.mjs +0 -76
  55. package/lib/utils/format-bridge.cjs +0 -102
  56. package/lib/utils/format-bridge.d.ts +0 -9
  57. package/lib/utils/system.cjs +0 -110
  58. package/lib/utils/system.d.ts +0 -3
@@ -24,12 +24,14 @@ module.exports = __toCommonJS(mcp_exports);
24
24
  var import_core = require("@aipanel/core");
25
25
  var import_node = require("@aipanel/core/node");
26
26
  var import_mcp_chrome = require("../core/mcp-chrome.cjs");
27
+ var import_chrome_project = require("../core/chrome-project.cjs");
27
28
  var import_mcp_tools = require("../core/mcp-tools.cjs");
29
+ var import_official_tools = require("../core/official-tools.cjs");
28
30
  var import_vue_devtools = require("./vue-devtools.cjs");
29
- var import_system = require("../utils/system.cjs");
31
+ var import_node2 = require("@aipanel/core/node");
30
32
  const log = (0, import_node.createLogger)("McpEndpoint");
31
- function setupMcpEndpoint(server, mcp, getPageContext, logFiles = []) {
32
- const projectRoot = (0, import_system.findGitRoot)(process.cwd());
33
+ function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProject) {
34
+ const projectRoot = (0, import_node2.findGitRoot)(process.cwd());
33
35
  server.middlewares.use(async (req, res, next) => {
34
36
  if (!req.url?.startsWith(import_core.MCP_API_PATH)) return next();
35
37
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -51,11 +53,14 @@ function setupMcpEndpoint(server, mcp, getPageContext, logFiles = []) {
51
53
  return;
52
54
  }
53
55
  if (req.method === "POST") {
56
+ const scope = (0, import_chrome_project.resolveProjectScope)(server, chromeProject);
54
57
  await handlePost(
55
58
  req,
56
59
  res,
57
60
  mcp,
58
- (0, import_mcp_chrome.getProjectOrigins)(server),
61
+ scope.origins,
62
+ scope.operationsOrigins,
63
+ scope.includeExtensions,
59
64
  getPageContext,
60
65
  logFiles,
61
66
  projectRoot
@@ -76,7 +81,7 @@ function handleGetSse(req, res, mcp) {
76
81
  const keepAlive = setInterval(() => res.write(":ping\n\n"), 15e3);
77
82
  req.on("close", () => clearInterval(keepAlive));
78
83
  }
79
- async function handlePost(req, res, mcp, projectOrigins, getPageContext, logFiles, projectRoot) {
84
+ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot) {
80
85
  try {
81
86
  const body = await readBody(req);
82
87
  if (!body) {
@@ -88,7 +93,7 @@ async function handlePost(req, res, mcp, projectOrigins, getPageContext, logFile
88
93
  log.debug("MCP request", { method, body: body.substring(0, 150) });
89
94
  switch (method) {
90
95
  case "tools/list":
91
- return handleToolsList(res, id, mcp.sessionId, logFiles);
96
+ return await handleToolsList(res, id, mcp, logFiles);
92
97
  case "tools/call":
93
98
  return await handleToolsCall(
94
99
  res,
@@ -96,6 +101,8 @@ async function handlePost(req, res, mcp, projectOrigins, getPageContext, logFile
96
101
  body,
97
102
  mcp,
98
103
  projectOrigins,
104
+ operationsOrigins,
105
+ includeExtensions,
99
106
  getPageContext,
100
107
  logFiles,
101
108
  projectRoot
@@ -111,16 +118,24 @@ async function handlePost(req, res, mcp, projectOrigins, getPageContext, logFile
111
118
  });
112
119
  }
113
120
  }
114
- function handleToolsList(res, id, sessionId, logFiles) {
115
- const tools = [...import_mcp_tools.CUSTOM_TOOLS, ...buildServiceLogTools(logFiles)];
116
- sendMcpJson(res, 200, { jsonrpc: "2.0", id, result: { tools } }, sessionId);
121
+ async function handleToolsList(res, id, mcp, logFiles) {
122
+ let official = [];
123
+ try {
124
+ official = await (0, import_official_tools.getOfficialProjectTools)(mcp);
125
+ } catch (e) {
126
+ log.warn("official chrome tools unavailable for tools/list", {
127
+ error: e instanceof Error ? e.message : String(e)
128
+ });
129
+ }
130
+ const tools = [...official, ...import_mcp_tools.CUSTOM_TOOLS, ...buildServiceLogTools(logFiles)];
131
+ sendMcpJson(res, 200, { jsonrpc: "2.0", id, result: { tools } }, mcp.sessionId);
117
132
  }
118
- function handleToolsCall(res, id, body, mcp, projectOrigins, getPageContext, logFiles, projectRoot) {
133
+ function handleToolsCall(res, id, body, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot) {
119
134
  const params = tryParseParams(body);
120
135
  const toolName = params?.name;
121
136
  const args = params?.arguments ?? {};
122
137
  if (toolName === "chrome-devtools_current_page") {
123
- return handleGetPageContext(res, id, mcp, projectOrigins, getPageContext);
138
+ return handleGetPageContext(res, id, mcp, operationsOrigins, includeExtensions, getPageContext);
124
139
  }
125
140
  if (toolName?.startsWith("vue-devtools_")) {
126
141
  return handleVueDevtoolsTool(res, id, mcp, projectOrigins, toolName, args);
@@ -134,25 +149,40 @@ function handleToolsCall(res, id, body, mcp, projectOrigins, getPageContext, log
134
149
  if (toolName?.startsWith("chrome-devtools_")) {
135
150
  const mapped = toolName.slice("chrome-devtools_".length);
136
151
  if (toolName === "chrome-devtools_list_pages") {
137
- return handleListPages(res, id, mcp, projectOrigins, getPageContext);
152
+ return handleListPages(res, id, mcp, operationsOrigins, includeExtensions, getPageContext);
138
153
  }
139
154
  if (toolName === "chrome-devtools_new_page") {
140
- return handleNewPage(res, id, mcp, args, projectOrigins, getPageContext);
155
+ return handleNewPage(res, id, mcp, args, projectOrigins, operationsOrigins);
141
156
  }
142
- return handleDevTool(res, id, mcp, mapped, args, projectOrigins, toolName);
157
+ if (!(0, import_mcp_tools.isAllowedToolName)(toolName)) {
158
+ return sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
159
+ }
160
+ return handleDevTool(
161
+ res,
162
+ id,
163
+ mcp,
164
+ mapped,
165
+ args,
166
+ operationsOrigins,
167
+ includeExtensions,
168
+ projectRoot,
169
+ toolName
170
+ );
143
171
  }
144
172
  sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
145
173
  }
146
- async function resolveProjectPages(mcp, projectOrigins, getPageContext) {
174
+ async function resolveProjectPages(mcp, operationsOrigins, includeExtensions, getPageContext) {
147
175
  const listResult = await mcp.callChromeDevTool("list_pages", {});
148
176
  if (listResult?.error || !listResult?.result) {
149
177
  throw new Error(listResult?.error?.message ?? "\u65E0\u6CD5\u83B7\u53D6 Chrome \u9875\u9762\u5217\u8868");
150
178
  }
151
179
  const text = listResult?.result?.content?.[0]?.text;
152
180
  const allPages = text ? (0, import_mcp_chrome.parseListPages)(text) : [];
153
- const filtered = allPages.filter((p) => (0, import_mcp_chrome.isProjectPage)(p.url, projectOrigins));
181
+ const filtered = allPages.filter(
182
+ (p) => (0, import_mcp_chrome.isPageAllowed)(p.url, operationsOrigins, includeExtensions)
183
+ );
154
184
  log.debug("Chrome pages", { total: allPages.length, urls: allPages.map((p) => p.url) });
155
- log.debug("project origins", { origins: projectOrigins });
185
+ log.debug("operations origins", { origins: operationsOrigins });
156
186
  log.debug("filtered pages", { count: filtered.length, pageIds: filtered.map((p) => p.pageId) });
157
187
  let activePageId = null;
158
188
  let activePageError;
@@ -163,7 +193,7 @@ async function resolveProjectPages(mcp, projectOrigins, getPageContext) {
163
193
  mcp,
164
194
  pc.url,
165
195
  pc.title,
166
- projectOrigins,
196
+ operationsOrigins,
167
197
  pc.sessionId,
168
198
  filtered,
169
199
  chromeSelectedPageId
@@ -173,37 +203,21 @@ async function resolveProjectPages(mcp, projectOrigins, getPageContext) {
173
203
  }
174
204
  return { filtered, activePageId, activePageError };
175
205
  }
176
- async function confirmOpenProjectPages(mcp, projectOrigins, getPageContext) {
177
- let pages = [];
178
- let ok = false;
179
- for (let attempt = 0; attempt < 3; attempt++) {
180
- try {
181
- ({ filtered: pages } = await resolveProjectPages(mcp, projectOrigins, getPageContext));
182
- ok = true;
183
- break;
184
- } catch {
185
- if (attempt < 2) await (0, import_core.sleep)(500);
186
- }
187
- }
188
- if (!ok) return null;
189
- if (pages.length === 0) {
190
- await (0, import_core.sleep)(500);
191
- try {
192
- ({ filtered: pages } = await resolveProjectPages(mcp, projectOrigins, getPageContext));
193
- } catch {
194
- }
195
- }
196
- return pages;
197
- }
198
- async function handleListPages(res, id, mcp, projectOrigins, getPageContext) {
206
+ async function handleListPages(res, id, mcp, operationsOrigins, includeExtensions, getPageContext) {
199
207
  try {
200
208
  const { filtered, activePageId } = await resolveProjectPages(
201
209
  mcp,
202
- projectOrigins,
210
+ operationsOrigins,
211
+ includeExtensions,
203
212
  getPageContext
204
213
  );
205
214
  if (filtered.length === 0) {
206
- sendMcpResult(res, id, "\u6682\u65E0\u9879\u76EE\u9875\u9762\uFF0C\u8BF7\u5148\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u672C\u5730\u5F00\u53D1\u9875\u9762", mcp.sessionId);
215
+ sendMcpResult(
216
+ res,
217
+ id,
218
+ "\u6682\u65E0\u53EF\u7528\u9875\u9762\uFF08\u9879\u76EE\u9875\u6216 allowOrigins \u767D\u540D\u5355\u9875\uFF09\uFF0C\u8BF7\u5148\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u76F8\u5E94\u9875\u9762",
219
+ mcp.sessionId
220
+ );
207
221
  return;
208
222
  }
209
223
  const pageList = filtered.map((p) => ({
@@ -219,15 +233,21 @@ async function handleListPages(res, id, mcp, projectOrigins, getPageContext) {
219
233
  sendMcpError(res, id, -32603, `MCP \u8C03\u7528\u5931\u8D25: ${e.message}`, mcp.sessionId);
220
234
  }
221
235
  }
222
- async function handleGetPageContext(res, id, mcp, projectOrigins, getPageContext) {
236
+ async function handleGetPageContext(res, id, mcp, operationsOrigins, includeExtensions, getPageContext) {
223
237
  try {
224
238
  const { filtered, activePageId, activePageError } = await resolveProjectPages(
225
239
  mcp,
226
- projectOrigins,
240
+ operationsOrigins,
241
+ includeExtensions,
227
242
  getPageContext
228
243
  );
229
244
  if (filtered.length === 0) {
230
- sendMcpResult(res, id, "\u6682\u65E0\u9879\u76EE\u9875\u9762\uFF0C\u8BF7\u5148\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u672C\u5730\u5F00\u53D1\u9875\u9762", mcp.sessionId);
245
+ sendMcpResult(
246
+ res,
247
+ id,
248
+ "\u6682\u65E0\u53EF\u7528\u9875\u9762\uFF08\u9879\u76EE\u9875\u6216 allowOrigins \u767D\u540D\u5355\u9875\uFF09\uFF0C\u8BF7\u5148\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u76F8\u5E94\u9875\u9762",
249
+ mcp.sessionId
250
+ );
231
251
  return;
232
252
  }
233
253
  const pc = getPageContext();
@@ -264,48 +284,54 @@ async function handleGetPageContext(res, id, mcp, projectOrigins, getPageContext
264
284
  sendMcpError(res, id, -32603, `MCP \u8C03\u7528\u5931\u8D25: ${e.message}`, mcp.sessionId);
265
285
  }
266
286
  }
267
- async function handleNewPage(res, id, mcp, args, projectOrigins, getPageContext) {
287
+ async function handleNewPage(res, id, mcp, args, projectOrigins, operationsOrigins) {
268
288
  try {
269
289
  const url = args["url"];
270
290
  if (typeof url !== "string" || url.trim().length === 0) {
271
291
  sendMcpError(res, id, -32e3, "\u7F3A\u5C11 url \u53C2\u6570\uFF0C\u8BF7\u63D0\u4F9B\u8981\u6253\u5F00\u7684\u9875\u9762 URL", mcp.sessionId);
272
292
  return;
273
293
  }
274
- if (!(0, import_mcp_chrome.isProjectPage)(url, projectOrigins)) {
294
+ if (!(0, import_mcp_chrome.isProjectPage)(url, operationsOrigins)) {
275
295
  sendMcpError(
276
296
  res,
277
297
  id,
278
298
  -32e3,
279
- `\u4E0D\u5141\u8BB8\u6253\u5F00\u975E\u672C\u9879\u76EE\u9875\u9762: ${url}\u3002\u4EC5\u5141\u8BB8\u6253\u5F00\u9879\u76EE\u9875\u9762 (${projectOrigins.join(", ")})`,
299
+ `\u4E0D\u5141\u8BB8\u6253\u5F00\u8BE5\u9875\u9762: ${url}\u3002\u4EC5\u5141\u8BB8\u9879\u76EE\u9875\u6216 allowOrigins \u767D\u540D\u5355\u9875 (${operationsOrigins.join(", ")})`,
280
300
  mcp.sessionId
281
301
  );
282
302
  return;
283
303
  }
284
- const projectPages = await confirmOpenProjectPages(mcp, projectOrigins, getPageContext);
285
- if (projectPages === null) {
286
- sendMcpError(
287
- res,
288
- id,
289
- -32e3,
290
- `\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u9879\u76EE\u9875\u9762\u72B6\u6001\uFF0C\u4E3A\u907F\u514D\u91CD\u590D\u6253\u5F00\uFF0C\u8BF7\u786E\u8BA4 Chrome DevTools \u5DF2\u8FDE\u63A5\u540E\u91CD\u8BD5`,
291
- mcp.sessionId
304
+ if ((0, import_mcp_chrome.isProjectPage)(url, projectOrigins)) {
305
+ const listResult = await mcp.callChromeDevTool("list_pages", {});
306
+ if (listResult?.error || !listResult?.result) {
307
+ sendMcpError(
308
+ res,
309
+ id,
310
+ -32e3,
311
+ `\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u9879\u76EE\u9875\u9762\u72B6\u6001\uFF0C\u4E3A\u907F\u514D\u91CD\u590D\u6253\u5F00\uFF0C\u8BF7\u786E\u8BA4 Chrome DevTools \u5DF2\u8FDE\u63A5\u540E\u91CD\u8BD5`,
312
+ mcp.sessionId
313
+ );
314
+ return;
315
+ }
316
+ const text = listResult?.result?.content?.[0]?.text;
317
+ const projectOpen = (text ? (0, import_mcp_chrome.parseListPages)(text) : []).filter(
318
+ (p) => (0, import_mcp_chrome.isProjectPage)(p.url, projectOrigins)
292
319
  );
293
- return;
294
- }
295
- if (projectPages.length > 0) {
296
- const existing = projectPages.map((p) => `- ${p.title} (${p.url}) [pageId: ${p.pageId}]`).join("\n");
297
- sendMcpResult(
298
- res,
299
- id,
300
- `\u5F53\u524D\u9879\u76EE\u5DF2\u6709\u6253\u5F00\u7684\u9875\u9762\uFF0C\u65E0\u9700\u91CD\u590D\u6253\u5F00\u3002
320
+ if (projectOpen.length > 0) {
321
+ const existing = projectOpen.map((p) => `- ${p.title} (${p.url}) [pageId: ${p.pageId}]`).join("\n");
322
+ sendMcpResult(
323
+ res,
324
+ id,
325
+ `\u5F53\u524D\u9879\u76EE\u5DF2\u6709\u6253\u5F00\u7684\u9875\u9762\uFF0C\u65E0\u9700\u91CD\u590D\u6253\u5F00\u3002
301
326
 
302
327
  \u5DF2\u6253\u5F00\u7684\u9875\u9762\uFF1A
303
328
  ${existing}
304
329
 
305
330
  \u5982\u9700\u64CD\u4F5C\u73B0\u6709\u9875\u9762\uFF0C\u8BF7\u4F7F\u7528 chrome-devtools_list_pages \u83B7\u53D6\u9875\u9762 ID\uFF0C\u518D\u914D\u5408\u5176\u4ED6 chrome-devtools_* \u5DE5\u5177\u4F7F\u7528\u3002`,
306
- mcp.sessionId
307
- );
308
- return;
331
+ mcp.sessionId
332
+ );
333
+ return;
334
+ }
309
335
  }
310
336
  const forwardBody = JSON.stringify({
311
337
  jsonrpc: "2.0",
@@ -321,37 +347,40 @@ ${existing}
321
347
  sendMcpError(res, id, -32603, `MCP \u8C03\u7528\u5931\u8D25: ${e.message}`, mcp.sessionId);
322
348
  }
323
349
  }
324
- async function handleDevTool(res, id, mcp, mapped, args, projectOrigins, toolName) {
350
+ async function handleDevTool(res, id, mcp, mapped, args, operationsOrigins, includeExtensions, projectRoot, toolName) {
325
351
  try {
326
352
  const pageId = args["pageId"];
327
353
  if (typeof pageId !== "number") {
328
354
  sendMcpError(res, id, -32e3, "\u7F3A\u5C11 pageId \u53C2\u6570\uFF0C\u8BF7\u5148\u83B7\u53D6\u9875\u9762 ID", mcp.sessionId);
329
355
  return;
330
356
  }
331
- const validation = await (0, import_mcp_chrome.validatePageId)(mcp, pageId, projectOrigins);
357
+ const validation = await (0, import_mcp_chrome.validatePageId)(mcp, pageId, operationsOrigins, includeExtensions);
332
358
  log.debug("handleDevTool validation", {
333
359
  pageId,
334
- projectPages: validation.projectPages.length,
335
- projectPageIds: validation.projectPages.map((p) => p.pageId),
336
- origins: projectOrigins,
360
+ allowedPages: validation.allowedPages.length,
361
+ allowedPageIds: validation.allowedPages.map((p) => p.pageId),
362
+ origins: operationsOrigins,
337
363
  isValid: validation.valid
338
364
  });
339
365
  if (!validation.valid) {
340
366
  sendMcpError(res, id, -32e3, validation.error, mcp.sessionId);
341
367
  return;
342
368
  }
343
- if (toolName === "chrome-devtools_navigate_page" && args["type"] === "url") {
344
- const targetUrl = args["url"];
345
- if (typeof targetUrl === "string" && targetUrl.length > 0) {
346
- if (!(0, import_mcp_chrome.isProjectPage)(targetUrl, projectOrigins)) {
347
- sendMcpError(
348
- res,
349
- id,
350
- -32e3,
351
- `\u4E0D\u5141\u8BB8\u8DF3\u8F6C\u5230\u975E\u672C\u9879\u76EE\u9875\u9762: ${targetUrl}\u3002\u4EC5\u5141\u8BB8\u5BFC\u822A\u5230\u9879\u76EE\u9875\u9762 (${projectOrigins.join(", ")})`,
352
- mcp.sessionId
353
- );
354
- return;
369
+ if (toolName === "chrome-devtools_navigate_page") {
370
+ const navType = typeof args["type"] === "string" ? args["type"] : "url";
371
+ if (navType === "url") {
372
+ const targetUrl = args["url"];
373
+ if (typeof targetUrl === "string" && targetUrl.length > 0) {
374
+ if (!(0, import_mcp_chrome.isProjectPage)(targetUrl, operationsOrigins)) {
375
+ sendMcpError(
376
+ res,
377
+ id,
378
+ -32e3,
379
+ `\u4E0D\u5141\u8BB8\u8DF3\u8F6C\u5230\u8BE5\u9875\u9762: ${targetUrl}\u3002\u4EC5\u5141\u8BB8\u5BFC\u822A\u5230\u9879\u76EE\u9875\u6216 allowOrigins \u767D\u540D\u5355\u9875 (${operationsOrigins.join(", ")})`,
380
+ mcp.sessionId
381
+ );
382
+ return;
383
+ }
355
384
  }
356
385
  }
357
386
  }
@@ -1,6 +1,6 @@
1
1
  import type { ViteDevServer } from "vite";
2
- import type { LogFileConfig, PageContext } from "@aipanel/core";
2
+ import type { ChromeProjectOptions, LogFileConfig, PageContext } from "@aipanel/core";
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[]): void;
6
+ export declare function setupMcpEndpoint(server: ViteDevServer, mcp: McpProxy, getPageContext: () => PageContext, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions): void;
@@ -34,10 +34,10 @@ function setupSseEndpoint(server, ctx) {
34
34
  });
35
35
  ctx.sseClients.add(res);
36
36
  log.debug("SSE client connected", { totalClients: ctx.sseClients.size });
37
- res.write(`data: ${JSON.stringify({ type: "CONNECTED" })}
37
+ res.write(`data: ${JSON.stringify({ type: import_core.SSE_EVENT_TYPES.CONNECTED })}
38
38
 
39
39
  `);
40
- const statusPayload = { type: "STATUS_SYNC" };
40
+ const statusPayload = { type: import_core.SSE_EVENT_TYPES.STATUS_SYNC };
41
41
  if (ctx.isServiceStarted !== void 0) {
42
42
  statusPayload.isStarted = ctx.isServiceStarted;
43
43
  }
@@ -17,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
  var vue_devtools_exports = {};
19
19
  __export(vue_devtools_exports, {
20
- VUE_DEVTOOLS_API_PATH: () => VUE_DEVTOOLS_API_PATH,
20
+ VUE_DEVTOOLS_API_PATH: () => import_core.VUE_DEVTOOLS_API_PATH,
21
21
  executeAction: () => executeAction,
22
22
  setupVueDevtoolsEndpoint: () => setupVueDevtoolsEndpoint
23
23
  });
@@ -26,9 +26,8 @@ var import_node = require("@aipanel/core/node");
26
26
  var import_core = require("@aipanel/core");
27
27
  var import_mcp_chrome = require("../core/mcp-chrome.cjs");
28
28
  const log = (0, import_node.createLogger)("Endpoints:VueDevtools");
29
- const VUE_DEVTOOLS_API_PATH = "/__aipanel_vue_devtools__";
30
29
  function setupVueDevtoolsEndpoint(server, mcp) {
31
- server.middlewares.use(VUE_DEVTOOLS_API_PATH, async (req, res) => {
30
+ server.middlewares.use(import_core.VUE_DEVTOOLS_API_PATH, async (req, res) => {
32
31
  res.setHeader("Content-Type", "application/json");
33
32
  res.setHeader("Access-Control-Allow-Origin", "*");
34
33
  res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
@@ -63,9 +62,9 @@ async function executeAction(action, args, mcp, projectOrigins) {
63
62
  }
64
63
  const validation = await (0, import_mcp_chrome.validatePageId)(mcp, pageId, projectOrigins);
65
64
  if (!validation.valid) {
66
- log.error("pageId \u65E0\u6548\u6216\u975E\u9879\u76EE\u9875\u9762", {
65
+ log.error("pageId \u65E0\u6548\u6216\u4E0D\u5728\u53EF\u64CD\u4F5C\u8303\u56F4", {
67
66
  pageId,
68
- projectPageIds: validation.projectPages.map((p) => p.pageId)
67
+ allowedPageIds: validation.allowedPages.map((p) => p.pageId)
69
68
  });
70
69
  throw new Error(validation.error);
71
70
  }
@@ -4,6 +4,9 @@
4
4
  */
5
5
  import type { ViteDevServer } from "vite";
6
6
  import type { McpProxy } from "../core/mcp-proxy";
7
- export declare const VUE_DEVTOOLS_API_PATH = "/__aipanel_vue_devtools__";
7
+ import { VUE_DEVTOOLS_API_PATH } from "@aipanel/core";
8
+ export { VUE_DEVTOOLS_API_PATH };
8
9
  export declare function setupVueDevtoolsEndpoint(server: ViteDevServer, mcp: McpProxy): void;
9
- export declare function executeAction(action: string, args: Record<string, unknown> | undefined, mcp: McpProxy, projectOrigins: string[]): Promise<unknown>;
10
+ export declare function executeAction(action: string, args: Record<string, unknown> | undefined, mcp: McpProxy,
11
+ /** vue-devtools 桥只注入在项目页面,范围为自动项目页 origins */
12
+ projectOrigins: string[]): Promise<unknown>;
package/lib/index.cjs CHANGED
@@ -40,14 +40,15 @@ var import_injector = require("./core/injector.cjs");
40
40
  var import_provider_loader = require("./core/provider-loader.cjs");
41
41
  var import_service = require("./core/service.cjs");
42
42
  var import_mcp_proxy = require("./core/mcp-proxy.cjs");
43
+ var import_mcp_tools = require("./core/mcp-tools.cjs");
43
44
  var import_paths = require("./utils/paths.cjs");
44
- var import_system = require("./utils/system.cjs");
45
+ var import_node2 = require("@aipanel/core/node");
45
46
  const DEVTOOLS_BRIDGE_IMPORTEE = "virtual:aipanel-vue-devtools-bridge";
46
47
  const DEVTOOLS_BRIDGE_QUERY = "aipanel_vue_devtools_bridge";
47
48
  const BRIDGE_SOURCE_PATH = (0, import_paths.resolveVueDevtoolsBridgePath)();
48
49
  const SILENT_CONTEXT_SCRIPT = `<script>
49
50
  (function () {
50
- var API = "/__aipanel_context__";
51
+ var API = "${import_core.CONTEXT_API_PATH}";
51
52
  var last = "";
52
53
  function report(force) {
53
54
  if (document.visibilityState !== "visible") return;
@@ -57,7 +58,7 @@ const SILENT_CONTEXT_SCRIPT = `<script>
57
58
  if (!force && key === last) return;
58
59
  last = key;
59
60
  var sessionId = "";
60
- try { sessionId = sessionStorage.getItem("_aipanel_pk") || ""; } catch (e) {}
61
+ try { sessionId = sessionStorage.getItem("${import_core.SESSION_ID_KEY}") || ""; } catch (e) {}
61
62
  fetch(API, {
62
63
  method: "POST",
63
64
  headers: { "Content-Type": "application/json" },
@@ -113,7 +114,27 @@ function createAIPanelPlugin(options = {}) {
113
114
  let activeTabId = DEFAULT_TAB;
114
115
  const serviceInstanceId = import_crypto.default.randomUUID();
115
116
  const sseClients = /* @__PURE__ */ new Set();
116
- const mcpProxy = new import_mcp_proxy.McpProxy({ idleTimeout: 5 * 60 * 1e3 });
117
+ const chromeProjectOptions = config.chromeMcp?.project;
118
+ (0, import_mcp_tools.configureToolScope)(
119
+ chromeProjectOptions?.tools?.extra ?? [],
120
+ chromeProjectOptions?.tools?.deny ?? [],
121
+ (msg) => log.warn(msg)
122
+ );
123
+ const chromeManagedArgs = [];
124
+ for (const short of chromeProjectOptions?.tools?.extra ?? []) {
125
+ const flag = (0, import_mcp_tools.extraToolFlag)(short);
126
+ if (flag && !chromeManagedArgs.includes(flag)) chromeManagedArgs.push(flag);
127
+ }
128
+ if (chromeProjectOptions?.includeExtensionPages) {
129
+ chromeManagedArgs.push("--experimental-include-all-pages");
130
+ }
131
+ const mcpProxy = new import_mcp_proxy.McpProxy({
132
+ idleTimeout: 5 * 60 * 1e3,
133
+ // 用户 chrome MCP 透传:追加 args/env(核心受保护参数不可覆盖,由 McpProxy 过滤)
134
+ userArgs: config.chromeMcp?.args,
135
+ env: config.chromeMcp?.env,
136
+ managedArgs: chromeManagedArgs
137
+ });
117
138
  let provider = null;
118
139
  const service = new import_service.AIPanelService(
119
140
  config,
@@ -125,7 +146,7 @@ function createAIPanelPlugin(options = {}) {
125
146
  actualProxyPort = port;
126
147
  }
127
148
  );
128
- service.workspaceRoot = (0, import_system.findGitRoot)(process.cwd());
149
+ service.workspaceRoot = (0, import_node2.findGitRoot)(process.cwd());
129
150
  return {
130
151
  name: "vite-plugin-aipanel",
131
152
  apply(_viteConfig, env) {
@@ -204,7 +225,8 @@ function createAIPanelPlugin(options = {}) {
204
225
  retryWarmupChromeMcp: () => service.retryWarmupChromeMcp()
205
226
  },
206
227
  mcpProxy,
207
- config.logFiles
228
+ config.logFiles,
229
+ chromeProjectOptions
208
230
  );
209
231
  if (!config.mcpOnly) {
210
232
  try {
@@ -329,7 +351,7 @@ function createAIPanelPlugin(options = {}) {
329
351
  ];
330
352
  const titleInject = `<script>
331
353
  (function () {
332
- var KEY = "_aipanel_pk";
354
+ var KEY = "${import_core.SESSION_ID_KEY}";
333
355
  if (!sessionStorage.getItem(KEY)) {
334
356
  sessionStorage.setItem(KEY, "[" + Math.random().toString(36).slice(2, 10) + "]");
335
357
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-aipanel",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "description": "Embed OpenCode Web UI in your Vite dev server for real-time code modification and preview",
5
5
  "type": "module",
6
6
  "main": "lib/index.cjs",
@@ -35,18 +35,19 @@
35
35
  "author": "",
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
+ "picomatch": "^4.0.2",
38
39
  "@vue/devtools-kit": "^8.0.0",
39
40
  "@vue/language-server": "^3.3.7",
40
41
  "chrome-devtools-mcp": "^1.8.0",
41
42
  "execa": "^9.6.1",
42
43
  "typescript-language-server": "^5.3.0",
43
44
  "unplugin-vue-inspector": "^3.0.0",
44
- "@aipanel/core": "1.2.8",
45
- "@aipanel/provider-opencode": "1.2.8"
45
+ "@aipanel/provider-opencode": "1.2.10",
46
+ "@aipanel/core": "1.2.10"
46
47
  },
47
48
  "peerDependencies": {
48
49
  "vite": ">=5.0.0",
49
- "@aipanel/provider-deepseek": "1.2.8"
50
+ "@aipanel/provider-deepseek": "1.2.10"
50
51
  },
51
52
  "peerDependenciesMeta": {
52
53
  "@aipanel/provider-deepseek": {
@@ -54,11 +55,13 @@
54
55
  }
55
56
  },
56
57
  "devDependencies": {
58
+ "@types/picomatch": "^4.0.0",
57
59
  "sharp": "^0.34.5",
58
- "@aipanel/client": "1.2.8"
60
+ "@aipanel/client": "1.2.10"
59
61
  },
60
62
  "scripts": {
61
- "build": "pagoda-cli build && vite build -c vite.client.config.ts",
62
- "typecheck": "tsc -p tsconfig.declaration.json --noEmit"
63
+ "build": "node scripts/sync-official-meta.mjs && pagoda-cli build && vite build -c vite.client.config.ts",
64
+ "typecheck": "tsc -p tsconfig.declaration.json --noEmit",
65
+ "test": "vitest run"
63
66
  }
64
67
  }
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * OpenCode VS Code 格式化桥接脚本
4
- *
5
- * VS Code 扩展可用时通过 HTTP API 格式化,不可用时直接返回(由 OpenCode 内置 prettier 兜底)。
6
- *
7
- * 用法: node format-bridge.cjs <filePath>
8
- */
9
- export {};
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env node
2
- import fs from "fs";
3
- import http from "http";
4
- import path from "path";
5
- const filePath = process.argv[2];
6
- if (!filePath) {
7
- console.error("Usage: node format-bridge.cjs <filePath>");
8
- process.exit(1);
9
- }
10
- const absPath = path.resolve(filePath);
11
- const PORT = parseInt(process.env.AIPANEL_VSCODE_PORT || "", 10) || 51939;
12
- function touchFile() {
13
- const now = /* @__PURE__ */ new Date();
14
- fs.utimesSync(absPath, now, now);
15
- }
16
- function probeHealth() {
17
- return new Promise((resolve) => {
18
- const req = http.get(`http://127.0.0.1:${PORT}/health`, (res) => {
19
- res.resume();
20
- resolve(res.statusCode === 200);
21
- });
22
- req.setTimeout(500, () => {
23
- req.destroy();
24
- resolve(false);
25
- });
26
- req.on("error", () => resolve(false));
27
- });
28
- }
29
- function formatViaExtension() {
30
- return new Promise((resolve, reject) => {
31
- const body = JSON.stringify({ filePath: absPath });
32
- const req = http.request(
33
- {
34
- hostname: "127.0.0.1",
35
- port: PORT,
36
- path: "/format",
37
- method: "POST",
38
- headers: { "Content-Type": "application/json" },
39
- timeout: 5e3
40
- },
41
- (res) => {
42
- const chunks = [];
43
- res.on("data", (c) => chunks.push(c));
44
- res.on("end", () => {
45
- try {
46
- const result = JSON.parse(Buffer.concat(chunks).toString());
47
- if (result.error) {
48
- reject(new Error(result.error));
49
- } else {
50
- resolve(result);
51
- }
52
- } catch (e) {
53
- reject(e);
54
- }
55
- });
56
- }
57
- );
58
- req.on("error", reject);
59
- req.on("timeout", () => {
60
- req.destroy();
61
- reject(new Error("Timeout"));
62
- });
63
- req.write(body);
64
- req.end();
65
- });
66
- }
67
- async function main() {
68
- const healthy = await probeHealth();
69
- if (!healthy) {
70
- touchFile();
71
- process.exit(0);
72
- }
73
- try {
74
- await formatViaExtension();
75
- } catch {
76
- }
77
- touchFile();
78
- process.exit(0);
79
- }
80
- main();
@@ -1,3 +0,0 @@
1
- import type { ResultPromise } from "execa";
2
- export declare function waitForServer(url: string, timeout?: number, process?: ResultPromise): Promise<void>;
3
- export declare function findGitRoot(startDir: string, maxDepth?: number): string;