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.
- package/es/core/chrome-project.d.ts +15 -0
- package/es/core/chrome-project.mjs +13 -0
- package/es/core/mcp-chrome.d.ts +12 -8
- package/es/core/mcp-chrome.mjs +52 -10
- package/es/core/mcp-proxy.d.ts +21 -1
- package/es/core/mcp-proxy.mjs +83 -12
- package/es/core/mcp-tools.d.ts +51 -3
- package/es/core/mcp-tools.mjs +109 -659
- package/es/core/official-meta.d.ts +9 -0
- package/es/core/official-meta.mjs +62 -0
- package/es/core/official-tools.d.ts +7 -0
- package/es/core/official-tools.mjs +54 -0
- package/es/core/proxy-server.mjs +16 -13
- package/es/core/service.mjs +4 -5
- package/es/endpoints/context.mjs +2 -2
- package/es/endpoints/host-events.mjs +1 -2
- package/es/endpoints/index.d.ts +2 -2
- package/es/endpoints/index.mjs +2 -2
- package/es/endpoints/mcp.d.ts +2 -2
- package/es/endpoints/mcp.mjs +116 -87
- package/es/endpoints/sse.mjs +3 -3
- package/es/endpoints/vue-devtools.d.ts +5 -2
- package/es/endpoints/vue-devtools.mjs +3 -4
- package/es/index.mjs +29 -6
- package/lib/client.js +2380 -2369
- package/lib/core/chrome-project.cjs +36 -0
- package/lib/core/chrome-project.d.ts +15 -0
- package/lib/core/mcp-chrome.cjs +64 -10
- package/lib/core/mcp-chrome.d.ts +12 -8
- package/lib/core/mcp-proxy.cjs +87 -13
- package/lib/core/mcp-proxy.d.ts +21 -1
- package/lib/core/mcp-tools.cjs +124 -660
- package/lib/core/mcp-tools.d.ts +51 -3
- package/lib/core/official-meta.cjs +85 -0
- package/lib/core/official-meta.d.ts +9 -0
- package/lib/core/official-tools.cjs +70 -0
- package/lib/core/official-tools.d.ts +7 -0
- package/lib/core/proxy-server.cjs +16 -13
- package/lib/core/service.cjs +4 -5
- package/lib/endpoints/context.cjs +1 -1
- package/lib/endpoints/host-events.cjs +0 -1
- package/lib/endpoints/index.cjs +2 -2
- package/lib/endpoints/index.d.ts +2 -2
- package/lib/endpoints/mcp.cjs +114 -85
- package/lib/endpoints/mcp.d.ts +2 -2
- package/lib/endpoints/sse.cjs +2 -2
- package/lib/endpoints/vue-devtools.cjs +4 -5
- package/lib/endpoints/vue-devtools.d.ts +5 -2
- package/lib/index.cjs +29 -7
- package/package.json +10 -7
- package/es/utils/format-bridge.d.ts +0 -9
- package/es/utils/format-bridge.mjs +0 -80
- package/es/utils/system.d.ts +0 -3
- package/es/utils/system.mjs +0 -76
- package/lib/utils/format-bridge.cjs +0 -102
- package/lib/utils/format-bridge.d.ts +0 -9
- package/lib/utils/system.cjs +0 -110
- package/lib/utils/system.d.ts +0 -3
package/lib/endpoints/mcp.cjs
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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,
|
|
115
|
-
|
|
116
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
155
|
+
return handleNewPage(res, id, mcp, args, projectOrigins, operationsOrigins);
|
|
141
156
|
}
|
|
142
|
-
|
|
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,
|
|
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(
|
|
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("
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
210
|
+
operationsOrigins,
|
|
211
|
+
includeExtensions,
|
|
203
212
|
getPageContext
|
|
204
213
|
);
|
|
205
214
|
if (filtered.length === 0) {
|
|
206
|
-
sendMcpResult(
|
|
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,
|
|
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
|
-
|
|
240
|
+
operationsOrigins,
|
|
241
|
+
includeExtensions,
|
|
227
242
|
getPageContext
|
|
228
243
|
);
|
|
229
244
|
if (filtered.length === 0) {
|
|
230
|
-
sendMcpResult(
|
|
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,
|
|
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,
|
|
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\
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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,
|
|
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,
|
|
357
|
+
const validation = await (0, import_mcp_chrome.validatePageId)(mcp, pageId, operationsOrigins, includeExtensions);
|
|
332
358
|
log.debug("handleDevTool validation", {
|
|
333
359
|
pageId,
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
origins:
|
|
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"
|
|
344
|
-
const
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
}
|
package/lib/endpoints/mcp.d.ts
CHANGED
|
@@ -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;
|
package/lib/endpoints/sse.cjs
CHANGED
|
@@ -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:
|
|
37
|
+
res.write(`data: ${JSON.stringify({ type: import_core.SSE_EVENT_TYPES.CONNECTED })}
|
|
38
38
|
|
|
39
39
|
`);
|
|
40
|
-
const statusPayload = { type:
|
|
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\
|
|
65
|
+
log.error("pageId \u65E0\u6548\u6216\u4E0D\u5728\u53EF\u64CD\u4F5C\u8303\u56F4", {
|
|
67
66
|
pageId,
|
|
68
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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 = "
|
|
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("
|
|
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
|
|
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,
|
|
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 = "
|
|
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.
|
|
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/
|
|
45
|
-
"@aipanel/
|
|
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.
|
|
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.
|
|
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,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();
|
package/es/utils/system.d.ts
DELETED