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,3 +1,19 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
1
17
  var __async = (__this, __arguments, generator) => {
2
18
  return new Promise((resolve, reject) => {
3
19
  var fulfilled = (value) => {
@@ -20,18 +36,22 @@ var __async = (__this, __arguments, generator) => {
20
36
  };
21
37
  import { MCP_API_PATH } from "@vite-plugin-opencode-assistant/shared";
22
38
  import { createLogger } from "@vite-plugin-opencode-assistant/shared/node";
23
- import { parseListPages, resolveChromePageId, getProjectOrigin } from "../endpoints/context.mjs";
39
+ import {
40
+ parseListPages,
41
+ resolveChromePageId,
42
+ getProjectOrigins,
43
+ isProjectPage
44
+ } from "../core/mcp-chrome.mjs";
24
45
  import { CUSTOM_TOOLS, TOOL_MAP } from "./mcp-tools.mjs";
25
46
  const log = createLogger("McpEndpoint");
26
47
  function setupMcpEndpoint(server, mcp, getPageContext) {
27
48
  server.middlewares.use((req, res, next) => __async(null, null, function* () {
28
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
49
+ var _a, _b, _c;
29
50
  if (!((_a = req.url) == null ? void 0 : _a.startsWith(MCP_API_PATH))) return next();
30
51
  const url = new URL(req.url, `http://localhost`);
31
52
  const token = (_c = url.searchParams.get("token")) != null ? _c : (_b = req.headers["authorization"]) == null ? void 0 : _b.replace(/^Bearer /i, "");
32
53
  if (token !== mcp.accessToken) {
33
- res.writeHead(401, { "Content-Type": "application/json" });
34
- res.end(JSON.stringify({ error: "Unauthorized" }));
54
+ sendMcpJson(res, 401, { error: "Unauthorized" });
35
55
  return;
36
56
  }
37
57
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -44,15 +64,7 @@ function setupMcpEndpoint(server, mcp, getPageContext) {
44
64
  return;
45
65
  }
46
66
  if (req.method === "GET") {
47
- res.writeHead(200, {
48
- "Content-Type": "text/event-stream",
49
- "Cache-Control": "no-cache",
50
- Connection: "keep-alive",
51
- "Mcp-Session-Id": mcp.sessionId
52
- });
53
- res.write(":ok\n\n");
54
- const keepAlive = setInterval(() => res.write(":ping\n\n"), 15e3);
55
- req.on("close", () => clearInterval(keepAlive));
67
+ handleGetSse(req, res, mcp);
56
68
  return;
57
69
  }
58
70
  if (req.method === "DELETE") {
@@ -61,167 +73,186 @@ function setupMcpEndpoint(server, mcp, getPageContext) {
61
73
  return;
62
74
  }
63
75
  if (req.method === "POST") {
64
- try {
65
- const body = yield readBody(req);
66
- if (!body) {
67
- res.writeHead(400);
68
- res.end("Empty body");
69
- return;
70
- }
71
- const { method, id } = tryParseRequest(body);
72
- log.debug("MCP request", { method, body: body.substring(0, 150) });
73
- if (method === "tools/list") {
74
- res.writeHead(200, {
75
- "Content-Type": "application/json",
76
- "Mcp-Session-Id": mcp.sessionId
77
- });
78
- res.end(JSON.stringify({ jsonrpc: "2.0", id, result: { tools: CUSTOM_TOOLS } }));
79
- return;
80
- }
81
- if (method === "tools/call") {
82
- const params = tryParseParams(body);
83
- const toolName = params == null ? void 0 : params.name;
84
- const mapped = toolName != null ? TOOL_MAP[toolName] : void 0;
85
- if (!mapped) {
86
- res.writeHead(200, {
87
- "Content-Type": "application/json",
88
- "Mcp-Session-Id": mcp.sessionId
89
- });
90
- res.end(
91
- JSON.stringify({
92
- jsonrpc: "2.0",
93
- id,
94
- error: { code: -32601, message: `Tool not found: ${toolName}` }
95
- })
96
- );
97
- return;
98
- }
99
- if (toolName === "devtools_list_pages") {
100
- const listResult = yield mcp.callChromeDevTool("list_pages", {});
101
- 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;
102
- const allPages = text ? parseListPages(text) : [];
103
- const pc = getPageContext();
104
- const projectOrigin = getProjectOrigin(pc.url);
105
- if (!projectOrigin) {
106
- res.writeHead(200, {
107
- "Content-Type": "application/json",
108
- "Mcp-Session-Id": mcp.sessionId
109
- });
110
- res.end(
111
- JSON.stringify({
112
- jsonrpc: "2.0",
113
- id,
114
- result: {
115
- content: [
116
- { 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" }
117
- ]
118
- }
119
- })
120
- );
121
- return;
122
- }
123
- const filtered = allPages.filter((p) => p.url.startsWith(projectOrigin));
124
- const chromeSelectedPageId = (_g = allPages.find((p) => p.selected)) == null ? void 0 : _g.pageId;
125
- const resolved = yield resolveChromePageId(
126
- mcp,
127
- pc.url,
128
- pc.title,
129
- pc.sessionId,
130
- filtered,
131
- chromeSelectedPageId
132
- );
133
- const activePageId = resolved.ok ? resolved.pageId : null;
134
- res.writeHead(200, {
135
- "Content-Type": "application/json",
136
- "Mcp-Session-Id": mcp.sessionId
137
- });
138
- res.end(
139
- JSON.stringify({
140
- jsonrpc: "2.0",
141
- id,
142
- result: {
143
- content: [
144
- {
145
- type: "text",
146
- text: JSON.stringify(
147
- filtered.map((p) => ({
148
- pageId: p.pageId,
149
- url: p.url,
150
- title: p.title,
151
- active: activePageId != null ? p.pageId === activePageId : false,
152
- selected: p.selected
153
- })),
154
- null,
155
- 2
156
- )
157
- }
158
- ]
159
- }
160
- })
161
- );
162
- return;
163
- }
164
- if (toolName !== "devtools_select_page") {
165
- const checkResult = yield mcp.callChromeDevTool("list_pages", {});
166
- 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;
167
- const checkPages = checkText ? parseListPages(checkText) : [];
168
- const pc = getPageContext();
169
- const origin = getProjectOrigin(pc.url);
170
- const hasProjectSelected = origin ? checkPages.some((p) => p.selected && p.url.startsWith(origin)) : true;
171
- if (!hasProjectSelected) {
172
- res.writeHead(200, {
173
- "Content-Type": "application/json",
174
- "Mcp-Session-Id": mcp.sessionId
175
- });
176
- res.end(
177
- JSON.stringify({
178
- jsonrpc: "2.0",
179
- id,
180
- error: {
181
- code: -32e3,
182
- 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"
183
- }
184
- })
185
- );
186
- return;
187
- }
188
- }
189
- const forwardBody = JSON.stringify({
190
- jsonrpc: "2.0",
191
- id,
192
- method: "tools/call",
193
- params: {
194
- name: mapped,
195
- arguments: (params == null ? void 0 : params.arguments) || {}
196
- }
197
- });
198
- const responseText2 = yield mcp.forward(forwardBody);
199
- log.debug("MCP response", { method: toolName, response: responseText2.substring(0, 100) });
200
- res.writeHead(200, {
201
- "Content-Type": "application/json",
202
- "Mcp-Session-Id": mcp.sessionId
203
- });
204
- res.end(responseText2);
205
- return;
206
- }
207
- const responseText = yield mcp.forward(body);
208
- res.writeHead(200, { "Content-Type": "application/json", "Mcp-Session-Id": mcp.sessionId });
209
- res.end(responseText);
210
- } catch (e) {
211
- log.debug("MCP POST error", { error: e.message });
212
- res.writeHead(500, { "Content-Type": "application/json" });
213
- res.end(
214
- JSON.stringify({
215
- jsonrpc: "2.0",
216
- error: { code: -32603, message: e.message }
217
- })
218
- );
219
- }
76
+ yield handlePost(req, res, mcp, getProjectOrigins(server), getPageContext);
220
77
  return;
221
78
  }
222
79
  next();
223
80
  }));
224
81
  }
82
+ function handleGetSse(req, res, mcp) {
83
+ res.writeHead(200, {
84
+ "Content-Type": "text/event-stream",
85
+ "Cache-Control": "no-cache",
86
+ Connection: "keep-alive",
87
+ "Mcp-Session-Id": mcp.sessionId
88
+ });
89
+ res.write(":ok\n\n");
90
+ const keepAlive = setInterval(() => res.write(":ping\n\n"), 15e3);
91
+ req.on("close", () => clearInterval(keepAlive));
92
+ }
93
+ function handlePost(req, res, mcp, projectOrigins, getPageContext) {
94
+ return __async(this, null, function* () {
95
+ try {
96
+ const body = yield readBody(req);
97
+ if (!body) {
98
+ res.writeHead(400);
99
+ res.end("Empty body");
100
+ return;
101
+ }
102
+ const { method, id } = tryParseRequest(body);
103
+ log.debug("MCP request", { method, body: body.substring(0, 150) });
104
+ switch (method) {
105
+ case "tools/list":
106
+ return handleToolsList(res, id, mcp.sessionId);
107
+ case "tools/call":
108
+ return handleToolsCall(res, id, body, mcp, projectOrigins, getPageContext);
109
+ default:
110
+ return handleForward(res, body, mcp);
111
+ }
112
+ } catch (e) {
113
+ log.debug("MCP POST error", { error: e.message });
114
+ sendMcpJson(res, 500, {
115
+ jsonrpc: "2.0",
116
+ error: { code: -32603, message: e.message }
117
+ });
118
+ }
119
+ });
120
+ }
121
+ function handleToolsList(res, id, sessionId) {
122
+ sendMcpJson(res, 200, { jsonrpc: "2.0", id, result: { tools: CUSTOM_TOOLS } }, sessionId);
123
+ }
124
+ function handleToolsCall(res, id, body, mcp, projectOrigins, getPageContext) {
125
+ const params = tryParseParams(body);
126
+ const toolName = params == null ? void 0 : params.name;
127
+ const mapped = toolName != null ? TOOL_MAP[toolName] : void 0;
128
+ if (!mapped) {
129
+ sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
130
+ return;
131
+ }
132
+ switch (toolName) {
133
+ case "devtools_list_pages":
134
+ return handleListPages(res, id, mcp, projectOrigins, getPageContext);
135
+ default:
136
+ return handleDevTool(res, id, mcp, mapped, (params == null ? void 0 : params.arguments) || {}, projectOrigins);
137
+ }
138
+ }
139
+ function handleListPages(res, id, mcp, projectOrigins, getPageContext) {
140
+ return __async(this, null, function* () {
141
+ var _a, _b, _c, _d;
142
+ const listResult = yield mcp.callChromeDevTool("list_pages", {});
143
+ 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;
144
+ const allPages = text ? parseListPages(text) : [];
145
+ log.debug("Chrome pages", { total: allPages.length, urls: allPages.map((p) => p.url) });
146
+ log.debug("project origins", { origins: projectOrigins });
147
+ const filtered = allPages.filter((p) => isProjectPage(p.url, projectOrigins));
148
+ log.debug("filtered pages", { count: filtered.length, pageIds: filtered.map((p) => p.pageId) });
149
+ if (filtered.length === 0) {
150
+ 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);
151
+ return;
152
+ }
153
+ const pc = getPageContext();
154
+ const chromeSelectedPageId = (_d = allPages.find((p) => p.selected)) == null ? void 0 : _d.pageId;
155
+ const resolved = yield resolveChromePageId(
156
+ mcp,
157
+ pc.url,
158
+ pc.title,
159
+ projectOrigins,
160
+ pc.sessionId,
161
+ filtered,
162
+ chromeSelectedPageId
163
+ );
164
+ const activePageId = resolved.ok ? resolved.pageId : null;
165
+ const pageList = filtered.map((p) => ({
166
+ pageId: p.pageId,
167
+ url: p.url,
168
+ title: p.title,
169
+ active: activePageId != null ? p.pageId === activePageId : false,
170
+ selected: p.selected
171
+ }));
172
+ sendMcpResult(res, id, JSON.stringify(pageList, null, 2), mcp.sessionId);
173
+ });
174
+ }
175
+ function handleDevTool(res, id, mcp, mapped, args, projectOrigins) {
176
+ return __async(this, null, function* () {
177
+ var _a, _b, _c;
178
+ const pageId = args["pageId"];
179
+ if (typeof pageId !== "number") {
180
+ sendMcpError(
181
+ res,
182
+ id,
183
+ -32e3,
184
+ "\u7F3A\u5C11 pageId \u53C2\u6570\uFF0C\u8BF7\u5148\u8C03\u7528 devtools_list_pages \u83B7\u53D6\u53EF\u7528\u9875\u9762\u5217\u8868",
185
+ mcp.sessionId
186
+ );
187
+ return;
188
+ }
189
+ const checkResult = yield mcp.callChromeDevTool("list_pages", {});
190
+ 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;
191
+ const checkPages = checkText ? parseListPages(checkText) : [];
192
+ const projectPages = checkPages.filter((p) => isProjectPage(p.url, projectOrigins));
193
+ const isValid = projectPages.some((p) => p.pageId === pageId);
194
+ log.debug("handleDevTool validation", {
195
+ pageId,
196
+ totalChromePages: checkPages.length,
197
+ projectPages: projectPages.length,
198
+ projectPageIds: projectPages.map((p) => p.pageId),
199
+ origins: projectOrigins,
200
+ isValid
201
+ });
202
+ if (!isValid) {
203
+ sendMcpError(res, id, -32e3, "pageId \u65E0\u6548\u6216\u975E\u9879\u76EE\u9875\u9762", mcp.sessionId);
204
+ return;
205
+ }
206
+ yield mcp.callChromeDevTool("select_page", { pageId, bringToFront: false });
207
+ const forwardArgs = __spreadValues({}, args);
208
+ delete forwardArgs["pageId"];
209
+ const forwardBody = JSON.stringify({
210
+ jsonrpc: "2.0",
211
+ id,
212
+ method: "tools/call",
213
+ params: { name: mapped, arguments: forwardArgs }
214
+ });
215
+ const responseText = yield mcp.forward(forwardBody);
216
+ log.debug("MCP response", { mapped, response: responseText.substring(0, 100) });
217
+ sendMcpJson(res, 200, responseText, mcp.sessionId);
218
+ });
219
+ }
220
+ function handleForward(res, body, mcp) {
221
+ return __async(this, null, function* () {
222
+ const responseText = yield mcp.forward(body);
223
+ sendMcpJson(res, 200, responseText, mcp.sessionId);
224
+ });
225
+ }
226
+ function sendMcpResult(res, id, text, sessionId) {
227
+ sendMcpJson(
228
+ res,
229
+ 200,
230
+ {
231
+ jsonrpc: "2.0",
232
+ id,
233
+ result: { content: [{ type: "text", text }] }
234
+ },
235
+ sessionId
236
+ );
237
+ }
238
+ function sendMcpError(res, id, code, message, sessionId) {
239
+ sendMcpJson(
240
+ res,
241
+ 200,
242
+ {
243
+ jsonrpc: "2.0",
244
+ id,
245
+ error: { code, message }
246
+ },
247
+ sessionId
248
+ );
249
+ }
250
+ function sendMcpJson(res, statusCode, body, sessionId) {
251
+ const headers = { "Content-Type": "application/json" };
252
+ if (sessionId) headers["Mcp-Session-Id"] = sessionId;
253
+ res.writeHead(statusCode, headers);
254
+ res.end(typeof body === "string" ? body : JSON.stringify(body));
255
+ }
225
256
  function readBody(req, maxSize = 1024 * 1024) {
226
257
  return new Promise((resolve, reject) => {
227
258
  let body = "";
@@ -0,0 +1,174 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var __async = (__this, __arguments, generator) => {
19
+ return new Promise((resolve, reject) => {
20
+ var fulfilled = (value) => {
21
+ try {
22
+ step(generator.next(value));
23
+ } catch (e) {
24
+ reject(e);
25
+ }
26
+ };
27
+ var rejected = (value) => {
28
+ try {
29
+ step(generator.throw(value));
30
+ } catch (e) {
31
+ reject(e);
32
+ }
33
+ };
34
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
35
+ step((generator = generator.apply(__this, __arguments)).next());
36
+ });
37
+ };
38
+ var mcp_chrome_exports = {};
39
+ __export(mcp_chrome_exports, {
40
+ extractEvalValue: () => extractEvalValue,
41
+ getProjectOrigins: () => getProjectOrigins,
42
+ isProjectPage: () => isProjectPage,
43
+ parseListPages: () => parseListPages,
44
+ resolveChromePageId: () => resolveChromePageId
45
+ });
46
+ module.exports = __toCommonJS(mcp_chrome_exports);
47
+ var import_node = require("@vite-plugin-opencode-assistant/shared/node");
48
+ const log = (0, import_node.createLogger)("McpChrome");
49
+ function getProjectOrigins(server) {
50
+ var _a, _b, _c, _d;
51
+ const local = (_b = (_a = server.resolvedUrls) == null ? void 0 : _a.local) != null ? _b : [];
52
+ const network = (_d = (_c = server.resolvedUrls) == null ? void 0 : _c.network) != null ? _d : [];
53
+ const origins = [...new Set([...local, ...network].map((u) => new URL(u).origin))];
54
+ log.debug("project origins", { origins });
55
+ return origins;
56
+ }
57
+ function isProjectPage(url, origins) {
58
+ return origins.some((origin) => url.startsWith(origin));
59
+ }
60
+ function parseListPages(text) {
61
+ const pages = [];
62
+ for (const line of text.split("\n")) {
63
+ const match = line.match(/^(\d+):\s*(.+?)\s*\((\S+)\)/);
64
+ if (match) {
65
+ pages.push({
66
+ pageId: parseInt(match[1], 10),
67
+ title: match[2].replace(/\s*\.{3}$/, "").trim(),
68
+ url: match[3],
69
+ selected: /\[selected\]/i.test(line)
70
+ });
71
+ }
72
+ }
73
+ return pages;
74
+ }
75
+ function extractEvalValue(text) {
76
+ if (!text) return void 0;
77
+ const marker = "```json\n";
78
+ const startIdx = text.indexOf(marker);
79
+ if (startIdx < 0) return text.trim();
80
+ const contentStart = startIdx + marker.length;
81
+ const endIdx = text.indexOf("\n```", contentStart);
82
+ const jsonStr = endIdx < 0 ? text.substring(contentStart) : text.substring(contentStart, endIdx);
83
+ try {
84
+ return JSON.parse(jsonStr.trim());
85
+ } catch (e) {
86
+ return jsonStr.trim();
87
+ }
88
+ }
89
+ function resolveChromePageId(mcp, url, title, projectOrigins, sessionId, pages, chromeSelectedPageId) {
90
+ return __async(this, null, function* () {
91
+ var _a, _b, _c, _d, _e, _f, _g;
92
+ if (!mcp || !mcp.isRunning) {
93
+ const reason = !mcp ? "MCP \u6A21\u5757\u672A\u521D\u59CB\u5316" : "Chrome DevTools MCP \u8FDB\u7A0B\u672A\u542F\u52A8";
94
+ log.debug(`resolveChromePageId: ${reason}`);
95
+ return { ok: false, error: reason };
96
+ }
97
+ if (!url) {
98
+ return { ok: false, error: "\u9875\u9762 URL \u4E3A\u7A7A\uFF0C\u5C1A\u672A\u6536\u5230\u4E0A\u4E0B\u6587\u4FE1\u606F" };
99
+ }
100
+ try {
101
+ if (!pages) {
102
+ const listResult = yield mcp.callChromeDevTool("list_pages", {});
103
+ 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;
104
+ if (!text) {
105
+ return { ok: false, error: "\u65E0\u6CD5\u83B7\u53D6 Chrome \u9875\u9762\u5217\u8868\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u6253\u5F00\u76EE\u6807\u9875\u9762" };
106
+ }
107
+ const allPages = parseListPages(text);
108
+ chromeSelectedPageId = (_d = allPages.find((p) => p.selected)) == null ? void 0 : _d.pageId;
109
+ pages = allPages.filter((p) => isProjectPage(p.url, projectOrigins));
110
+ }
111
+ log.debug("resolveChromePageId: list_pages result", {
112
+ pages: pages.map((p) => ({ id: p.pageId, url: p.url, title: p.title.substring(0, 40) })),
113
+ target: { url, title }
114
+ });
115
+ const normalizeUrl = (u) => u.replace(/\/$/, "");
116
+ const targetUrl = normalizeUrl(url);
117
+ if (sessionId) {
118
+ let matchedPageId = null;
119
+ for (const page of pages) {
120
+ yield mcp.callChromeDevTool("select_page", { pageId: page.pageId, bringToFront: false });
121
+ const evalResult = yield mcp.callChromeDevTool("evaluate_script", {
122
+ function: "() => sessionStorage.getItem('_opencode_pk')"
123
+ });
124
+ const rawText = (_g = (_f = (_e = evalResult == null ? void 0 : evalResult.result) == null ? void 0 : _e.content) == null ? void 0 : _f[0]) == null ? void 0 : _g.text;
125
+ const extracted = extractEvalValue(rawText);
126
+ if (extracted === sessionId) {
127
+ matchedPageId = page.pageId;
128
+ break;
129
+ }
130
+ }
131
+ if (chromeSelectedPageId != null && chromeSelectedPageId !== matchedPageId) {
132
+ yield mcp.callChromeDevTool("select_page", {
133
+ pageId: chromeSelectedPageId,
134
+ bringToFront: false
135
+ });
136
+ }
137
+ if (matchedPageId != null) {
138
+ return { ok: true, pageId: matchedPageId };
139
+ }
140
+ return {
141
+ ok: false,
142
+ error: `\u672A\u80FD\u901A\u8FC7\u4F1A\u8BDD\u6807\u8BC6\u627E\u5230\u76EE\u6807\u9875\u9762\uFF0C\u8BF7\u786E\u8BA4\u76EE\u6807\u9875\u9762\u5DF2\u6253\u5F00`
143
+ };
144
+ }
145
+ const matches = pages.filter((p) => normalizeUrl(p.url) === targetUrl);
146
+ if (matches.length === 0) {
147
+ return {
148
+ ok: false,
149
+ error: `Chrome \u4E2D\u672A\u627E\u5230\u5339\u914D\u7684\u9875\u9762 (${targetUrl})\uFF0C\u8BF7\u786E\u8BA4\u672C\u5730\u5F00\u53D1\u9875\u9762\u5DF2\u5728 Chrome DevTools \u4E2D\u6253\u5F00`
150
+ };
151
+ }
152
+ if (matches.length === 1) {
153
+ log.debug("resolveChromePageId: URL matched", { pageId: matches[0].pageId });
154
+ return { ok: true, pageId: matches[0].pageId };
155
+ }
156
+ return {
157
+ ok: false,
158
+ error: `\u5B58\u5728 ${matches.length} \u4E2A\u540C URL \u7684\u9875\u9762\uFF0C\u7F3A\u5C11\u9875\u9762\u6807\u8BC6\u65E0\u6CD5\u533A\u5206\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u540E\u91CD\u8BD5`
159
+ };
160
+ } catch (err) {
161
+ const msg = `MCP \u8C03\u7528\u5931\u8D25: ${err.message}`;
162
+ log.debug("Failed to resolve pageId via MCP", { error: err.message });
163
+ return { ok: false, error: msg };
164
+ }
165
+ });
166
+ }
167
+ // Annotate the CommonJS export names for ESM import in node:
168
+ 0 && (module.exports = {
169
+ extractEvalValue,
170
+ getProjectOrigins,
171
+ isProjectPage,
172
+ parseListPages,
173
+ resolveChromePageId
174
+ });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * MCP Chrome 页面相关工具
3
+ *
4
+ * 专注于 Chrome DevTools 页面匹配与解析逻辑
5
+ */
6
+ import type { ViteDevServer } from "vite";
7
+ import type { McpProxy } from "./mcp-proxy";
8
+ /** 从 Vite 服务器解析的所有 URL 中提取项目 origin 列表 */
9
+ export declare function getProjectOrigins(server: ViteDevServer): string[];
10
+ /** 判断页面 URL 是否属于项目的某个 origin */
11
+ export declare function isProjectPage(url: string, origins: string[]): boolean;
12
+ export interface PageInfo {
13
+ pageId: number;
14
+ url: string;
15
+ title: string;
16
+ /** Chrome DevTools 当前选中的页面 */
17
+ selected: boolean;
18
+ }
19
+ /** 解析 list_pages 返回的文本。格式:"56: Title (URL) [selected]" */
20
+ export declare function parseListPages(text: string): PageInfo[];
21
+ /** 从 evaluate_script 返回的格式化文本中提取 JSON 值 */
22
+ export declare function extractEvalValue(text: string | undefined): string | undefined;
23
+ type PageIdResult = {
24
+ ok: true;
25
+ pageId: number;
26
+ } | {
27
+ ok: false;
28
+ error: string;
29
+ };
30
+ /**
31
+ * 通过 MCP 解析当前页面对应的 Chrome DevTools pageId
32
+ *
33
+ * 策略:
34
+ * 1. list_pages → 解析每行的 "pageId: title (URL)" 格式
35
+ * 2. 优先用 sessionId 匹配(跨导航可靠,遍历所有页面 evaluate_script)
36
+ * 3. 无 sessionId 时降级为 URL 精确匹配
37
+ *
38
+ * 失败时返回具体原因,由调用方透传给 Agent。
39
+ */
40
+ export declare function resolveChromePageId(mcp: McpProxy | undefined, url: string, title: string, projectOrigins: string[], sessionId?: string, pages?: PageInfo[],
41
+ /** Chrome 当前选中的 pageId(调用方传入,避免过滤后丢失非项目页面信息) */
42
+ chromeSelectedPageId?: number): Promise<PageIdResult>;
43
+ export {};