vite-plugin-opencode-assistant 1.1.25 → 1.1.26

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,6 +1,7 @@
1
1
  /**
2
2
  * 自定义 DevTools 工具定义
3
- * 所有工具自动映射到 chrome-devtools-mcp 底层工具
3
+ * 所有工具(除 devtools_list_pages)必须传入 pageId 参数,
4
+ * 代理层校验 pageId 是否为项目页面后方可调用 chrome-devtools-mcp。
4
5
  */
5
6
  export interface CustomTool {
6
7
  name: string;
@@ -1,7 +1,21 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
4
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
5
19
  var __export = (target, all) => {
6
20
  for (var name in all)
7
21
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -43,18 +57,17 @@ __export(mcp_exports, {
43
57
  module.exports = __toCommonJS(mcp_exports);
44
58
  var import_shared = require("@vite-plugin-opencode-assistant/shared");
45
59
  var import_node = require("@vite-plugin-opencode-assistant/shared/node");
46
- var import_context = require("../endpoints/context.cjs");
60
+ var import_mcp_chrome = require("../core/mcp-chrome.cjs");
47
61
  var import_mcp_tools = require("./mcp-tools.cjs");
48
62
  const log = (0, import_node.createLogger)("McpEndpoint");
49
63
  function setupMcpEndpoint(server, mcp, getPageContext) {
50
64
  server.middlewares.use((req, res, next) => __async(null, null, function* () {
51
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
65
+ var _a, _b, _c;
52
66
  if (!((_a = req.url) == null ? void 0 : _a.startsWith(import_shared.MCP_API_PATH))) return next();
53
67
  const url = new URL(req.url, `http://localhost`);
54
68
  const token = (_c = url.searchParams.get("token")) != null ? _c : (_b = req.headers["authorization"]) == null ? void 0 : _b.replace(/^Bearer /i, "");
55
69
  if (token !== mcp.accessToken) {
56
- res.writeHead(401, { "Content-Type": "application/json" });
57
- res.end(JSON.stringify({ error: "Unauthorized" }));
70
+ sendMcpJson(res, 401, { error: "Unauthorized" });
58
71
  return;
59
72
  }
60
73
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -67,15 +80,7 @@ function setupMcpEndpoint(server, mcp, getPageContext) {
67
80
  return;
68
81
  }
69
82
  if (req.method === "GET") {
70
- res.writeHead(200, {
71
- "Content-Type": "text/event-stream",
72
- "Cache-Control": "no-cache",
73
- Connection: "keep-alive",
74
- "Mcp-Session-Id": mcp.sessionId
75
- });
76
- res.write(":ok\n\n");
77
- const keepAlive = setInterval(() => res.write(":ping\n\n"), 15e3);
78
- req.on("close", () => clearInterval(keepAlive));
83
+ handleGetSse(req, res, mcp);
79
84
  return;
80
85
  }
81
86
  if (req.method === "DELETE") {
@@ -84,167 +89,186 @@ function setupMcpEndpoint(server, mcp, getPageContext) {
84
89
  return;
85
90
  }
86
91
  if (req.method === "POST") {
87
- try {
88
- const body = yield readBody(req);
89
- if (!body) {
90
- res.writeHead(400);
91
- res.end("Empty body");
92
- return;
93
- }
94
- const { method, id } = tryParseRequest(body);
95
- log.debug("MCP request", { method, body: body.substring(0, 150) });
96
- if (method === "tools/list") {
97
- res.writeHead(200, {
98
- "Content-Type": "application/json",
99
- "Mcp-Session-Id": mcp.sessionId
100
- });
101
- res.end(JSON.stringify({ jsonrpc: "2.0", id, result: { tools: import_mcp_tools.CUSTOM_TOOLS } }));
102
- return;
103
- }
104
- if (method === "tools/call") {
105
- const params = tryParseParams(body);
106
- const toolName = params == null ? void 0 : params.name;
107
- const mapped = toolName != null ? import_mcp_tools.TOOL_MAP[toolName] : void 0;
108
- if (!mapped) {
109
- res.writeHead(200, {
110
- "Content-Type": "application/json",
111
- "Mcp-Session-Id": mcp.sessionId
112
- });
113
- res.end(
114
- JSON.stringify({
115
- jsonrpc: "2.0",
116
- id,
117
- error: { code: -32601, message: `Tool not found: ${toolName}` }
118
- })
119
- );
120
- return;
121
- }
122
- if (toolName === "devtools_list_pages") {
123
- const listResult = yield mcp.callChromeDevTool("list_pages", {});
124
- const text = (_f = (_e = (_d = listResult == null ? void 0 : listResult.result) == null ? void 0 : _d.content) == null ? void 0 : _e[0]) == null ? void 0 : _f.text;
125
- const allPages = text ? (0, import_context.parseListPages)(text) : [];
126
- const pc = getPageContext();
127
- const projectOrigin = (0, import_context.getProjectOrigin)(pc.url);
128
- if (!projectOrigin) {
129
- res.writeHead(200, {
130
- "Content-Type": "application/json",
131
- "Mcp-Session-Id": mcp.sessionId
132
- });
133
- res.end(
134
- JSON.stringify({
135
- jsonrpc: "2.0",
136
- id,
137
- result: {
138
- content: [
139
- { type: "text", text: "\u6682\u65E0\u9879\u76EE\u9875\u9762\uFF0C\u8BF7\u5148\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u672C\u5730\u5F00\u53D1\u9875\u9762" }
140
- ]
141
- }
142
- })
143
- );
144
- return;
145
- }
146
- const filtered = allPages.filter((p) => p.url.startsWith(projectOrigin));
147
- const chromeSelectedPageId = (_g = allPages.find((p) => p.selected)) == null ? void 0 : _g.pageId;
148
- const resolved = yield (0, import_context.resolveChromePageId)(
149
- mcp,
150
- pc.url,
151
- pc.title,
152
- pc.sessionId,
153
- filtered,
154
- chromeSelectedPageId
155
- );
156
- const activePageId = resolved.ok ? resolved.pageId : null;
157
- res.writeHead(200, {
158
- "Content-Type": "application/json",
159
- "Mcp-Session-Id": mcp.sessionId
160
- });
161
- res.end(
162
- JSON.stringify({
163
- jsonrpc: "2.0",
164
- id,
165
- result: {
166
- content: [
167
- {
168
- type: "text",
169
- text: JSON.stringify(
170
- filtered.map((p) => ({
171
- pageId: p.pageId,
172
- url: p.url,
173
- title: p.title,
174
- active: activePageId != null ? p.pageId === activePageId : false,
175
- selected: p.selected
176
- })),
177
- null,
178
- 2
179
- )
180
- }
181
- ]
182
- }
183
- })
184
- );
185
- return;
186
- }
187
- if (toolName !== "devtools_select_page") {
188
- const checkResult = yield mcp.callChromeDevTool("list_pages", {});
189
- const checkText = (_j = (_i = (_h = checkResult == null ? void 0 : checkResult.result) == null ? void 0 : _h.content) == null ? void 0 : _i[0]) == null ? void 0 : _j.text;
190
- const checkPages = checkText ? (0, import_context.parseListPages)(checkText) : [];
191
- const pc = getPageContext();
192
- const origin = (0, import_context.getProjectOrigin)(pc.url);
193
- const hasProjectSelected = origin ? checkPages.some((p) => p.selected && p.url.startsWith(origin)) : true;
194
- if (!hasProjectSelected) {
195
- res.writeHead(200, {
196
- "Content-Type": "application/json",
197
- "Mcp-Session-Id": mcp.sessionId
198
- });
199
- res.end(
200
- JSON.stringify({
201
- jsonrpc: "2.0",
202
- id,
203
- error: {
204
- code: -32e3,
205
- message: "Chrome DevTools \u5F53\u524D\u672A\u9009\u4E2D\u9879\u76EE\u9875\u9762\uFF0C\u8BF7\u5148\u8C03\u7528 devtools_list_pages \u67E5\u770B\u53EF\u7528\u9875\u9762\uFF0C\u518D\u8C03\u7528 devtools_select_page \u9009\u62E9\u76EE\u6807\u9875\u9762"
206
- }
207
- })
208
- );
209
- return;
210
- }
211
- }
212
- const forwardBody = JSON.stringify({
213
- jsonrpc: "2.0",
214
- id,
215
- method: "tools/call",
216
- params: {
217
- name: mapped,
218
- arguments: (params == null ? void 0 : params.arguments) || {}
219
- }
220
- });
221
- const responseText2 = yield mcp.forward(forwardBody);
222
- log.debug("MCP response", { method: toolName, response: responseText2.substring(0, 100) });
223
- res.writeHead(200, {
224
- "Content-Type": "application/json",
225
- "Mcp-Session-Id": mcp.sessionId
226
- });
227
- res.end(responseText2);
228
- return;
229
- }
230
- const responseText = yield mcp.forward(body);
231
- res.writeHead(200, { "Content-Type": "application/json", "Mcp-Session-Id": mcp.sessionId });
232
- res.end(responseText);
233
- } catch (e) {
234
- log.debug("MCP POST error", { error: e.message });
235
- res.writeHead(500, { "Content-Type": "application/json" });
236
- res.end(
237
- JSON.stringify({
238
- jsonrpc: "2.0",
239
- error: { code: -32603, message: e.message }
240
- })
241
- );
242
- }
92
+ yield handlePost(req, res, mcp, (0, import_mcp_chrome.getProjectOrigins)(server), getPageContext);
243
93
  return;
244
94
  }
245
95
  next();
246
96
  }));
247
97
  }
98
+ function handleGetSse(req, res, mcp) {
99
+ res.writeHead(200, {
100
+ "Content-Type": "text/event-stream",
101
+ "Cache-Control": "no-cache",
102
+ Connection: "keep-alive",
103
+ "Mcp-Session-Id": mcp.sessionId
104
+ });
105
+ res.write(":ok\n\n");
106
+ const keepAlive = setInterval(() => res.write(":ping\n\n"), 15e3);
107
+ req.on("close", () => clearInterval(keepAlive));
108
+ }
109
+ function handlePost(req, res, mcp, projectOrigins, getPageContext) {
110
+ return __async(this, null, function* () {
111
+ try {
112
+ const body = yield readBody(req);
113
+ if (!body) {
114
+ res.writeHead(400);
115
+ res.end("Empty body");
116
+ return;
117
+ }
118
+ const { method, id } = tryParseRequest(body);
119
+ log.debug("MCP request", { method, body: body.substring(0, 150) });
120
+ switch (method) {
121
+ case "tools/list":
122
+ return handleToolsList(res, id, mcp.sessionId);
123
+ case "tools/call":
124
+ return handleToolsCall(res, id, body, mcp, projectOrigins, getPageContext);
125
+ default:
126
+ return handleForward(res, body, mcp);
127
+ }
128
+ } catch (e) {
129
+ log.debug("MCP POST error", { error: e.message });
130
+ sendMcpJson(res, 500, {
131
+ jsonrpc: "2.0",
132
+ error: { code: -32603, message: e.message }
133
+ });
134
+ }
135
+ });
136
+ }
137
+ function handleToolsList(res, id, sessionId) {
138
+ sendMcpJson(res, 200, { jsonrpc: "2.0", id, result: { tools: import_mcp_tools.CUSTOM_TOOLS } }, sessionId);
139
+ }
140
+ function handleToolsCall(res, id, body, mcp, projectOrigins, getPageContext) {
141
+ const params = tryParseParams(body);
142
+ const toolName = params == null ? void 0 : params.name;
143
+ const mapped = toolName != null ? import_mcp_tools.TOOL_MAP[toolName] : void 0;
144
+ if (!mapped) {
145
+ sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
146
+ return;
147
+ }
148
+ switch (toolName) {
149
+ case "devtools_list_pages":
150
+ return handleListPages(res, id, mcp, projectOrigins, getPageContext);
151
+ default:
152
+ return handleDevTool(res, id, mcp, mapped, (params == null ? void 0 : params.arguments) || {}, projectOrigins);
153
+ }
154
+ }
155
+ function handleListPages(res, id, mcp, projectOrigins, getPageContext) {
156
+ return __async(this, null, function* () {
157
+ var _a, _b, _c, _d;
158
+ const listResult = yield mcp.callChromeDevTool("list_pages", {});
159
+ const text = (_c = (_b = (_a = listResult == null ? void 0 : listResult.result) == null ? void 0 : _a.content) == null ? void 0 : _b[0]) == null ? void 0 : _c.text;
160
+ const allPages = text ? (0, import_mcp_chrome.parseListPages)(text) : [];
161
+ log.debug("Chrome pages", { total: allPages.length, urls: allPages.map((p) => p.url) });
162
+ log.debug("project origins", { origins: projectOrigins });
163
+ const filtered = allPages.filter((p) => (0, import_mcp_chrome.isProjectPage)(p.url, projectOrigins));
164
+ log.debug("filtered pages", { count: filtered.length, pageIds: filtered.map((p) => p.pageId) });
165
+ if (filtered.length === 0) {
166
+ 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);
167
+ return;
168
+ }
169
+ const pc = getPageContext();
170
+ const chromeSelectedPageId = (_d = allPages.find((p) => p.selected)) == null ? void 0 : _d.pageId;
171
+ const resolved = yield (0, import_mcp_chrome.resolveChromePageId)(
172
+ mcp,
173
+ pc.url,
174
+ pc.title,
175
+ projectOrigins,
176
+ pc.sessionId,
177
+ filtered,
178
+ chromeSelectedPageId
179
+ );
180
+ const activePageId = resolved.ok ? resolved.pageId : null;
181
+ const pageList = filtered.map((p) => ({
182
+ pageId: p.pageId,
183
+ url: p.url,
184
+ title: p.title,
185
+ active: activePageId != null ? p.pageId === activePageId : false,
186
+ selected: p.selected
187
+ }));
188
+ sendMcpResult(res, id, JSON.stringify(pageList, null, 2), mcp.sessionId);
189
+ });
190
+ }
191
+ function handleDevTool(res, id, mcp, mapped, args, projectOrigins) {
192
+ return __async(this, null, function* () {
193
+ var _a, _b, _c;
194
+ const pageId = args["pageId"];
195
+ if (typeof pageId !== "number") {
196
+ sendMcpError(
197
+ res,
198
+ id,
199
+ -32e3,
200
+ "\u7F3A\u5C11 pageId \u53C2\u6570\uFF0C\u8BF7\u5148\u8C03\u7528 devtools_list_pages \u83B7\u53D6\u53EF\u7528\u9875\u9762\u5217\u8868",
201
+ mcp.sessionId
202
+ );
203
+ return;
204
+ }
205
+ const checkResult = yield mcp.callChromeDevTool("list_pages", {});
206
+ const checkText = (_c = (_b = (_a = checkResult == null ? void 0 : checkResult.result) == null ? void 0 : _a.content) == null ? void 0 : _b[0]) == null ? void 0 : _c.text;
207
+ const checkPages = checkText ? (0, import_mcp_chrome.parseListPages)(checkText) : [];
208
+ const projectPages = checkPages.filter((p) => (0, import_mcp_chrome.isProjectPage)(p.url, projectOrigins));
209
+ const isValid = projectPages.some((p) => p.pageId === pageId);
210
+ log.debug("handleDevTool validation", {
211
+ pageId,
212
+ totalChromePages: checkPages.length,
213
+ projectPages: projectPages.length,
214
+ projectPageIds: projectPages.map((p) => p.pageId),
215
+ origins: projectOrigins,
216
+ isValid
217
+ });
218
+ if (!isValid) {
219
+ sendMcpError(res, id, -32e3, "pageId \u65E0\u6548\u6216\u975E\u9879\u76EE\u9875\u9762", mcp.sessionId);
220
+ return;
221
+ }
222
+ yield mcp.callChromeDevTool("select_page", { pageId, bringToFront: false });
223
+ const forwardArgs = __spreadValues({}, args);
224
+ delete forwardArgs["pageId"];
225
+ const forwardBody = JSON.stringify({
226
+ jsonrpc: "2.0",
227
+ id,
228
+ method: "tools/call",
229
+ params: { name: mapped, arguments: forwardArgs }
230
+ });
231
+ const responseText = yield mcp.forward(forwardBody);
232
+ log.debug("MCP response", { mapped, response: responseText.substring(0, 100) });
233
+ sendMcpJson(res, 200, responseText, mcp.sessionId);
234
+ });
235
+ }
236
+ function handleForward(res, body, mcp) {
237
+ return __async(this, null, function* () {
238
+ const responseText = yield mcp.forward(body);
239
+ sendMcpJson(res, 200, responseText, mcp.sessionId);
240
+ });
241
+ }
242
+ function sendMcpResult(res, id, text, sessionId) {
243
+ sendMcpJson(
244
+ res,
245
+ 200,
246
+ {
247
+ jsonrpc: "2.0",
248
+ id,
249
+ result: { content: [{ type: "text", text }] }
250
+ },
251
+ sessionId
252
+ );
253
+ }
254
+ function sendMcpError(res, id, code, message, sessionId) {
255
+ sendMcpJson(
256
+ res,
257
+ 200,
258
+ {
259
+ jsonrpc: "2.0",
260
+ id,
261
+ error: { code, message }
262
+ },
263
+ sessionId
264
+ );
265
+ }
266
+ function sendMcpJson(res, statusCode, body, sessionId) {
267
+ const headers = { "Content-Type": "application/json" };
268
+ if (sessionId) headers["Mcp-Session-Id"] = sessionId;
269
+ res.writeHead(statusCode, headers);
270
+ res.end(typeof body === "string" ? body : JSON.stringify(body));
271
+ }
248
272
  function readBody(req, maxSize = 1024 * 1024) {
249
273
  return new Promise((resolve, reject) => {
250
274
  let body = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-opencode-assistant",
3
- "version": "1.1.25",
3
+ "version": "1.1.26",
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",
@@ -38,15 +38,15 @@
38
38
  "chrome-devtools-mcp": "^1.6.0",
39
39
  "unplugin-vue-inspector": "^3.0.0",
40
40
  "execa": "^9.6.1",
41
- "@vite-plugin-opencode-assistant/opencode": "1.1.25",
42
- "@vite-plugin-opencode-assistant/shared": "1.1.25",
43
- "@vite-plugin-opencode-assistant/components": "1.1.25"
41
+ "@vite-plugin-opencode-assistant/shared": "1.1.26",
42
+ "@vite-plugin-opencode-assistant/opencode": "1.1.26",
43
+ "@vite-plugin-opencode-assistant/components": "1.1.26"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "vite": ">=5.0.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@vite-plugin-opencode-assistant/client": "1.1.25"
49
+ "@vite-plugin-opencode-assistant/client": "1.1.26"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "pagoda-cli build && vite build -c vite.client.config.ts",