vite-plugin-aipanel 1.2.8 → 1.2.9

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.
@@ -45,7 +45,9 @@ var __privateWrapper = (obj, member, setter, getter) => ({
45
45
  });
46
46
  var mcp_proxy_exports = {};
47
47
  __export(mcp_proxy_exports, {
48
- McpProxy: () => McpProxy
48
+ CORE_MCP_ARGS: () => CORE_MCP_ARGS,
49
+ McpProxy: () => McpProxy,
50
+ filterUserMcpArgs: () => filterUserMcpArgs
49
51
  });
50
52
  module.exports = __toCommonJS(mcp_proxy_exports);
51
53
  var import_node_child_process = require("node:child_process");
@@ -53,8 +55,36 @@ var import_node_readline = require("node:readline");
53
55
  var import_node_crypto = __toESM(require("node:crypto"));
54
56
  var import_node_path = __toESM(require("node:path"));
55
57
  var import_node = require("@aipanel/core/node");
56
- var _proc, _rl, _messageId, _internalIdBase, _pending, _args, _startPromise, _lastExit, _stderrTail, _idleTimer, _idleTimeout, _McpProxy_instances, doStart_fn, formatNotAvailable_fn, resetIdleTimer_fn;
58
+ var _proc, _rl, _messageId, _internalIdBase, _pending, _args, _env, _startPromise, _lastExit, _stderrTail, _idleTimer, _idleTimeout, _McpProxy_instances, doStart_fn, formatNotAvailable_fn, resetIdleTimer_fn;
57
59
  const log = (0, import_node.createLogger)("McpProxy");
60
+ const CORE_MCP_ARGS = [
61
+ "--auto-connect",
62
+ "--no-usage-statistics",
63
+ "--no-performance-crux",
64
+ // chrome-devtools-mcp >=1.8.0 默认开启 pageIdRouting(要求每个页面级工具都传 pageId)。
65
+ // 代理层已自行校验 pageId 并用 select_page 选中目标页面后再转发,故显式关闭,
66
+ // 避免底层工具 schema 强制必填 pageId 导致转发时的参数校验失败。
67
+ "--no-page-id-routing"
68
+ ];
69
+ const PROTECTED_FLAGS = /* @__PURE__ */ new Set([
70
+ "auto-connect",
71
+ "usage-statistics",
72
+ "performance-crux",
73
+ "page-id-routing"
74
+ ]);
75
+ function canonicalFlagName(arg) {
76
+ let flag = arg.trim();
77
+ if (!flag.startsWith("-")) return null;
78
+ flag = flag.replace(/^--?/, "").split("=")[0] ?? "";
79
+ if (flag.startsWith("no-")) flag = flag.slice(3);
80
+ return flag || null;
81
+ }
82
+ function filterUserMcpArgs(userArgs) {
83
+ return userArgs.filter((arg) => {
84
+ const name = canonicalFlagName(arg);
85
+ return name === null || !PROTECTED_FLAGS.has(name);
86
+ });
87
+ }
58
88
  function resolveChromeDevToolsMcpBin() {
59
89
  const pluginDir = (0, import_node.resolvePackageDir)("vite-plugin-aipanel");
60
90
  const require2 = (0, import_node.createPackageRequire)(pluginDir);
@@ -74,6 +104,7 @@ class McpProxy {
74
104
  __privateAdd(this, _internalIdBase, 1e6);
75
105
  __privateAdd(this, _pending, /* @__PURE__ */ new Map());
76
106
  __privateAdd(this, _args);
107
+ __privateAdd(this, _env);
77
108
  __privateAdd(this, _startPromise, null);
78
109
  /** 最近一次进程退出信息,用于生成精确错误(无退出记录时为 null) */
79
110
  __privateAdd(this, _lastExit, null);
@@ -82,15 +113,14 @@ class McpProxy {
82
113
  __privateAdd(this, _idleTimer, null);
83
114
  __privateAdd(this, _idleTimeout);
84
115
  __publicField(this, "sessionId");
85
- __privateSet(this, _args, options.args ?? [
86
- "--auto-connect",
87
- "--no-usage-statistics",
88
- "--no-performance-crux",
89
- // chrome-devtools-mcp >=1.8.0 默认开启 pageIdRouting(要求每个页面级工具都传 pageId)。
90
- // 代理层已自行校验 pageId 并用 select_page 选中目标页面后再转发,故显式关闭,
91
- // 避免底层工具 schema 强制必填 pageId 导致转发时的参数校验失败。
92
- "--no-page-id-routing"
93
- ]);
116
+ const userArgs = options.userArgs ?? [];
117
+ const filtered = filterUserMcpArgs(userArgs);
118
+ if (filtered.length !== userArgs.length) {
119
+ const dropped = userArgs.filter((a) => !filtered.includes(a));
120
+ log.warn("chrome MCP \u7528\u6237\u53C2\u6570\u4E0E\u6838\u5FC3\u53D7\u4FDD\u62A4\u9879\u51B2\u7A81\uFF0C\u5DF2\u5FFD\u7565", { dropped });
121
+ }
122
+ __privateSet(this, _args, [...filtered, ...CORE_MCP_ARGS]);
123
+ __privateSet(this, _env, options.env);
94
124
  __privateSet(this, _idleTimeout, options.idleTimeout ?? 0);
95
125
  this.sessionId = import_node_crypto.default.randomUUID();
96
126
  }
@@ -184,6 +214,7 @@ _messageId = new WeakMap();
184
214
  _internalIdBase = new WeakMap();
185
215
  _pending = new WeakMap();
186
216
  _args = new WeakMap();
217
+ _env = new WeakMap();
187
218
  _startPromise = new WeakMap();
188
219
  _lastExit = new WeakMap();
189
220
  _stderrTail = new WeakMap();
@@ -197,7 +228,8 @@ doStart_fn = async function() {
197
228
  __privateSet(this, _lastExit, null);
198
229
  __privateSet(this, _stderrTail, []);
199
230
  __privateSet(this, _proc, (0, import_node_child_process.spawn)(process.execPath, [binPath, ...__privateGet(this, _args)], {
200
- stdio: ["pipe", "pipe", "pipe"]
231
+ stdio: ["pipe", "pipe", "pipe"],
232
+ env: { ...process.env, ...__privateGet(this, _env) }
201
233
  }));
202
234
  log.debug("Using local chrome-devtools-mcp");
203
235
  } catch {
@@ -287,5 +319,7 @@ resetIdleTimer_fn = function() {
287
319
  };
288
320
  // Annotate the CommonJS export names for ESM import in node:
289
321
  0 && (module.exports = {
290
- McpProxy
322
+ CORE_MCP_ARGS,
323
+ McpProxy,
324
+ filterUserMcpArgs
291
325
  });
@@ -1,5 +1,12 @@
1
+ /** chrome-devtools-mcp 的核心受保护参数(用户不可覆盖;始终最后注入确保生效) */
2
+ export declare const CORE_MCP_ARGS: readonly ["--auto-connect", "--no-usage-statistics", "--no-performance-crux", "--no-page-id-routing"];
3
+ /** 过滤用户透传参数:剔除与核心受保护 flag 冲突的项,其余原样保留 */
4
+ export declare function filterUserMcpArgs(userArgs: readonly string[]): string[];
1
5
  export interface McpProxyOptions {
2
- args?: string[];
6
+ /** 用户透传的额外 CLI 参数(追加;与核心受保护项冲突的会被剔除) */
7
+ userArgs?: string[];
8
+ /** 用户透传的额外环境变量(合并到 process.env 之上) */
9
+ env?: Record<string, string>;
3
10
  idleTimeout?: number;
4
11
  }
5
12
  export declare class McpProxy {
@@ -32,7 +32,8 @@ __export(proxy_server_exports, {
32
32
  module.exports = __toCommonJS(proxy_server_exports);
33
33
  var import_http = __toESM(require("http"));
34
34
  var import_core = require("@aipanel/core");
35
- const log = (0, import_core.createLogger)("ProxyServer");
35
+ var import_node = require("@aipanel/core/node");
36
+ const log = (0, import_node.createLogger)("ProxyServer");
36
37
  function startProxyServer(targetUrl, port, options = {}) {
37
38
  return new Promise((resolve, reject) => {
38
39
  const target = new URL(targetUrl);
@@ -85,18 +86,20 @@ function startProxyServer(targetUrl, port, options = {}) {
85
86
  });
86
87
  proxyRes.on("end", () => {
87
88
  let body = Buffer.concat(chunks).toString("utf-8");
88
- if (body.match(/<\/head>/i)) {
89
- body = body.replace(
90
- /<\/head>/i,
91
- `<script src="${import_core.BRIDGE_SCRIPT_PATH}"></script></head>`
92
- );
93
- } else if (body.match(/<\/body>/i)) {
94
- body = body.replace(
95
- /<\/body>/i,
96
- `<script src="${import_core.BRIDGE_SCRIPT_PATH}"></script></body>`
97
- );
98
- } else {
99
- body += `<script src="${import_core.BRIDGE_SCRIPT_PATH}"></script>`;
89
+ if (bridgeScript.trim().length > 0) {
90
+ if (body.match(/<\/head>/i)) {
91
+ body = body.replace(
92
+ /<\/head>/i,
93
+ `<script src="${import_core.BRIDGE_SCRIPT_PATH}"></script></head>`
94
+ );
95
+ } else if (body.match(/<\/body>/i)) {
96
+ body = body.replace(
97
+ /<\/body>/i,
98
+ `<script src="${import_core.BRIDGE_SCRIPT_PATH}"></script></body>`
99
+ );
100
+ } else {
101
+ body += `<script src="${import_core.BRIDGE_SCRIPT_PATH}"></script>`;
102
+ }
100
103
  }
101
104
  const headers = {};
102
105
  for (const [key, value] of Object.entries(proxyRes.headers)) {
package/lib/index.cjs CHANGED
@@ -57,7 +57,7 @@ const SILENT_CONTEXT_SCRIPT = `<script>
57
57
  if (!force && key === last) return;
58
58
  last = key;
59
59
  var sessionId = "";
60
- try { sessionId = sessionStorage.getItem("_aipanel_pk") || ""; } catch (e) {}
60
+ try { sessionId = sessionStorage.getItem("${import_core.SESSION_ID_KEY}") || ""; } catch (e) {}
61
61
  fetch(API, {
62
62
  method: "POST",
63
63
  headers: { "Content-Type": "application/json" },
@@ -113,7 +113,12 @@ function createAIPanelPlugin(options = {}) {
113
113
  let activeTabId = DEFAULT_TAB;
114
114
  const serviceInstanceId = import_crypto.default.randomUUID();
115
115
  const sseClients = /* @__PURE__ */ new Set();
116
- const mcpProxy = new import_mcp_proxy.McpProxy({ idleTimeout: 5 * 60 * 1e3 });
116
+ const mcpProxy = new import_mcp_proxy.McpProxy({
117
+ idleTimeout: 5 * 60 * 1e3,
118
+ // 用户 chrome MCP 透传:追加 args/env(核心受保护参数不可覆盖,由 McpProxy 过滤)
119
+ userArgs: config.chromeMcp?.args,
120
+ env: config.chromeMcp?.env
121
+ });
117
122
  let provider = null;
118
123
  const service = new import_service.AIPanelService(
119
124
  config,
@@ -329,7 +334,7 @@ function createAIPanelPlugin(options = {}) {
329
334
  ];
330
335
  const titleInject = `<script>
331
336
  (function () {
332
- var KEY = "_aipanel_pk";
337
+ var KEY = "${import_core.SESSION_ID_KEY}";
333
338
  if (!sessionStorage.getItem(KEY)) {
334
339
  sessionStorage.setItem(KEY, "[" + Math.random().toString(36).slice(2, 10) + "]");
335
340
  }
@@ -24,20 +24,21 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  var import_fs = __toESM(require("fs"));
25
25
  var import_http = __toESM(require("http"));
26
26
  var import_path = __toESM(require("path"));
27
+ var import_core = require("@aipanel/core");
27
28
  const filePath = process.argv[2];
28
29
  if (!filePath) {
29
30
  console.error("Usage: node format-bridge.cjs <filePath>");
30
31
  process.exit(1);
31
32
  }
32
33
  const absPath = import_path.default.resolve(filePath);
33
- const PORT = parseInt(process.env.AIPANEL_VSCODE_PORT || "", 10) || 51939;
34
+ const PORT = parseInt(process.env[import_core.ENV_VSCODE_PORT] || "", 10) || import_core.VSCODE_EXTENSION_PORT;
34
35
  function touchFile() {
35
36
  const now = /* @__PURE__ */ new Date();
36
37
  import_fs.default.utimesSync(absPath, now, now);
37
38
  }
38
39
  function probeHealth() {
39
40
  return new Promise((resolve) => {
40
- const req = import_http.default.get(`http://127.0.0.1:${PORT}/health`, (res) => {
41
+ const req = import_http.default.get(`http://${import_core.DEFAULT_HOSTNAME}:${PORT}${import_core.VSCODE_ROUTE_HEALTH}`, (res) => {
41
42
  res.resume();
42
43
  resolve(res.statusCode === 200);
43
44
  });
@@ -53,9 +54,9 @@ function formatViaExtension() {
53
54
  const body = JSON.stringify({ filePath: absPath });
54
55
  const req = import_http.default.request(
55
56
  {
56
- hostname: "127.0.0.1",
57
+ hostname: import_core.DEFAULT_HOSTNAME,
57
58
  port: PORT,
58
- path: "/format",
59
+ path: import_core.VSCODE_ROUTE_FORMAT,
59
60
  method: "POST",
60
61
  headers: { "Content-Type": "application/json" },
61
62
  timeout: 5e3
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.9",
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",
@@ -41,12 +41,12 @@
41
41
  "execa": "^9.6.1",
42
42
  "typescript-language-server": "^5.3.0",
43
43
  "unplugin-vue-inspector": "^3.0.0",
44
- "@aipanel/core": "1.2.8",
45
- "@aipanel/provider-opencode": "1.2.8"
44
+ "@aipanel/core": "1.2.9",
45
+ "@aipanel/provider-opencode": "1.2.9"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "vite": ">=5.0.0",
49
- "@aipanel/provider-deepseek": "1.2.8"
49
+ "@aipanel/provider-deepseek": "1.2.9"
50
50
  },
51
51
  "peerDependenciesMeta": {
52
52
  "@aipanel/provider-deepseek": {
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "sharp": "^0.34.5",
58
- "@aipanel/client": "1.2.8"
58
+ "@aipanel/client": "1.2.9"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "pagoda-cli build && vite build -c vite.client.config.ts",